diff options
author | Peter Bex <peter@more-magic.net> | 2017-11-09 21:26:48 +0100 |
---|---|---|
committer | Peter Bex <peter@more-magic.net> | 2017-11-09 21:26:48 +0100 |
commit | e7bf6cc5f6301e5d4be4d877b57039512379a1ec (patch) | |
tree | eeea0de6aef1bf824ee63c46272c1776222034d9 | |
parent | 6ffe2658fb703ddac9b7801b2f67ab0ea597472d (diff) | |
download | scsh-process-e7bf6cc5f6301e5d4be4d877b57039512379a1ec.tar.gz |
Temporarily mask sigchld to avoid race condition in fork
We do a fork, and immediately afterwards we insert a scsh-process
object into the pending processes table with the pid of the newly
created child process.
However, sigchld may arrive in between the fork and the insertion of
the scsh-process object into the pending process table. The sigchld
handler will reap the child process and attempt to update the pending
scsh-process object, but it can't find it yet because the insertion of
this object into the table happens after the fork, thus losing the
reaped child's exit status.
Then, if the program waits for the process after that, we enter an
infinite busy waiting loop that expects a sigchld to arrive and update
the object through the table, but that never happens because the child
has already been reaped.
-rw-r--r-- | scsh-process.scm | 49 |
1 files changed, 29 insertions, 20 deletions
diff --git a/scsh-process.scm b/scsh-process.scm index 4153c00..4e6708d 100644 --- a/scsh-process.scm +++ b/scsh-process.scm @@ -221,26 +221,35 @@ (exit 0))) (define (fork #!optional thunk continue-threads?) - (let* ((maybe-reinstall-deadlock-workaround! - (lambda () - (cond-expand - (has-thread-killer - (unless continue-threads? (install-deadlock-workaround!))) - (else (void))))) - (thunk (and thunk (lambda () - (clear-scsh-pending-processes!) - (maybe-reinstall-deadlock-workaround!) - (thunk)))) - (pid (cond-expand - (has-thread-killer - (process-fork thunk (not continue-threads?))) - (else ;; Ignore both args if thunk is #f, so #f won't be applied - (if thunk (process-fork thunk) (process-fork)))))) - (cond ((zero? pid) - (clear-scsh-pending-processes!) - (maybe-reinstall-deadlock-workaround!) - #f) - (else (add-scsh-pending-process! pid))))) + (dynamic-wind + ;; If we're really unlucky, sigchld might be delivered + ;; immediately after forking, but before we added the child's + ;; pid to the pending processes table. This means we'll lose + ;; the event and won't mark the process as finished, resulting + ;; in an endless loop in process-wait. So, mask it. + (lambda () (signal-mask! signal/chld)) + (lambda () + (let* ((maybe-reinstall-deadlock-workaround! + (lambda () + (cond-expand + (has-thread-killer + (unless continue-threads? (install-deadlock-workaround!))) + (else (void))))) + (thunk (and thunk (lambda () + (clear-scsh-pending-processes!) + (maybe-reinstall-deadlock-workaround!) + (thunk)))) + (pid (cond-expand + (has-thread-killer + (process-fork thunk (not continue-threads?))) + (else ;; Ignore both args if thunk is #f, so #f won't be applied + (if thunk (process-fork thunk) (process-fork)))))) + (cond ((zero? pid) + (clear-scsh-pending-processes!) + (maybe-reinstall-deadlock-workaround!) + #f) + (else (add-scsh-pending-process! pid))))) + (lambda () (signal-unmask! signal/chld)))) (define %fork fork) |