@@ -19,7 +19,7 @@ extern void* _emscripten_main_thread_futex;
1919
2020static int futex_wait_main_browser_thread (volatile void * addr ,
2121 uint32_t val ,
22- double timeout ) {
22+ double timeout , bool cancelable ) {
2323 // Atomics.wait is not available in the main browser thread, so simulate it
2424 // via busy spinning. Only the main browser thread is allowed to call into
2525 // this function. It is not thread-safe to be called from any other thread.
@@ -45,6 +45,12 @@ static int futex_wait_main_browser_thread(volatile void* addr,
4545 assert (last_addr == 0 );
4646
4747 while (1 ) {
48+ #ifdef __EMSCRIPTEN_PTHREADS__
49+ // We if we were cancelled
50+ if (cancelable && pthread_self ()-> cancel ) {
51+ return - ETIMEDOUT ;
52+ }
53+ #endif
4854 // Check for a timeout.
4955 now = emscripten_get_now ();
5056 if (now > end ) {
@@ -119,48 +125,90 @@ int emscripten_futex_wait(volatile void *addr, uint32_t val, double max_wait_ms)
119125 return - EINVAL ;
120126 }
121127
122- // Pass 0 here, which means we don't have access to the current time in this
123- // function. This tells _emscripten_yield to call emscripten_get_now if (and
124- // only if) it needs to know the time.
125- _emscripten_yield (0 );
126-
127128 int ret ;
128129 emscripten_conditional_set_current_thread_status (EM_THREAD_STATUS_RUNNING , EM_THREAD_STATUS_WAITFUTEX );
129130
131+ #ifdef __EMSCRIPTEN_PTHREADS__
132+ bool cancelable = pthread_self ()-> cancelasync == PTHREAD_CANCEL_ASYNCHRONOUS ;
133+ #else
134+ bool cancelable = false;
135+ #endif
136+
130137 // For the main browser thread and audio worklets we can't use
131138 // __builtin_wasm_memory_atomic_wait32 so we have busy wait instead.
132139 if (!_emscripten_thread_supports_atomics_wait ()) {
133- ret = futex_wait_main_browser_thread (addr , val , max_wait_ms );
140+ ret = futex_wait_main_browser_thread (addr , val , max_wait_ms , cancelable );
134141 emscripten_conditional_set_current_thread_status (EM_THREAD_STATUS_WAITFUTEX , EM_THREAD_STATUS_RUNNING );
135142 return ret ;
136143 }
137144
138145 // -1 (or any negative number) means wait indefinitely.
139146 int64_t max_wait_ns = ATOMICS_WAIT_DURATION_INFINITE ;
140147 if (max_wait_ms != INFINITY ) {
141- max_wait_ns = (int64_t )(max_wait_ms * 1000 * 1000 );
148+ max_wait_ns = (int64_t )(max_wait_ms * 1e6 );
142149 }
143- #ifdef EMSCRIPTEN_DYNAMIC_LINKING
144- // After the main thread queues dlopen events, it checks if the target threads
145- // are sleeping.
146- // If `sleeping` is set then the main thread knows that event will be
147- // processed after the sleep (before any other user code). In this case the
148- // main thread does not wait for any kind of response form the thread.
149- // If `sleeping` is not set then we know we should wait for the thread process
150- // the queue, either from the call here directly after setting `sleeping` to
151- // 1, or from another callsite (e.g. the one in `emscripten_yield`).
152- int is_runtime_thread = emscripten_is_main_runtime_thread ();
153- if (!is_runtime_thread ) {
154- __pthread_self ()-> sleeping = 1 ;
155- _emscripten_process_dlopen_queue ();
150+
151+ #ifdef __EMSCRIPTEN_PTHREADS__
152+ // When building with pthread support there are two conditions under which we
153+ // need to limit the amount of time we spend in atomic.wait.
154+ // 1. We are the main runtime thread. In this case we need to be able to
155+ // process proxied events from workers. Note that this is not always
156+ // the same as being the main browser thread. For example, when running
157+ // under node or when launching and emscripten-built program in a Web
158+ // Worker. This this case we limit our wait slices to 1ms intervals.
159+ // 2. When the current thread has async cancellation enabled. In this case
160+ // we limit the wait duration to 100ms intervals.
161+ int64_t wakeup_interval = 0 ;
162+ bool is_runtime_thread = emscripten_is_main_runtime_thread ();
163+ if (is_runtime_thread ) {
164+ // If the current thread is the main runtime thread then only wait in 1ms slices.
165+ wakeup_interval = 1 * 1e6 ;
166+ }
167+ else if (cancelable ) {
168+ // If the current thread is async cancellable then only wait in 100ms slices.
169+ wakeup_interval = 100 * 1e6 ;
170+ }
171+
172+ int64_t interations ;
173+ if (wakeup_interval ) {
174+ interations = max_wait_ns / wakeup_interval ;
175+ max_wait_ns = wakeup_interval ;
156176 }
177+
178+ do {
157179#endif
158- ret = __builtin_wasm_memory_atomic_wait32 ((int * )addr , val , max_wait_ns );
180+ // Pass 0 here, which means we don't have access to the current time in this
181+ // function. This tells _emscripten_yield to call emscripten_get_now if (and
182+ // only if) it needs to know the time.
183+ _emscripten_yield (0 );
184+
159185#ifdef EMSCRIPTEN_DYNAMIC_LINKING
160- if (!is_runtime_thread ) {
161- __pthread_self ()-> sleeping = 0 ;
162- _emscripten_process_dlopen_queue ();
163- }
186+ // After the main thread queues dlopen events, it checks if the target threads
187+ // are sleeping.
188+ // If `sleeping` is set then the main thread knows that event will be
189+ // processed after the sleep (before any other user code). In this case the
190+ // main thread does not wait for any kind of response form the thread.
191+ // If `sleeping` is not set then we know we should wait for the thread process
192+ // the queue, either from the call here directly after setting `sleeping` to
193+ // 1, or from another callsite (e.g. the one in `emscripten_yield`).
194+ if (!is_runtime_thread ) {
195+ __pthread_self ()-> sleeping = 1 ;
196+ _emscripten_process_dlopen_queue ();
197+ }
198+ #endif
199+ ret = __builtin_wasm_memory_atomic_wait32 ((int * )addr , val , max_wait_ns );
200+ #ifdef EMSCRIPTEN_DYNAMIC_LINKING
201+ if (!is_runtime_thread ) {
202+ __pthread_self ()-> sleeping = 0 ;
203+ _emscripten_process_dlopen_queue ();
204+ }
205+ #endif
206+ #ifdef __EMSCRIPTEN_PTHREADS__
207+ if (cancelable && ret == ATOMICS_WAIT_TIMED_OUT && pthread_self ()-> cancel ) {
208+ // We were cancelled
209+ break ;
210+ }
211+ } while (wakeup_interval && ret == ATOMICS_WAIT_TIMED_OUT && (max_wait_ms == INFINITY || interations -- ));
164212#endif
165213
166214 emscripten_conditional_set_current_thread_status (EM_THREAD_STATUS_WAITFUTEX , EM_THREAD_STATUS_RUNNING );
0 commit comments