Re: Problem interrupting threads in PR_Accept() wiht PR_Interrupt()

Julien R Pierre - Sun Microsystems <[email protected]>
Newsgroups gmane.comp.mozilla.devel.nspr
Message-ID <[email protected]>
Joachim,

Joachim Ziegler wrote:
> Wan-Teh Chang wrote:
>> I/O functions that may block indefinitely, for example, PR_Recv and PR_Send,
>> can also be interrupted.  Your handle_call function must be calling
>> such functions.
> 
> Yes, they call PR_Send() and PR_TransmitFile().
> 
>> You can also just use a global variable:
>>
>> PRBool stopping = PR_FALSE;
>>
>> Change your worker thread's while loop to test it:
>>
>>     while(!stopping)
> 
> Yes, this works. Thank you!
> 
> The original task as given by my project leader was to write a portable
> demo server that does not loose any request (except for the ones caused
> by network timeouts or network problems, which cannot be avoided). That
> is, after a request has been PR_Accept()ed, it must run to full
> completion, even in the case that the time to serve the request is long
> and that during this time the administrator signals the server to go down.
> 
> So I formulated the while loop now as follows, including your hint and
> introducing Boolean variables worker_busy[]:
> 
>   while( !shutdown_workers ){
> 
>     if((fd = PR_Accept( sock, NULL, PR_INTERVAL_NO_TIMEOUT ))
>        == NULL) {
>       if(PR_GetError() == PR_PENDING_INTERRUPT_ERROR)
>         break; /* that's OK: master has signaled an interrupt */
>       else {
>         oops("PR_Accept in thread_start()");
>         continue;
>       }
>     }
> 
>     /* RACE HERE */
> 
>     worker_busy[nthread] = 1;
>     handle_call(fd, nthread);
>     PR_Close( fd );
>     worker_busy[nthread] = 0;
>   }
> 
> 
> The master thread first checks that all workers are not busy before
> sending PR_Interrupt(). But I realize that there is a tiny race
> condition in this approach, namely that the interrupt signal occurs
> exactly while the thread is in the line indicated by "RACE HERE". Then
> the request is accepted, but the thread will be interrupted later in
> PR_Send() or PR_TransmitFile(). I cannot combine PR_Accept() and setting
> worker_busy to TRUE into an atomar operation.
> 
> Is there any solution to this problem? Can I make PR_Send() and
> PR_TransmitFile() ignore the PR_Interrupt()?
> 
> Greetings,
> Joachim

I think the solution to this race is to let the worker threads 
acknowledge that they are stopping.

You can use a condition variable to do that. This is better than joining 
the thread, because PR_JoinThread doesn't take a timeout, but 
PR_WaitCondvar does.

Whenever your worker thread detects the PR_PENDING_INTERRUPT_ERROR or 
notices the "stopping" global variable, it can post that condition 
variable with PR_NotifyCondvar just before exiting.

The main thread code would use PR_WaitCondvar with PR_INTERVAL_NO_WAIT 
to ensure that the thread is truly exiting, and if not, keep trying to 
interrupt again, until the condition is notified. You might insert a 
short PR_Sleep in there to avoid spinning the CPU.

Only after the condition is posted would you call PR_JoinThread.

One advantage of this approach is that you can try to stop all your 
threads "at the same time" rather than sequentially, and join them in 
the order they are stopping, when the condition variable is posted, 
rather than in the order of your PRThread* array.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.