Boost logo

Boost Users :

From: Zeljko Vrba (zvrba_at_[hidden])
Date: 2007-10-04 02:01:15


On Wed, Oct 03, 2007 at 07:47:57PM -0400, Scott Gifford wrote:
>
> As with most Unix servers, the server is shut down with an OS signal
> telling the server to shut down. The server catches this signal, then
> simply calls exit(1). Any important cleanup happens in the
> destructors of global variables, so it will happen properly no matter
> how the server exits.
>
This is not "properly"; I guessed your problem upfront before I had
finished reading this very paragraph :)

First, you need to block SIGINT in all threads except the main thread.
If you don't have a main thread, make it just for the purpose of signal
handling. Otherwise, SIGINT will be delivered to a random thread.
Maybe that's OK in your case.

Second, you don't need to lock the mutex before signaling the condition
variable (this is what causes your deadlock).

Third, how do you clean up threads which are NOT waiting at the condition
variable at the moment of signal arrival?

Fourth, your cleanup is NOT "proper" in any way because your cleanup
executes in the context of signal handler. In that context (i.e.
before the signal handler returns - which is never in your case), only
async-signal safe functions may be used. Neither mutex locking nor
condition signal is async signal safe.

So how do you do it "properly." Hm: have another, 'main' thread just for
the purposes of signal handling; have SIGINT unblocked in this thread and
block it in all other threads. That thread does something like:

while(!flag)
  sigsuspend(...);

and SIGINT just sets the flag to true. When the while() loop exits,
you're out of the signal context and then you may use functions such
as pthread_cancel() to cancel all threads, or pthread_kill() to
explicitly deliver signal to all threads and yes, also
pthread_cond_signal() and pthread_cond_broadcast(). Then use
pthread_join() to wait that all threads finish and _then_ call exit
from the main thread. (If you use pthread_kill, you have the same
caveat with async signal safe functions).

I'm not sure that even the above scheme is 100% fool-proof, but it
seems less broken than your current solution.

Also, I don't see how you can use them to notify threads that also do
some work outside of their monitor, i.e. if they are structured like:

while(1) {
 // get mutex
 // wait on condition
 // do some work
 // release mutex
 // do some more work (*)
}

If you signal the variable while the thread is executing in (*), it
will never pick up the signal.

How do you do it in Boost.Thread - I don't know. The whole idea of using
destructors to clean up global data in a multi-threaded program sounds
like calling for trouble. While the destructors themselves may use locks,
in how many threads is the destructor list walked and cleaned (the code
generated by the compiler to walk over the list of global objects and call
destructor for each) executing? Does your runtime library and/or compiler
guarantee that every global destructor is executed exactly once even in a
MT setting? (This sounds kinda the inverse of the threadsafe singleton
pattern.)

>
> Does anybody have any suggestions for a straightforward way to handle
> this properly?
>
Um, signals and threads don't mix well. No "straightforward" solution.
Read about and understand async-signal safety.


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net