[libdispatch-dev] libdispatch threads, blocked SIGINT and child process

Daniel A. Steffen dsteffen at apple.com
Fri Dec 3 00:41:07 PST 2010


Thomas,

On Dec 3, 2010, at 12:00 AM, Thomas Clement wrote:

> HI have encountered a strange behavior when using libdispatch (OS X 10.6.5). I'm not sure if there is a bug in libdispatch or else what the correct fix is for me.
> I found myself in the situation of doing a fork/exec/waitpid from a libdispatch thread, the child process waiting for user input, then doing ctrl-c in my Terminal window. I would expect the child process to also be terminated, however the child process is still running, consuming lots of cpu (and its ppid is now 1 which is weird).

The reason for the cpu-consuming behavior is that the child process main thread inherits the signal mask of the parent process thread that calls fork().
As that thread is a libdispatch thread it has SIGINT masked by default, so you end up with a single-threaded child process with SIGINT masked. This means that the kernel cannot find an unmasked thread in that process to deliver the signal on when the Terminal shell sends it. As the kernel continuously retries to deliver the signal, this results in the increased cpu consumption you observed.

> I have noticed that libdispatch calls pthread_sigmask() when creating new threads blocking many signals.

In fact it does not, the code in question is only used on platforms without workqueue support.
However on Mac OS X the kernel does indeed mask a similar set of signals on the workqueue threads it creates on behalf of libdispatch.

> I have found that calling pthread_sigmask() in my dispatch_asynced code to unblock the SIGINT signal fixes the issue (the child process is also terminated when doing a ctrl-c in my Terminal window).

This is the correct action to take, you must not make any assumptions about the signal mask of libdispatch worker threads, so if you need a specific signal mask to be in place like in this case, your workitem must set it up with pthread_sigmask(), and restore it to the original value before returning.

This is documented in dispatch_queue_create(3): 

     Applications MAY call the following interfaces from a block submitted to a dispatch queue if and only
     if they restore the thread to its original state before returning:

           o   pthread_setcancelstate()
           o   pthread_setcanceltype()
           o   pthread_setschedparam()
           o   pthread_sigmask()
           o   pthread_setugid_np()
           o   pthread_chdir()
           o   pthread_fchdir()

> You can reproduce the issue using the attached sample code (which uses launchctl as the child process but it could be anything else). You can run the process from the Terminal window (./sigdispatch_br) then doing ctrl-c and watching the child process from a "top".

Daniel


More information about the libdispatch-dev mailing list