[FASTCGI] libfcgi in multi threaded app

Dave Bender codehero at gmail.com
Mon May 3 23:02:14 EDT 2010


On Mon, May 3, 2010 at 7:21 PM, Rob <rclemley at booksys.com> wrote:

>  Dave, Unless you actually answer the question of who's using these 3
> variables, this will be my last post on this thread.
>
> I know that my apps handle multi-threaded simultaneous concurrent
> connections to web clients via [libfcgi - mod_fastcgi - apache2 - web
> browsers], and both the docs and the source code support this.
>
>
OK I get it. I overlooked your reference to apache fast_modcgi and was only
looking at libfcgi. Looking at lighttpd's mod_fastcgi, I see it along with
apache also ignores management records. I guess the spec's definition of
multiplexing is out of sync with current practice. Sorry for the
misunderstanding.


>
>  Looking through the source, the first thing that FCGX_Accept_r does is
> call FCGX_Finish_r.
>
>
> Which doesn't mean anything unless you re-use your FCGX_Request structure.
> The FCGX_Finish_r called inside Accept_r is a no-op in my case.
>

It's not exactly a no-op. It seems kind of silly for FCGX_Finish_r to free
all the request data (stream buffers), when immediately following it a new
request will be reallocated!


>
>   Also, in Appendix B, example #4, we see:
>>
>> 4. Two instances of example 1, multiplexed onto a single connection. The
>> first request is more difficult than the second, so the application finishes
>> the requests out of order: [...]
>>
>>
>> I think the variables that we're discussing probably don't mean what you
>> think they mean.
>>
>
>  Please read the spec and the source code more carefully.
>
>
> ???  Appendix B.4 clearly shows how the protocol maintains concurrent
> requests.
>
> That's a crude statement bordering on insult.  Instructing me to "read more
> carefully?"   What does that say about you?
>
>
It wasn't meant to be insulting, sorry if it came out like that. I think we
were talking past each other. However, I think you may need to read B4
again. B4 illustrates the spec's intended method of multiplexing. It
explicity says these requests are MULTIPLEXED over the SAME transport
connection (in practice would be one socket fd). In this case the protocol
defines a mechanism the library does not support. Everything you have talked
about so far to handle multiple connections relies on the OS giving you
separate fds for each request. B4 is out of sync with reality; libfcgi would
return an FGCI_END_REQUEST as soon as request 2 showed its face.

Sorry to be a stickler on these points, but from a syscall standpoint the
distinction is big. According the original spec, multiple requests could be
sent over a transport connection; libfcgi's way is to have a transport
connection's lifetime bound to the lifetime of one request. While simpler
than the spec's multiplexing method, the cost is extra accept(), close(),
malloc(), and free() calls.



>
>
>
>> Rob
>>
>>
>>
>> On 05/03/2010 01:32 PM, Dave Bender wrote:
>>
>> Section 4.1 ( http://www.fastcgi.com/devkit/doc/fcgi-spec.html#SA )
>> states:
>>
>> FCGI_MPXS_CONNS: "0" if this application does not multiplex connections
>> (i.e. handle concurrent requests over each connection), "1" otherwise.
>>
>> Seeing as how the library is limited to 1 connection and 1 request,
>>
>> On Mon, May 3, 2010 at 2:27 PM, Rob <rclemley at booksys.com> wrote:
>>
>>>  I cannot really address your question because you're touching deeper
>>> into the internals of libfcgi and attempting to reconcile it with "the
>>> spec", without explicit references to which part of the spec.  I just don't
>>> have time to do that much research right now.
>>>
>>>  Section 4.1 ( http://www.fastcgi.com/devkit/doc/fcgi-spec.html#SA )
>> states:
>>
>> FCGI_MPXS_CONNS: "0" if this application does not multiplex connections
>> (i.e. handle concurrent requests over each connection), "1" otherwise.
>>
>> This would imply connections are handled one at a time.
>>
>>
>>>  I would guess that you're missing some understanding of the complete
>>> flow of processing web requests, and how the requests are handled in the
>>> various nooks and crannies of FastCGI.
>>>
>>>
>>  Rob
>>>
>>>
>>>
>>> On 05/03/2010 12:22 PM, Dave Bender wrote:
>>>
>>> I'm also curious, how does the library even accept multiple requests
>>> simultaneously when the librar returns the following values for its
>>> management records:
>>>
>>>             if(strcmp(name, FCGI_MAX_CONNS) == 0) {
>>>                 value = '1';
>>>             } else if(strcmp(name, FCGI_MAX_REQS) == 0) {
>>>                 value = '1';
>>>             } else if(strcmp(name, FCGI_MPXS_CONNS) == 0) {
>>>                 value = '0';
>>>             } else {
>>>                 name = NULL;
>>>             }
>>> (taken from fcgiapp.c, version 2.4.0)
>>>
>>> According to the spec, the web server should only be sending 1 at a time.
>>>
>>> -Dave
>>>
>>>  On Mon, May 3, 2010 at 1:14 PM, Martin Chapman <chapmanm at pixia.com>wrote:
>>>
>>>> Rob,
>>>>
>>>> So how does this solution account for the fact that FCGX_Accept_r ()
>>>> resets
>>>> the stdio global structure each time it's called?  If you are
>>>> synchronizing
>>>> the code between each request, regardless of it's processed on a
>>>> separate
>>>> worker thread then only one request at a time is really being executed.
>>>>
>>>> Martin
>>>>
>>>>
>>>> -----Original Message-----
>>>> From: fastcgi-developers-bounces+chapmanm=pixia.com at mailman.fastcgi.com
>>>> [mailto:fastcgi-developers-bounces+chapmanm<fastcgi-developers-bounces%2Bchapmanm>
>>>> =pixia.com at mailman.fastcgi.com]
>>>> On Behalf Of Rob
>>>> Sent: Monday, May 03, 2010 11:06 AM
>>>> To: fastcgi-developers at mailman.pins.net
>>>> Subject: Re: [FASTCGI] libfcgi in multi threaded app
>>>>
>>>>  On 05/03/2010 10:27 AM, AlannY wrote:
>>>> > I've created a socket via FCGX_OpenSocket. Everything all right.
>>>> >
>>>> > Then, I'm creating threads (5, for example). In each thread, I'm
>>>> > creating a request with FCGX_InitRequest (with socket, created early
>>>> and
>>>> > FCGI_FAIL_ACCEPT_ON_INTR).
>>>> >
>>>> > Then the main loop starts. In this loop I'm calling FCGX_Accept_r on
>>>> > this request. Here is trouble. Some of threads can successfully
>>>> accept,
>>>> > but some of then can't. Return code from FCGX_Accept is -9.
>>>>
>>>> The main thread loop should repeatedly call FCGX_Accept_r().   Requests
>>>> successfully accepted by FCGX_Accept_r() must then be passed off to your
>>>> worker threads for processing.  Therefore, you might choose to
>>>> dynamically allocate an fcgi request structure before calling
>>>> FCGX_Accept_r() and then pass the dynamically allocated request
>>>> structure to an available worker thread.  The worker thread would then
>>>> process the request and then deallocate the request.
>>>>
>>>> The multi-threaded problem is: "how to pass the requests to the worker
>>>> threads?"  Your program  will be simple and graceful if you choose the
>>>> means of a synchronized queue data structure.  In a synchronized queue
>>>> object, all of the thread synchronization is performed upon access to
>>>> get or put items on the queue.   The mutex guards access to the internal
>>>> data.  The conditions NotFull and NotEmpty signal the threads to wake up
>>>> appropriately via the condition primitives: notify_one or notify_all.
>>>> We use C++ and boost::threads to do something like this:
>>>>
>>>> pseudo code for main thread:
>>>>
>>>>     synchronized_queue_class synchronized_queue  #this is the only
>>>> object that must be visible from all threads
>>>>
>>>>     function get_fcgi_request( request )
>>>>         request = allocate(sizeof request)
>>>>         FCGX_init_request(request ...)
>>>>         if FCGX_Accept_r(request) is successful
>>>>         then return TRUE
>>>>         else return FALSE
>>>>         endif
>>>>     endfunction
>>>>
>>>>     while get_fcgi_request( request )
>>>>         synchronized_queue.Put(request)
>>>>     endwhile
>>>>
>>>> pseudo code for worker threads:
>>>>
>>>>     while synchronized_queue.Get(request)
>>>>         process(request)
>>>>         deallocate(request)
>>>>     endwhile
>>>>
>>>> pseudo code for synchronized_queue_class:
>>>>
>>>>     class synchronized_queue_class:
>>>>         queue internal_queue
>>>>         condition notFull_condition
>>>>         condition notEmpty_condition
>>>>         mutex    monitor
>>>>
>>>> procedure Put(item):
>>>>             scoped_lock(monitor)
>>>>             while internal_queue.full()
>>>> notFull_condition.wait(monitor)
>>>>             internal_queue.put(item)
>>>>             notEmpty_condition.notify_one()
>>>>         endprocedure
>>>>
>>>> procedure Get(item):
>>>>             scoped_lock(monitor)
>>>>             while internal_queue.empty:
>>>> notEmpty_condition.wait(monitor)
>>>>             internal_queue.push(item)
>>>>             notFull_condtion.notify_one()
>>>>         endprocedure
>>>>
>>>>     endclass
>>>>
>>>> (There are other details in the synchronized queue which should be
>>>> addressed, such as how to shut down the  queue at the end of the program
>>>> and tell the worker threads to exit.)
>>>>
>>>>
>>>>
>>>>
>>>>
>>>> _______________________________________________
>>>> FastCGI-developers mailing list
>>>> FastCGI-developers at mailman.fastcgi.com
>>>> http://mailman.pins.net/mailman/listinfo.cgi/fastcgi-developers
>>>>
>>>> _______________________________________________
>>>> FastCGI-developers mailing list
>>>> FastCGI-developers at mailman.fastcgi.com
>>>> http://mailman.pins.net/mailman/listinfo.cgi/fastcgi-developers
>>>>
>>>
>>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.pins.net/mailman/private.cgi/fastcgi-developers/attachments/20100503/587b7277/attachment-0001.html>


More information about the FastCGI-developers mailing list