Skip to content

Commit 9bf0082

Browse files
committed
Don't execute the proxy queue while adding to it from same thread.
test_pthread_cancel was intermittently failing with a deadlock. From the backtrace, I found we were adding to the queue which can sometimes call malloc->emscripten_yield->emscripten_proxy_execute_queue which also tries to lock the same queue on the same thread.
1 parent 3d5f9af commit 9bf0082

File tree

1 file changed

+16
-0
lines changed

1 file changed

+16
-0
lines changed

system/lib/pthread/proxying.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
struct em_proxying_queue {
2222
// Protects all accesses to em_task_queues, size, and capacity.
2323
pthread_mutex_t mutex;
24+
// If the mutex is locked this is the thread that is using it.
25+
pthread_t active_thread;
2426
// `size` task queue pointers stored in an array of size `capacity`.
2527
em_task_queue** task_queues;
2628
int size;
@@ -30,6 +32,7 @@ struct em_proxying_queue {
3032
// The system proxying queue.
3133
static em_proxying_queue system_proxying_queue = {
3234
.mutex = PTHREAD_MUTEX_INITIALIZER,
35+
.active_thread = NULL,
3336
.task_queues = NULL,
3437
.size = 0,
3538
.capacity = 0,
@@ -47,6 +50,7 @@ em_proxying_queue* em_proxying_queue_create(void) {
4750
}
4851
*q = (em_proxying_queue){
4952
.mutex = PTHREAD_MUTEX_INITIALIZER,
53+
.active_thread = NULL,
5054
.task_queues = NULL,
5155
.size = 0,
5256
.capacity = 0,
@@ -123,6 +127,16 @@ void emscripten_proxy_execute_queue(em_proxying_queue* q) {
123127
executing_system_queue = 1;
124128
}
125129

130+
// When the current thread is adding tasks it locks the queue, but we can
131+
// potentially try to execute the queue during the add (from
132+
// emscripten_yield). This will deadlock the thread, so only try to take the
133+
// lock if the current thread is not adding to the queue. We then hope the
134+
// queue is executed later when it is unlocked.
135+
// XXX: This could leave to starvation if we never process the queue.
136+
if (q->active_thread == pthread_self()) {
137+
return;
138+
}
139+
126140
pthread_mutex_lock(&q->mutex);
127141
em_task_queue* tasks = get_tasks_for_thread(q, pthread_self());
128142
pthread_mutex_unlock(&q->mutex);
@@ -140,7 +154,9 @@ void emscripten_proxy_execute_queue(em_proxying_queue* q) {
140154
static int do_proxy(em_proxying_queue* q, pthread_t target_thread, task t) {
141155
assert(q != NULL);
142156
pthread_mutex_lock(&q->mutex);
157+
q->active_thread = pthread_self();
143158
em_task_queue* tasks = get_or_add_tasks_for_thread(q, target_thread);
159+
q->active_thread = NULL;
144160
pthread_mutex_unlock(&q->mutex);
145161
if (tasks == NULL) {
146162
return 0;

0 commit comments

Comments
 (0)