2121#include <stdlib.h>
2222#include <sys/param.h>
2323
24+ #ifdef __EMSCRIPTEN_PTHREADS__
25+ // Note: We use a weak reference here. If it's null we know that threads are
26+ // not cancelable.
27+ weak long __cancel (void );
28+ #endif
29+
2430extern void * _emscripten_main_thread_futex ;
2531
2632static int futex_wait_main_browser_thread (volatile void * addr ,
@@ -53,8 +59,7 @@ static int futex_wait_main_browser_thread(volatile void* addr,
5359 while (1 ) {
5460#ifdef __EMSCRIPTEN_PTHREADS__
5561 if (cancelable && __pthread_self ()-> cancel ) {
56- __pthread_testcancel ();
57- return - ETIMEDOUT ;
62+ return __cancel ();
5863 }
5964#endif
6065 // Check for a timeout.
@@ -79,7 +84,10 @@ static int futex_wait_main_browser_thread(volatile void* addr,
7984 // We were told to stop waiting, so stop.
8085 break ;
8186 }
82- _emscripten_yield (now );
87+ bool timer_fired = _emscripten_yield (now );
88+ if (timer_fired ) {
89+ return - EINTR ;
90+ }
8391
8492 // Check the value, as if we were starting the futex all over again.
8593 // This handles the following case:
@@ -132,27 +140,24 @@ static double dummy() {
132140
133141weak_alias (dummy , _emscripten_next_timer );
134142
135- int emscripten_futex_wait (volatile void * addr , uint32_t val , double max_wait_ms ) {
143+ static int _do_futex_wait (volatile void * addr , uint32_t val , double max_wait_ms ) {
136144 if ((((intptr_t )addr )& 3 ) != 0 ) {
137145 return - EINVAL ;
138146 }
139147
140148 int ret ;
141- emscripten_conditional_set_current_thread_status (EM_THREAD_STATUS_RUNNING , EM_THREAD_STATUS_WAITFUTEX );
142149
143150#ifdef __EMSCRIPTEN_PTHREADS__
144151 pthread_t self = __pthread_self ();
145- bool cancelable = self -> canceldisable != PTHREAD_CANCEL_DISABLE ;
152+ bool cancelable = __cancel && self -> canceldisable != PTHREAD_CANCEL_DISABLE ;
146153#else
147154 bool cancelable = false;
148155#endif
149156
150157 // For the main browser thread and audio worklets we can't use
151158 // __builtin_wasm_memory_atomic_wait32 so we have busy wait instead.
152159 if (!_emscripten_thread_supports_atomics_wait ()) {
153- ret = futex_wait_main_browser_thread (addr , val , max_wait_ms , cancelable );
154- emscripten_conditional_set_current_thread_status (EM_THREAD_STATUS_WAITFUTEX , EM_THREAD_STATUS_RUNNING );
155- return ret ;
160+ return futex_wait_main_browser_thread (addr , val , max_wait_ms , cancelable );
156161 }
157162
158163 DBG ("emscripten_futex_wait ms=%f" , max_wait_ms );
@@ -182,28 +187,31 @@ int emscripten_futex_wait(volatile void *addr, uint32_t val, double max_wait_ms)
182187 }
183188
184189 // Clear the wait_addr
185- DBG ("emscripten_futex_wait done notify=%d cancelable=%d cancel=%d" , !!(self -> wait_addr & NOTIFY_BIT ), cancelable , self -> cancel );
186- self -> wait_addr = 0 ;
190+ bool notified = atomic_exchange (& self -> wait_addr , 0 ) & NOTIFY_BIT ;
191+
192+ // Here we are mimicking the behaviour of musl's __syscall_cp_c which wraps
193+ // the linux futex syscall.
194+ if (self -> cancel && cancelable ) {
195+ return __cancel ();
196+ }
197+
198+ DBG ("emscripten_futex_wait done notified=%d cancelable=%d cancel=%d" , notified , cancelable , self -> cancel );
187199
188200 // Pass 0 here, which means we don't have access to the current time in this
189201 // function. This tells _emscripten_yield to call emscripten_get_now if (and
190202 // only if) it needs to know the time.
191- _emscripten_yield (0 );
192-
193- if (cancelable && self -> cancel ) {
194- __pthread_testcancel ();
195- // If __pthread_testcancel does return here it means that canceldisable
196- // must be set to PTHREAD_CANCEL_MASKED. In this case we emulate the
197- // behaviour of the futex syscall and return ECANCELLED here.
198- // See pthread_cond_timedwait.c for the only use of this flag.
199- emscripten_conditional_set_current_thread_status (EM_THREAD_STATUS_WAITFUTEX , EM_THREAD_STATUS_RUNNING );
200- return - ECANCELED ;
203+ bool timer_fired = _emscripten_yield (0 );
204+ if (notified || timer_fired ) {
205+ return - EINTR ;
201206 }
202207#else // __EMSCRIPTEN_PTHREADS__
203208 ret = __builtin_wasm_memory_atomic_wait32 ((int * )addr , val , max_wait_ns );
209+ bool timer_fired = _emscripten_yield (0 );
210+ if (timer_fired ) {
211+ return - EINTR ;
212+ }
204213#endif // __EMSCRIPTEN_PTHREADS__
205214
206- emscripten_conditional_set_current_thread_status (EM_THREAD_STATUS_WAITFUTEX , EM_THREAD_STATUS_RUNNING );
207215
208216 if (ret == ATOMICS_WAIT_NOT_EQUAL ) {
209217 return - EWOULDBLOCK ;
@@ -214,3 +222,11 @@ int emscripten_futex_wait(volatile void *addr, uint32_t val, double max_wait_ms)
214222 assert (ret == ATOMICS_WAIT_OK );
215223 return 0 ;
216224}
225+
226+
227+ int emscripten_futex_wait (volatile void * addr , uint32_t val , double max_wait_ms ) {
228+ emscripten_conditional_set_current_thread_status (EM_THREAD_STATUS_RUNNING , EM_THREAD_STATUS_WAITFUTEX );
229+ int ret = _do_futex_wait (addr , val , max_wait_ms );
230+ emscripten_conditional_set_current_thread_status (EM_THREAD_STATUS_WAITFUTEX , EM_THREAD_STATUS_RUNNING );
231+ return ret ;
232+ }
0 commit comments