2121struct 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 ;
2624 // `size` task queue pointers stored in an array of size `capacity`.
2725 em_task_queue * * task_queues ;
2826 int size ;
@@ -32,12 +30,13 @@ struct em_proxying_queue {
3230// The system proxying queue.
3331static em_proxying_queue system_proxying_queue = {
3432 .mutex = PTHREAD_MUTEX_INITIALIZER ,
35- .active_thread = NULL ,
3633 .task_queues = NULL ,
3734 .size = 0 ,
3835 .capacity = 0 ,
3936};
4037
38+ static _Thread_local int system_queue_in_use = 0 ;
39+
4140em_proxying_queue * emscripten_proxy_get_system_queue (void ) {
4241 return & system_proxying_queue ;
4342}
@@ -50,7 +49,6 @@ em_proxying_queue* em_proxying_queue_create(void) {
5049 }
5150 * q = (em_proxying_queue ){
5251 .mutex = PTHREAD_MUTEX_INITIALIZER ,
53- .active_thread = NULL ,
5452 .task_queues = NULL ,
5553 .size = 0 ,
5654 .capacity = 0 ,
@@ -110,32 +108,28 @@ static em_task_queue* get_or_add_tasks_for_thread(em_proxying_queue* q,
110108 return tasks ;
111109}
112110
113- static _Thread_local bool executing_system_queue = false;
114-
115111void emscripten_proxy_execute_queue (em_proxying_queue * q ) {
116112 assert (q != NULL );
117113 assert (pthread_self ());
118114
119- // Recursion guard to avoid infinite recursion when we arrive here from the
120- // pthread_lock call below that executes the system queue. The per-task_queue
121- // recursion lock can't catch these recursions because it can only be checked
122- // after the lock has been acquired.
123- bool is_system_queue = q == & system_proxying_queue ;
124- if (is_system_queue ) {
125- if (executing_system_queue ) {
126- return ;
127- }
128- executing_system_queue = true;
129- }
130-
131- // When the current thread is adding tasks it locks the queue, but we can
115+ // Below is a recursion and deadlock guard:
116+ // The recursion guard is to avoid infinite recursion when we arrive here from
117+ // the pthread_lock call below that executes the system queue. The
118+ // per-task_queue recursion lock can't catch these recursions because it can
119+ // only be checked after the lock has been acquired.
120+ //
121+ // This also guards against deadlocks when adding to the system queue. When
122+ // the current thread is adding tasks, it locks the queue, but we can
132123 // potentially try to execute the queue during the add (from
133124 // emscripten_yield). This will deadlock the thread, so only try to take the
134- // lock if the current thread is not adding to the queue. We then hope the
125+ // lock if the current thread is not using the queue. We then hope the
135126 // queue is executed later when it is unlocked.
136- // XXX: This could leave to starvation if we never process the queue.
137- if (q -> active_thread == pthread_self ()) {
138- return ;
127+ int is_system_queue = q == & system_proxying_queue ;
128+ if (is_system_queue ) {
129+ if (system_queue_in_use ) {
130+ return ;
131+ }
132+ system_queue_in_use = true;
139133 }
140134
141135 pthread_mutex_lock (& q -> mutex );
@@ -148,16 +142,21 @@ void emscripten_proxy_execute_queue(em_proxying_queue* q) {
148142 }
149143
150144 if (is_system_queue ) {
151- executing_system_queue = false;
145+ system_queue_in_use = false;
152146 }
153147}
154148
155149static int do_proxy (em_proxying_queue * q , pthread_t target_thread , task t ) {
156150 assert (q != NULL );
157151 pthread_mutex_lock (& q -> mutex );
158- q -> active_thread = pthread_self ();
152+ int is_system_queue = q == & system_proxying_queue ;
153+ if (is_system_queue ) {
154+ system_queue_in_use = 1 ;
155+ }
159156 em_task_queue * tasks = get_or_add_tasks_for_thread (q , target_thread );
160- q -> active_thread = NULL ;
157+ if (is_system_queue ) {
158+ system_queue_in_use = 0 ;
159+ }
161160 pthread_mutex_unlock (& q -> mutex );
162161 if (tasks == NULL ) {
163162 return 0 ;
0 commit comments