about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2018-08-28 12:45:44 -0400
committerRich Felker <dalias@aerifal.cx>2018-08-28 12:45:44 -0400
commit060ed9367337cbbd59a9e5e638a1c2f460192f25 (patch)
treecdb04df5887a422cdc4a239d68664c393bd23b47
parent92c52644e04ac754b1b282a9d33d033e04e1399e (diff)
downloadmusl-060ed9367337cbbd59a9e5e638a1c2f460192f25.tar.gz
musl-060ed9367337cbbd59a9e5e638a1c2f460192f25.tar.xz
musl-060ed9367337cbbd59a9e5e638a1c2f460192f25.zip
fix deadlock in async thread self-cancellation
with async cancellation enabled, pthread_cancel(pthread_self())
deadlocked due to pthread_kill holding killlock which is needed by
pthread_exit.

this could be solved by making pthread_kill block signals around the
critical section, at least when the target thread is itself, but the
issue only arises for cancellation, and otherwise would just be
imposing unnecessary cost.

instead just have pthread_cancel explicitly check for async
self-cancellation and call pthread_exit(PTHREAD_CANCELED) directly
rather than going through the signal machinery.
-rw-r--r--src/thread/pthread_cancel.c6
1 files changed, 5 insertions, 1 deletions
diff --git a/src/thread/pthread_cancel.c b/src/thread/pthread_cancel.c
index 3d229223..bf07dbeb 100644
--- a/src/thread/pthread_cancel.c
+++ b/src/thread/pthread_cancel.c
@@ -92,6 +92,10 @@ int pthread_cancel(pthread_t t)
 		init = 1;
 	}
 	a_store(&t->cancel, 1);
-	if (t == pthread_self() && !t->cancelasync) return 0;
+	if (t == pthread_self()) {
+		if (t->canceldisable == PTHREAD_CANCEL_ENABLE && t->cancelasync)
+			pthread_exit(PTHREAD_CANCELED);
+		return 0;
+	}
 	return pthread_kill(t, SIGCANCEL);
 }