libdispatch threads, blocked SIGINT and child process
Hello, I 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). I have noticed that libdispatch calls pthread_sigmask() when creating new threads blocking many signals. 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). 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". So, what do you guys think? Thanks, Thomas
Oops, I posted this to the wrong mailing-list, sorry. On 2 déc. 2010, at 15:56, Thomas Clement wrote:
Hello,
I 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).
I have noticed that libdispatch calls pthread_sigmask() when creating new threads blocking many signals. 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).
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".
So, what do you guys think?
Thanks, Thomas
<sigdispatch_br.zip>_______________________________________________ launchd-dev mailing list launchd-dev@lists.macosforge.org http://lists.macosforge.org/mailman/listinfo.cgi/launchd-dev
... however the child process is still running, consuming lots of cpu (and its ppid is now 1 which is weird).
That's standard Unix behavior when a process' parent dies – it's reparented by the init process. On OS X, that's the system-wide launched instance.
I have noticed that libdispatch calls pthread_sigmask() when creating new threads blocking many signals. 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).
I would expect GCD to do some thread-preparation before it starts dequeueing work from queues; I'm not surprised that setting up the thread's signal mask is part of that preparation. Considering the main goal of GCD is to abstract away the whole notion of threads, I would consider any property of a GCD thread to be immutable (such as those manipulated with the pthread_* APIs.)
So, what do you guys think?
To accomplish your goal, I imagine I would simply use the posix_spawn APIs to safely spawn the process from the GCD queue without the need to muck with the GCD thread's state; see posix_spawnattr_setsigmask(). Alternatively, you could spawn a thread to be designated the "fork thread", which could be configured with the appropriate signal mask and whatever other attributes. Using a simple queueing mechanism and the appropriate synchronization, your GCD blocks can communicate with the fork thread to instruct it to spawn the necessary process, which inherits the configured signal mask and whatever other state.
On 3 déc. 2010, at 10:58, Dave Keck wrote:
... however the child process is still running, consuming lots of cpu (and its ppid is now 1 which is weird).
That's standard Unix behavior when a process' parent dies – it's reparented by the init process. On OS X, that's the system-wide launched instance.
Ok. I expected its new parent to be the per-user launchd instance instead.
I have noticed that libdispatch calls pthread_sigmask() when creating new threads blocking many signals. 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).
I would expect GCD to do some thread-preparation before it starts dequeueing work from queues; I'm not surprised that setting up the thread's signal mask is part of that preparation.
Considering the main goal of GCD is to abstract away the whole notion of threads, I would consider any property of a GCD thread to be immutable (such as those manipulated with the pthread_* APIs.)
It turns out the man page of dispatch_queue_create() specifically lists the pthread_* calls which are compatible with GCD. pthread_sigmask() is one of them (the thread must be restored to its original state before returning though).
So, what do you guys think?
To accomplish your goal, I imagine I would simply use the posix_spawn APIs to safely spawn the process from the GCD queue without the need to muck with the GCD thread's state; see posix_spawnattr_setsigmask().
Right, I didn't know about that API :) It seems the other solution is to call pthread_sigmask() between my fork() and exec() to unblock the SIGINT signal.
Alternatively, you could spawn a thread to be designated the "fork thread", which could be configured with the appropriate signal mask and whatever other attributes. Using a simple queueing mechanism and the appropriate synchronization, your GCD blocks can communicate with the fork thread to instruct it to spawn the necessary process, which inherits the configured signal mask and whatever other state.
Right but it kind of defeats the point of using GCD ;) Thanks! Thomas
It turns out the man page of dispatch_queue_create() specifically lists the pthread_* calls which are compatible with GCD. pthread_sigmask() is one of them (the thread must be restored to its original state before returning though).
I thought I remembered seeing some docs on that topic, but couldn't find them earlier. Thanks.
It seems the other solution is to call pthread_sigmask() between my fork() and exec() to unblock the SIGINT signal.
Yep. I believe it's been mentioned on darwin-dev that posix_spawn is now the recommended spawning API. (Though it has its shortcomings, some of which can't be worked around and require fork/exec, such as closing all file descriptors in the child. rdar://7724566 and rdar://8390960)
participants (2)
-
Dave Keck
-
Thomas Clement