Search code examples
c++cfastcgi

Confused about the FCGI concept


I'm currently trying to implement a small FastCGI multi-threaded application that runs on linux. I use the library from fastcgi.com. Now I'm not sure if I really understand the concept how the FCGI communication between the webserver and my application works.

First I create a new socket in my application. I return the file descriptor.

int socketIn = 0;
struct sockaddr_un local;
int length = 0;
int value = 1;

memset(&local, 0, sizeof(local));

local.sun_family = AF_UNIX;

strcpy(local.sun_path, socketPath);

length = strlen(local.sun_path) + sizeof(local.sun_family);

/* delete old unix socket */
if(-1 == unlink(socketPath))
{
    switch (errno)
    {
        case ENOENT:
        {
        }break;

        default:
        {
            printf("\n[Error]\tCould not remove old socket.");
            return -1;
        }
    }
}

/* create new socket */
if (-1 == (socketIn = socket(AF_UNIX, SOCK_STREAM, 0))) 
{
    printf("\n[Error]\tCould not create socket.");
    return -2;
}

/* bind socket */
if (-1 == bind(socketIn, (struct sockaddr *)&local, length)) 
{
    printf("\n[Error]\tCould not bind socket.");
    return -4;
}

return socketIn;

After that I init the FCGI library: FCGX_Init();

Now I start to create my threads:

#define THREAD_COUNT 2

static int counts[THREAD_COUNT];

struct thread_data{
   int  thread_id;
   int  fcgiSocket;
};

struct thread_data thread_data_array[THREAD_COUNT];

int main(int argc, char** argv) 
{
   int i;
   pthread_t id[THREAD_COUNT];
   for (i = 1; i < THREAD_COUNT; i++)
   {
      thread_data_array[i].thread_id = i;
      thread_data_array[i].fcgiSocket = fcgi.fcgiSocket;
      pthread_create(&id[i], NULL, doit, &thread_data_array[i]);
   }

   thread_data_array[0].thread_id = 0;
   thread_data_array[0].fcgiSocket = fcgi.fcgiSocket;

   doit((void *)&thread_data_array[0]);

   return 0;
}

Finally my thread code:

static void *doit(void *a)
{
    struct thread_data *my_data;
    my_data = (struct thread_data *) a;
    int rc, i;
    pid_t pid = getpid();
    FCGX_Request request;
    char *server_name;

    FCGX_InitRequest(&request, my_data->fcgiSocket, 0);

    for (;;)
    {
        static pthread_mutex_t accept_mutex = PTHREAD_MUTEX_INITIALIZER;
        static pthread_mutex_t counts_mutex = PTHREAD_MUTEX_INITIALIZER;

        /* Some platforms require accept() serialization, some don't.. */
        pthread_mutex_lock(&accept_mutex);
        rc = FCGX_Accept_r(&request);
        pthread_mutex_unlock(&accept_mutex);

        if (rc < 0)
            break;

        server_name = FCGX_GetParam("SERVER_NAME", request.envp);

        FCGX_FPrintF(request.out,
            "Content-type: text/html\r\n"
            "\r\n"
            "<title>FastCGI Hello! (multi-threaded C, fcgiapp library)</title>"
            "<h1>FastCGI Hello! (multi-threaded C, fcgiapp library)</h1>"
            "Thread %d, Process %ld<p>"
            "Request counts for %d threads running on host <i>%s</i><p><code>",
            my_data->thread_id, pid, THREAD_COUNT, server_name ? server_name : "?");

        pthread_mutex_lock(&counts_mutex);
        ++counts[my_data->thread_id];
        for (i = 0; i < THREAD_COUNT; i++)
            FCGX_FPrintF(request.out, "%5d " , counts[i]);
        pthread_mutex_unlock(&counts_mutex);

        FCGX_Finish_r(&request);
    }

    return NULL;
}

Now I'm worried about the socket I use. The 3 threads will all write to the same socket. Is there an other or better way to solve this problem with a multi-threaded FCGI application?


Solution

  • I am replying to an old post, but maybe someone else can benefit from this.

    "Now I'm worried about the socket I use. The 3 threads will all write to the same socket."

    Nothing to worry about!

    According to Fast CGI specification, Each request have associated ID, and when the thread writes the response back to socket, it associates the ID of the request its serving. So even if the threads write in different order, the web server knows which response is for which request. The specification is here -

    http://www.fastcgi.com/drupal/node/6?q=node/22

    And the writes to a socket are guaranteed to be atomic as long as the size of message is less than PIPE_BUF. And libfcgi limits the write size to PIPE_BUF. You can check this in the following file -

    http://www.fastcgi.com/mod_fastcgi/mod_fastcgi.h

    #define FCGI_MAX_MSG_LEN  PIPE_BUF
    

    Hope this explanation clears the doubts!