aboutsummaryrefslogtreecommitdiff
path: root/psx/psx.c
diff options
context:
space:
mode:
authorAndrew G. Morgan <morgan@kernel.org>2020-11-11 19:53:06 -0800
committerAndrew G. Morgan <morgan@kernel.org>2020-11-11 20:31:23 -0800
commit7cfe15ee579ea83a7780c6190576fdcab3e2faac (patch)
treee11fb3068c6d06b8f3c28546db1626b02f464ad0 /psx/psx.c
parent9b1c003748d4df78416d50fce139f0875224440b (diff)
downloadlibcap-7cfe15ee579ea83a7780c6190576fdcab3e2faac.tar.gz
Allow a dying thread to participate in the psx mechanism.
If a dying thread blocks all interrupts in order to die in peace, it can deadlock the psx mechanism for all the surviving threads. To account for this corner case, while waiting to enter the _PSX_EXITING state, temporarily unblock the psx_tracker.psx_sig. https://github.com/golang/go/issues/42494 Add a psx_test.go:TestThreadChurn() test case for this issue. Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
Diffstat (limited to 'psx/psx.c')
-rw-r--r--psx/psx.c34
1 files changed, 34 insertions, 0 deletions
diff --git a/psx/psx.c b/psx/psx.c
index 72c2098..233267d 100644
--- a/psx/psx.c
+++ b/psx/psx.c
@@ -336,11 +336,45 @@ typedef struct {
* https://sourceware.org/bugzilla/show_bug.cgi?id=12889
*/
static void _psx_exiting(void *node) {
+ /*
+ * Until we are in the _PSX_EXITING state, we must not block the
+ * psx_sig interrupt for this dying thread. That is, until this
+ * exiting thread can set ref->gone to 1, this dying thread is
+ * still participating in the psx syscall distribution.
+ *
+ * See https://github.com/golang/go/issues/42494 for a situation
+ * where this code is called with psx_tracker.psx_sig blocked.
+ */
+ sigset_t sigbit, orig_sigbits;
+ sigemptyset(&sigbit);
+ pthread_sigmask(SIG_UNBLOCK, &sigbit, &orig_sigbits);
+ sigaddset(&sigbit, psx_tracker.psx_sig);
+ pthread_sigmask(SIG_UNBLOCK, &sigbit, NULL);
+
+ /*
+ * With psx_tracker.psx_sig unblocked we can wait until this
+ * thread can enter the _PSX_EXITING state.
+ */
psx_new_state(_PSX_IDLE, _PSX_EXITING);
+
+ /*
+ * We now indicate that this thread is no longer participating in
+ * the psx mechanism.
+ */
registered_thread_t *ref = node;
pthread_mutex_lock(&ref->mu);
ref->gone = 1;
pthread_mutex_unlock(&ref->mu);
+
+ /*
+ * At this point, we can restore the calling sigmask to whatever
+ * the caller thought was appropriate for a dying thread to have.
+ */
+ pthread_sigmask(SIG_SETMASK, &orig_sigbits, NULL);
+
+ /*
+ * Allow the rest of the psx system carry on as per normal.
+ */
psx_new_state(_PSX_EXITING, _PSX_IDLE);
}