Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@dermesser
Created July 3, 2014 11:14
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save dermesser/e2f9b66457ae19ebd116 to your computer and use it in GitHub Desktop.
Save dermesser/e2f9b66457ae19ebd116 to your computer and use it in GitHub Desktop.
In case anyone wants to see a multi-threaded FastCGI application written with the fcgiapp library.
# include <stdlib.h>
# include <stdio.h>
# include <sys/stat.h>
# include <pthread.h>
# include <fcgiapp.h>
const char* const sockpath = "/tmp/fcgicpp.sock";
void* start_fcgi_worker(void* arg);
struct FCGI_Info
{
int fcgi_fd;
};
int main(void)
{
int fcgifd = FCGX_OpenSocket(sockpath,128);
chmod(sockpath,0777);
if ( 0 > fcgifd )
{
printf("Error opening socket\n");
exit(1);
}
/*
if ( FCGX_IsCGI() )
{
printf("Please run this process as FastCGI process.\n");
exit(1);
}
*/
const unsigned int n_threads = 4;
pthread_t threads[n_threads];
struct FCGI_Info info;
info.fcgi_fd = fcgifd;
for ( unsigned int i = 0; i < n_threads; i++ )
{
pthread_create(&threads[i],NULL,start_fcgi_worker,(void*)&info);
}
// Wait indefinitely
for ( unsigned int i = 0; i < n_threads; i++ )
{
pthread_join(threads[i],NULL);
}
return 0;
}
void* start_fcgi_worker(void* arg)
{
struct FCGI_Info* info = (struct FCGI_Info*)arg;
FCGX_Init();
FCGX_Request request;
FCGX_InitRequest(&request,info->fcgi_fd,0);
while ( 1 )
{
FCGX_Accept_r(&request);
FCGX_PutStr("Content-type: text/plain\r\n\r\n",28,request.out);
FCGX_PutStr("Hey!\n",5,request.out);
FCGX_Finish_r(&request);
}
}
@dermesser
Copy link
Author

This server can handle up to 20k requests/second on an nginx installation that itself is capable of serving ~50k static file requests per second.

@ttkdroid
Copy link

Well done mate (2 years after)

Did you try fastcgi using tcp ports instead of Unix sockets?

What hardware have you used and how did you run these tests? I want to do some tests on my FCGI app...

Cheers

@robertdfrench
Copy link

This is beautiful.

@xdevelnet
Copy link

You may optimize (or improve/analyze) this code by doing the following:

  1. Move away FCGX_Init() from worker to main thread
  2. Remove whole struct FCGI_Info, because it contains only fd, which is used globally and it value is the same for all threads. Declare fd in global scope
  3. There is a memory leak in fastcgi library itself. In order to fix it you may need to #include <fcgios.h> and execute OS_LibShutdown(); at end of main thread
  4. Currently in this example there is no way to gracefully shut down threads because they are not receiving signals. The proper way to do that is to make syscalls with EINTR enabled and send to each thread signal(s)
  5. For some reason in this "official" example https://github.com/FastCGI-Archives/fcgi2/blob/master/examples/threaded.c FCGX_Accept_r(&request) is evaluated with mutex-es protection. Have no idea if you or me should do the same.

@bwillcox
Copy link

bwillcox commented May 5, 2022

Locking around calls to FCGX_Accept_r seem unnecessary. The header file says "(multi-thread safe)".

@dermesser
Copy link
Author

Thank you all for your comments! They are really helpful :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment