1 /*
2 * Copyright 2015 The Emscripten Authors. All rights reserved.
3 * Emscripten is available under two separate licenses, the MIT license and the
4 * University of Illinois/NCSA Open Source License. Both these licenses can be
5 * found in the LICENSE file.
6 */
7
8 #define _GNU_SOURCE
9 #include "../internal/libc.h"
10 #include "../internal/pthread_impl.h"
11 #include <assert.h>
12 #include <dirent.h>
13 #include <emscripten.h>
14 #include <emscripten/threading.h>
15 #include <errno.h>
16 #include <fcntl.h>
17 #include <math.h>
18 #include <poll.h>
19 #include <pthread.h>
20 #include <stdarg.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <sys/ioctl.h>
24 #include <sys/mman.h>
25 #include <sys/socket.h>
26 #include <sys/stat.h>
27 #include <sys/statvfs.h>
28 #include <sys/time.h>
29 #include <termios.h>
30 #include <unistd.h>
31 #include <utime.h>
32
33 // With LLVM 3.6, C11 is the default compilation mode.
34 // gets() is deprecated under that standard, but emcc
35 // still provides it, so always include it in the build.
36 #if __STDC_VERSION__ >= 201112L
37 char* gets(char*);
38 #endif
39
40 // Extra pthread_attr_t field:
41 #define _a_transferredcanvases __u.__s[9]
42
43 void __pthread_testcancel();
44
emscripten_pthread_attr_gettransferredcanvases(const pthread_attr_t * a,const char ** str)45 int emscripten_pthread_attr_gettransferredcanvases(const pthread_attr_t* a, const char** str) {
46 *str = (const char*)a->_a_transferredcanvases;
47 return 0;
48 }
49
emscripten_pthread_attr_settransferredcanvases(pthread_attr_t * a,const char * str)50 int emscripten_pthread_attr_settransferredcanvases(pthread_attr_t* a, const char* str) {
51 a->_a_transferredcanvases = (int)str;
52 return 0;
53 }
54
_pthread_getcanceltype()55 int _pthread_getcanceltype() { return pthread_self()->cancelasync; }
56
__pthread_mutex_locked(pthread_mutex_t * mutex)57 static void inline __pthread_mutex_locked(pthread_mutex_t* mutex) {
58 // The lock is now ours, mark this thread as the owner of this lock.
59 assert(mutex);
60 assert(mutex->_m_lock == 0);
61 mutex->_m_lock = pthread_self()->tid;
62 if (_pthread_getcanceltype() == PTHREAD_CANCEL_ASYNCHRONOUS)
63 __pthread_testcancel();
64 }
65
sched_get_priority_max(int policy)66 int sched_get_priority_max(int policy) {
67 // Web workers do not actually support prioritizing threads,
68 // but mimic values that Linux apparently reports, see
69 // http://man7.org/linux/man-pages/man2/sched_get_priority_min.2.html
70 if (policy == SCHED_FIFO || policy == SCHED_RR)
71 return 99;
72 else
73 return 0;
74 }
75
sched_get_priority_min(int policy)76 int sched_get_priority_min(int policy) {
77 // Web workers do not actually support prioritizing threads,
78 // but mimic values that Linux apparently reports, see
79 // http://man7.org/linux/man-pages/man2/sched_get_priority_min.2.html
80 if (policy == SCHED_FIFO || policy == SCHED_RR)
81 return 1;
82 else
83 return 0;
84 }
85
pthread_setcancelstate(int new,int * old)86 int pthread_setcancelstate(int new, int* old) {
87 if (new > 1U)
88 return EINVAL;
89 struct pthread* self = pthread_self();
90 if (old)
91 *old = self->canceldisable;
92 self->canceldisable = new;
93 return 0;
94 }
95
_pthread_isduecanceled(struct pthread * pthread_ptr)96 int _pthread_isduecanceled(struct pthread* pthread_ptr) {
97 return pthread_ptr->threadStatus == 2 /*canceled*/;
98 }
99
__pthread_testcancel()100 void __pthread_testcancel() {
101 struct pthread* self = pthread_self();
102 if (self->canceldisable)
103 return;
104 if (_pthread_isduecanceled(self)) {
105 EM_ASM(throw 'Canceled!');
106 }
107 }
108
pthread_getattr_np(pthread_t t,pthread_attr_t * a)109 int pthread_getattr_np(pthread_t t, pthread_attr_t* a) {
110 *a = (pthread_attr_t){0};
111 a->_a_detach = !!t->detached;
112 a->_a_stackaddr = (uintptr_t)t->stack;
113 a->_a_stacksize = t->stack_size - DEFAULT_STACK_SIZE;
114 return 0;
115 }
116
117 static uint32_t dummyZeroAddress = 0;
118
emscripten_thread_sleep(double msecs)119 void emscripten_thread_sleep(double msecs) {
120 double now = emscripten_get_now();
121 double target = now + msecs;
122
123 __pthread_testcancel(); // pthreads spec: sleep is a cancellation point, so must test if this
124 // thread is cancelled during the sleep.
125 emscripten_current_thread_process_queued_calls();
126
127 // If we have less than this many msecs left to wait, busy spin that instead.
128 const double minimumTimeSliceToSleep = 0.1;
129
130 // main thread may need to run proxied calls, so sleep in very small slices to be responsive.
131 const double maxMsecsSliceToSleep = emscripten_is_main_browser_thread() ? 1 : 100;
132
133 emscripten_conditional_set_current_thread_status(
134 EM_THREAD_STATUS_RUNNING, EM_THREAD_STATUS_SLEEPING);
135 now = emscripten_get_now();
136 while (now < target) {
137 // Keep processing the main loop of the calling thread.
138 __pthread_testcancel(); // pthreads spec: sleep is a cancellation point, so must test if this
139 // thread is cancelled during the sleep.
140 emscripten_current_thread_process_queued_calls();
141
142 now = emscripten_get_now();
143 double msecsToSleep = target - now;
144 if (msecsToSleep > maxMsecsSliceToSleep)
145 msecsToSleep = maxMsecsSliceToSleep;
146 if (msecsToSleep >= minimumTimeSliceToSleep)
147 emscripten_futex_wait(&dummyZeroAddress, 0, msecsToSleep);
148 now = emscripten_get_now();
149 };
150
151 emscripten_conditional_set_current_thread_status(
152 EM_THREAD_STATUS_SLEEPING, EM_THREAD_STATUS_RUNNING);
153 }
154
nanosleep(const struct timespec * req,struct timespec * rem)155 int nanosleep(const struct timespec* req, struct timespec* rem) {
156 if (!req || req->tv_nsec < 0 || req->tv_nsec > 999999999L || req->tv_sec < 0) {
157 errno = EINVAL;
158 return -1;
159 }
160 emscripten_thread_sleep(req->tv_sec * 1000.0 + req->tv_nsec / 1e6);
161 return 0;
162 }
163
usleep(unsigned usec)164 int usleep(unsigned usec) {
165 emscripten_thread_sleep(usec / 1e3);
166 return 0;
167 }
168
169 // Allocator and deallocator for em_queued_call objects.
em_queued_call_malloc()170 static em_queued_call* em_queued_call_malloc() {
171 em_queued_call* call = (em_queued_call*)malloc(sizeof(em_queued_call));
172 assert(call); // Not a programming error, but use assert() in debug builds to catch OOM scenarios.
173 if (call) {
174 call->operationDone = 0;
175 call->functionPtr = 0;
176 call->satelliteData = 0;
177 }
178 return call;
179 }
em_queued_call_free(em_queued_call * call)180 static void em_queued_call_free(em_queued_call* call) {
181 if (call)
182 free(call->satelliteData);
183 free(call);
184 }
185
emscripten_async_waitable_close(em_queued_call * call)186 void emscripten_async_waitable_close(em_queued_call* call) { em_queued_call_free(call); }
187
188 extern double emscripten_receive_on_main_thread_js(int functionIndex, int numCallArgs, double* args);
189 extern int _emscripten_notify_thread_queue(pthread_t targetThreadId, pthread_t mainThreadId);
190
191 #if defined(__has_feature)
192 #if __has_feature(address_sanitizer)
193 #define HAS_ASAN
194 void __lsan_disable_in_this_thread(void);
195 void __lsan_enable_in_this_thread(void);
196 int emscripten_builtin_pthread_create(void *thread, void *attr,
197 void *(*callback)(void *), void *arg);
198 #endif
199 #endif
200
_do_call(em_queued_call * q)201 static void _do_call(em_queued_call* q) {
202 // C function pointer
203 assert(EM_FUNC_SIG_NUM_FUNC_ARGUMENTS(q->functionEnum) <= EM_QUEUED_CALL_MAX_ARGS);
204 switch (q->functionEnum) {
205 case EM_PROXIED_PTHREAD_CREATE:
206 #ifdef HAS_ASAN
207 // ASan wraps the emscripten_builtin_pthread_create call in __lsan::ScopedInterceptorDisabler.
208 // Unfortunately, that only disables it on the thread that made the call.
209 // This is sufficient on the main thread.
210 // On non-main threads, pthread_create gets proxied to the main thread, where LSan is not
211 // disabled. This makes it necessary for us to disable LSan here, so that it does not detect
212 // pthread's internal allocations as leaks.
213 __lsan_disable_in_this_thread();
214 q->returnValue.i =
215 emscripten_builtin_pthread_create(q->args[0].vp, q->args[1].vp, q->args[2].vp, q->args[3].vp);
216 __lsan_enable_in_this_thread();
217 #else
218 q->returnValue.i =
219 pthread_create(q->args[0].vp, q->args[1].vp, q->args[2].vp, q->args[3].vp);
220 #endif
221 break;
222 case EM_PROXIED_CREATE_CONTEXT:
223 q->returnValue.i = emscripten_webgl_create_context(q->args[0].cp, q->args[1].vp);
224 break;
225 case EM_PROXIED_RESIZE_OFFSCREENCANVAS:
226 q->returnValue.i =
227 emscripten_set_canvas_element_size(q->args[0].cp, q->args[1].i, q->args[2].i);
228 break;
229 case EM_PROXIED_JS_FUNCTION:
230 q->returnValue.d =
231 emscripten_receive_on_main_thread_js((int)q->functionPtr, q->args[0].i, &q->args[1].d);
232 break;
233 case EM_FUNC_SIG_V:
234 ((em_func_v)q->functionPtr)();
235 break;
236 case EM_FUNC_SIG_VI:
237 ((em_func_vi)q->functionPtr)(q->args[0].i);
238 break;
239 case EM_FUNC_SIG_VF:
240 ((em_func_vf)q->functionPtr)(q->args[0].f);
241 break;
242 case EM_FUNC_SIG_VII:
243 ((em_func_vii)q->functionPtr)(q->args[0].i, q->args[1].i);
244 break;
245 case EM_FUNC_SIG_VIF:
246 ((em_func_vif)q->functionPtr)(q->args[0].i, q->args[1].f);
247 break;
248 case EM_FUNC_SIG_VFF:
249 ((em_func_vff)q->functionPtr)(q->args[0].f, q->args[1].f);
250 break;
251 case EM_FUNC_SIG_VIII:
252 ((em_func_viii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i);
253 break;
254 case EM_FUNC_SIG_VIIF:
255 ((em_func_viif)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].f);
256 break;
257 case EM_FUNC_SIG_VIFF:
258 ((em_func_viff)q->functionPtr)(q->args[0].i, q->args[1].f, q->args[2].f);
259 break;
260 case EM_FUNC_SIG_VFFF:
261 ((em_func_vfff)q->functionPtr)(q->args[0].f, q->args[1].f, q->args[2].f);
262 break;
263 case EM_FUNC_SIG_VIIII:
264 ((em_func_viiii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i, q->args[3].i);
265 break;
266 case EM_FUNC_SIG_VIIFI:
267 ((em_func_viifi)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].f, q->args[3].i);
268 break;
269 case EM_FUNC_SIG_VIFFF:
270 ((em_func_vifff)q->functionPtr)(q->args[0].i, q->args[1].f, q->args[2].f, q->args[3].f);
271 break;
272 case EM_FUNC_SIG_VFFFF:
273 ((em_func_vffff)q->functionPtr)(q->args[0].f, q->args[1].f, q->args[2].f, q->args[3].f);
274 break;
275 case EM_FUNC_SIG_VIIIII:
276 ((em_func_viiiii)q->functionPtr)(
277 q->args[0].i, q->args[1].i, q->args[2].i, q->args[3].i, q->args[4].i);
278 break;
279 case EM_FUNC_SIG_VIFFFF:
280 ((em_func_viffff)q->functionPtr)(
281 q->args[0].i, q->args[1].f, q->args[2].f, q->args[3].f, q->args[4].f);
282 break;
283 case EM_FUNC_SIG_VIIIIII:
284 ((em_func_viiiiii)q->functionPtr)(
285 q->args[0].i, q->args[1].i, q->args[2].i, q->args[3].i, q->args[4].i, q->args[5].i);
286 break;
287 case EM_FUNC_SIG_VIIIIIII:
288 ((em_func_viiiiiii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i, q->args[3].i,
289 q->args[4].i, q->args[5].i, q->args[6].i);
290 break;
291 case EM_FUNC_SIG_VIIIIIIII:
292 ((em_func_viiiiiiii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i, q->args[3].i,
293 q->args[4].i, q->args[5].i, q->args[6].i, q->args[7].i);
294 break;
295 case EM_FUNC_SIG_VIIIIIIIII:
296 ((em_func_viiiiiiiii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i, q->args[3].i,
297 q->args[4].i, q->args[5].i, q->args[6].i, q->args[7].i, q->args[8].i);
298 break;
299 case EM_FUNC_SIG_VIIIIIIIIII:
300 ((em_func_viiiiiiiiii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i,
301 q->args[3].i, q->args[4].i, q->args[5].i, q->args[6].i, q->args[7].i, q->args[8].i,
302 q->args[9].i);
303 break;
304 case EM_FUNC_SIG_VIIIIIIIIIII:
305 ((em_func_viiiiiiiiiii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i,
306 q->args[3].i, q->args[4].i, q->args[5].i, q->args[6].i, q->args[7].i, q->args[8].i,
307 q->args[9].i, q->args[10].i);
308 break;
309 case EM_FUNC_SIG_I:
310 q->returnValue.i = ((em_func_i)q->functionPtr)();
311 break;
312 case EM_FUNC_SIG_II:
313 q->returnValue.i = ((em_func_ii)q->functionPtr)(q->args[0].i);
314 break;
315 case EM_FUNC_SIG_III:
316 q->returnValue.i = ((em_func_iii)q->functionPtr)(q->args[0].i, q->args[1].i);
317 break;
318 case EM_FUNC_SIG_IIII:
319 q->returnValue.i = ((em_func_iiii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i);
320 break;
321 case EM_FUNC_SIG_IIIII:
322 q->returnValue.i =
323 ((em_func_iiiii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i, q->args[3].i);
324 break;
325 case EM_FUNC_SIG_IIIIII:
326 q->returnValue.i = ((em_func_iiiiii)q->functionPtr)(
327 q->args[0].i, q->args[1].i, q->args[2].i, q->args[3].i, q->args[4].i);
328 break;
329 case EM_FUNC_SIG_IIIIIII:
330 q->returnValue.i = ((em_func_iiiiiii)q->functionPtr)(
331 q->args[0].i, q->args[1].i, q->args[2].i, q->args[3].i, q->args[4].i, q->args[5].i);
332 break;
333 case EM_FUNC_SIG_IIIIIIII:
334 q->returnValue.i = ((em_func_iiiiiiii)q->functionPtr)(q->args[0].i, q->args[1].i,
335 q->args[2].i, q->args[3].i, q->args[4].i, q->args[5].i, q->args[6].i);
336 break;
337 case EM_FUNC_SIG_IIIIIIIII:
338 q->returnValue.i = ((em_func_iiiiiiiii)q->functionPtr)(q->args[0].i, q->args[1].i,
339 q->args[2].i, q->args[3].i, q->args[4].i, q->args[5].i, q->args[6].i, q->args[7].i);
340 break;
341 case EM_FUNC_SIG_IIIIIIIIII:
342 q->returnValue.i =
343 ((em_func_iiiiiiiiii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i,
344 q->args[3].i, q->args[4].i, q->args[5].i, q->args[6].i, q->args[7].i, q->args[8].i);
345 break;
346 default:
347 assert(0 && "Invalid Emscripten pthread _do_call opcode!");
348 }
349
350 // If the caller is detached from this operation, it is the main thread's responsibility to free
351 // up the call object.
352 if (q->calleeDelete) {
353 emscripten_async_waitable_close(q);
354 // No need to wake a listener, nothing is listening to this since the call object is detached.
355 } else {
356 // The caller owns this call object, it is listening to it and will free it up.
357 q->operationDone = 1;
358 emscripten_futex_wake(&q->operationDone, INT_MAX);
359 }
360 }
361
362 #define CALL_QUEUE_SIZE 128
363
364 typedef struct CallQueue {
365 void* target_thread;
366 em_queued_call** call_queue;
367 int call_queue_head; // Shared data synchronized by call_queue_lock.
368 int call_queue_tail;
369 struct CallQueue* next;
370 } CallQueue;
371
372 // Currently global to the queue, but this can be improved to be per-queue specific. (TODO: with
373 // lockfree list operations on callQueue_head, or removing the list by moving this data to
374 // pthread_t)
375 static pthread_mutex_t call_queue_lock = PTHREAD_MUTEX_INITIALIZER;
376 static CallQueue* callQueue_head = 0;
377
GetQueue(void * target)378 static CallQueue* GetQueue(
379 void* target) // Not thread safe, call while having call_queue_lock obtained.
380 {
381 assert(target);
382 CallQueue* q = callQueue_head;
383 while (q && q->target_thread != target)
384 q = q->next;
385 return q;
386 }
387
GetOrAllocateQueue(void * target)388 static CallQueue* GetOrAllocateQueue(
389 void* target) // Not thread safe, call while having call_queue_lock obtained.
390 {
391 CallQueue* q = GetQueue(target);
392 if (q)
393 return q;
394
395 q = (CallQueue*)malloc(sizeof(CallQueue));
396 q->target_thread = target;
397 q->call_queue = 0;
398 q->call_queue_head = 0;
399 q->call_queue_tail = 0;
400 q->next = 0;
401 if (callQueue_head) {
402 CallQueue* last = callQueue_head;
403 while (last->next)
404 last = last->next;
405 last->next = q;
406 } else {
407 callQueue_head = q;
408 }
409 return q;
410 }
411
emscripten_wait_for_call_v(em_queued_call * call,double timeoutMSecs)412 EMSCRIPTEN_RESULT emscripten_wait_for_call_v(em_queued_call* call, double timeoutMSecs) {
413 int r;
414
415 int done = emscripten_atomic_load_u32(&call->operationDone);
416 if (!done) {
417 double now = emscripten_get_now();
418 double waitEndTime = now + timeoutMSecs;
419 emscripten_set_current_thread_status(EM_THREAD_STATUS_WAITPROXY);
420 while (!done && now < waitEndTime) {
421 r = emscripten_futex_wait(&call->operationDone, 0, waitEndTime - now);
422 done = emscripten_atomic_load_u32(&call->operationDone);
423 now = emscripten_get_now();
424 }
425 emscripten_set_current_thread_status(EM_THREAD_STATUS_RUNNING);
426 }
427 if (done)
428 return EMSCRIPTEN_RESULT_SUCCESS;
429 else
430 return EMSCRIPTEN_RESULT_TIMED_OUT;
431 }
432
emscripten_wait_for_call_i(em_queued_call * call,double timeoutMSecs,int * outResult)433 EMSCRIPTEN_RESULT emscripten_wait_for_call_i(
434 em_queued_call* call, double timeoutMSecs, int* outResult) {
435 EMSCRIPTEN_RESULT res = emscripten_wait_for_call_v(call, timeoutMSecs);
436 if (res == EMSCRIPTEN_RESULT_SUCCESS && outResult)
437 *outResult = call->returnValue.i;
438 return res;
439 }
440
441 static pthread_t main_browser_thread_id_ = 0;
442
emscripten_register_main_browser_thread_id(pthread_t main_browser_thread_id)443 void EMSCRIPTEN_KEEPALIVE emscripten_register_main_browser_thread_id(
444 pthread_t main_browser_thread_id) {
445 main_browser_thread_id_ = main_browser_thread_id;
446 }
447
emscripten_main_browser_thread_id()448 pthread_t EMSCRIPTEN_KEEPALIVE emscripten_main_browser_thread_id() {
449 return main_browser_thread_id_;
450 }
451
do_emscripten_dispatch_to_thread(pthread_t target_thread,em_queued_call * call)452 int EMSCRIPTEN_KEEPALIVE do_emscripten_dispatch_to_thread(
453 pthread_t target_thread, em_queued_call* call) {
454 assert(call);
455
456 // #if PTHREADS_DEBUG // TODO: Create a debug version of pthreads library
457 // EM_ASM_INT({dump('thread ' + _pthread_self() + ' (ENVIRONMENT_IS_WORKER: ' +
458 //ENVIRONMENT_IS_WORKER + '), queueing call of function enum=' + $0 + '/ptr=' + $1 + ' on thread '
459 //+ $2 + '\n' + new Error().stack)}, call->functionEnum, call->functionPtr, target_thread);
460 // #endif
461
462 // Can't be a null pointer here, but can't be EM_CALLBACK_THREAD_CONTEXT_MAIN_BROWSER_THREAD
463 // either.
464 assert(target_thread);
465 if (target_thread == EM_CALLBACK_THREAD_CONTEXT_MAIN_BROWSER_THREAD)
466 target_thread = emscripten_main_browser_thread_id();
467
468 // If we are the target recipient of this message, we can just call the operation directly.
469 if (target_thread == EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD ||
470 target_thread == pthread_self()) {
471 _do_call(call);
472 return 1;
473 }
474
475 // Add the operation to the call queue of the main runtime thread.
476 pthread_mutex_lock(&call_queue_lock);
477 CallQueue* q = GetOrAllocateQueue(target_thread);
478 if (!q->call_queue)
479 q->call_queue = malloc(
480 sizeof(em_queued_call*) * CALL_QUEUE_SIZE); // Shared data synchronized by call_queue_lock.
481
482 int head = emscripten_atomic_load_u32((void*)&q->call_queue_head);
483 int tail = emscripten_atomic_load_u32((void*)&q->call_queue_tail);
484 int new_tail = (tail + 1) % CALL_QUEUE_SIZE;
485
486 while (new_tail == head) { // Queue is full?
487 pthread_mutex_unlock(&call_queue_lock);
488
489 // If queue of the main browser thread is full, then we wait. (never drop messages for the main
490 // browser thread)
491 if (target_thread == emscripten_main_browser_thread_id()) {
492 emscripten_futex_wait((void*)&q->call_queue_head, head, INFINITY);
493 pthread_mutex_lock(&call_queue_lock);
494 head = emscripten_atomic_load_u32((void*)&q->call_queue_head);
495 tail = emscripten_atomic_load_u32((void*)&q->call_queue_tail);
496 new_tail = (tail + 1) % CALL_QUEUE_SIZE;
497 } else {
498 // For the queues of other threads, just drop the message.
499 // #if DEBUG TODO: a debug build of pthreads library?
500 // EM_ASM(console.error('Pthread queue overflowed, dropping queued
501 //message to thread. ' + new Error().stack));
502 // #endif
503 em_queued_call_free(call);
504 return 0;
505 }
506 }
507
508 q->call_queue[tail] = call;
509
510 // If the call queue was empty, the main runtime thread is likely idle in the browser event loop,
511 // so send a message to it to ensure that it wakes up to start processing the command we have
512 // posted.
513 if (head == tail) {
514 int success = _emscripten_notify_thread_queue(target_thread, emscripten_main_browser_thread_id());
515 // Failed to dispatch the thread, delete the crafted message.
516 if (!success) {
517 em_queued_call_free(call);
518 pthread_mutex_unlock(&call_queue_lock);
519 return 0;
520 }
521 }
522
523 emscripten_atomic_store_u32((void*)&q->call_queue_tail, new_tail);
524
525 pthread_mutex_unlock(&call_queue_lock);
526
527 return 0;
528 }
529
emscripten_async_run_in_main_thread(em_queued_call * call)530 void EMSCRIPTEN_KEEPALIVE emscripten_async_run_in_main_thread(em_queued_call* call) {
531 do_emscripten_dispatch_to_thread(emscripten_main_browser_thread_id(), call);
532 }
533
emscripten_sync_run_in_main_thread(em_queued_call * call)534 void EMSCRIPTEN_KEEPALIVE emscripten_sync_run_in_main_thread(em_queued_call* call) {
535 emscripten_async_run_in_main_thread(call);
536
537 // Enter to wait for the operation to complete.
538 emscripten_wait_for_call_v(call, INFINITY);
539 }
540
emscripten_sync_run_in_main_thread_0(int function)541 void* EMSCRIPTEN_KEEPALIVE emscripten_sync_run_in_main_thread_0(int function) {
542 em_queued_call q = {function};
543 q.returnValue.vp = 0;
544 emscripten_sync_run_in_main_thread(&q);
545 return q.returnValue.vp;
546 }
547
emscripten_sync_run_in_main_thread_1(int function,void * arg1)548 void* EMSCRIPTEN_KEEPALIVE emscripten_sync_run_in_main_thread_1(int function, void* arg1) {
549 em_queued_call q = {function};
550 q.args[0].vp = arg1;
551 q.returnValue.vp = 0;
552 emscripten_sync_run_in_main_thread(&q);
553 return q.returnValue.vp;
554 }
555
emscripten_sync_run_in_main_thread_2(int function,void * arg1,void * arg2)556 void* EMSCRIPTEN_KEEPALIVE emscripten_sync_run_in_main_thread_2(
557 int function, void* arg1, void* arg2) {
558 em_queued_call q = {function};
559 q.args[0].vp = arg1;
560 q.args[1].vp = arg2;
561 q.returnValue.vp = 0;
562 emscripten_sync_run_in_main_thread(&q);
563 return q.returnValue.vp;
564 }
565
emscripten_sync_run_in_main_thread_xprintf_varargs(int function,int param0,const char * format,...)566 void* EMSCRIPTEN_KEEPALIVE emscripten_sync_run_in_main_thread_xprintf_varargs(
567 int function, int param0, const char* format, ...) {
568 va_list args;
569 va_start(args, format);
570 const int CAP = 128;
571 char str[CAP];
572 char* s = str;
573 int len = vsnprintf(s, CAP, format, args);
574 if (len >= CAP) {
575 s = (char*)malloc(len + 1);
576 va_start(args, format);
577 len = vsnprintf(s, len + 1, format, args);
578 }
579 em_queued_call q = {function};
580 q.args[0].vp = (void*)param0;
581 q.args[1].vp = s;
582 q.returnValue.vp = 0;
583 emscripten_sync_run_in_main_thread(&q);
584 if (s != str)
585 free(s);
586 return q.returnValue.vp;
587 }
588
emscripten_sync_run_in_main_thread_3(int function,void * arg1,void * arg2,void * arg3)589 void* EMSCRIPTEN_KEEPALIVE emscripten_sync_run_in_main_thread_3(
590 int function, void* arg1, void* arg2, void* arg3) {
591 em_queued_call q = {function};
592 q.args[0].vp = arg1;
593 q.args[1].vp = arg2;
594 q.args[2].vp = arg3;
595 q.returnValue.vp = 0;
596 emscripten_sync_run_in_main_thread(&q);
597 return q.returnValue.vp;
598 }
599
emscripten_sync_run_in_main_thread_4(int function,void * arg1,void * arg2,void * arg3,void * arg4)600 void* EMSCRIPTEN_KEEPALIVE emscripten_sync_run_in_main_thread_4(
601 int function, void* arg1, void* arg2, void* arg3, void* arg4) {
602 em_queued_call q = {function};
603 q.args[0].vp = arg1;
604 q.args[1].vp = arg2;
605 q.args[2].vp = arg3;
606 q.args[3].vp = arg4;
607 q.returnValue.vp = 0;
608 emscripten_sync_run_in_main_thread(&q);
609 return q.returnValue.vp;
610 }
611
emscripten_sync_run_in_main_thread_5(int function,void * arg1,void * arg2,void * arg3,void * arg4,void * arg5)612 void* EMSCRIPTEN_KEEPALIVE emscripten_sync_run_in_main_thread_5(
613 int function, void* arg1, void* arg2, void* arg3, void* arg4, void* arg5) {
614 em_queued_call q = {function};
615 q.args[0].vp = arg1;
616 q.args[1].vp = arg2;
617 q.args[2].vp = arg3;
618 q.args[3].vp = arg4;
619 q.args[4].vp = arg5;
620 q.returnValue.vp = 0;
621 emscripten_sync_run_in_main_thread(&q);
622 return q.returnValue.vp;
623 }
624
emscripten_sync_run_in_main_thread_6(int function,void * arg1,void * arg2,void * arg3,void * arg4,void * arg5,void * arg6)625 void* EMSCRIPTEN_KEEPALIVE emscripten_sync_run_in_main_thread_6(
626 int function, void* arg1, void* arg2, void* arg3, void* arg4, void* arg5, void* arg6) {
627 em_queued_call q = {function};
628 q.args[0].vp = arg1;
629 q.args[1].vp = arg2;
630 q.args[2].vp = arg3;
631 q.args[3].vp = arg4;
632 q.args[4].vp = arg5;
633 q.args[5].vp = arg6;
634 q.returnValue.vp = 0;
635 emscripten_sync_run_in_main_thread(&q);
636 return q.returnValue.vp;
637 }
638
emscripten_sync_run_in_main_thread_7(int function,void * arg1,void * arg2,void * arg3,void * arg4,void * arg5,void * arg6,void * arg7)639 void* EMSCRIPTEN_KEEPALIVE emscripten_sync_run_in_main_thread_7(int function, void* arg1,
640 void* arg2, void* arg3, void* arg4, void* arg5, void* arg6, void* arg7) {
641 em_queued_call q = {function};
642 q.args[0].vp = arg1;
643 q.args[1].vp = arg2;
644 q.args[2].vp = arg3;
645 q.args[3].vp = arg4;
646 q.args[4].vp = arg5;
647 q.args[5].vp = arg6;
648 q.args[6].vp = arg7;
649 q.returnValue.vp = 0;
650 emscripten_sync_run_in_main_thread(&q);
651 return q.returnValue.vp;
652 }
653
emscripten_current_thread_process_queued_calls()654 void EMSCRIPTEN_KEEPALIVE emscripten_current_thread_process_queued_calls() {
655 // #if PTHREADS_DEBUG == 2
656 // EM_ASM(console.error('thread ' + _pthread_self() + ':
657 //emscripten_current_thread_process_queued_calls(), ' + new Error().stack));
658 // #endif
659
660 // TODO: Under certain conditions we may want to have a nesting guard also for pthreads (and it
661 // will certainly be cleaner that way), but we don't yet have TLS variables outside
662 // pthread_set/getspecific, so convert this to TLS after TLS is implemented.
663 static int bool_main_thread_inside_nested_process_queued_calls = 0;
664
665 if (emscripten_is_main_browser_thread()) {
666 // It is possible that when processing a queued call, the call flow leads back to calling this
667 // function in a nested fashion! Therefore this scenario must explicitly be detected, and
668 // processing the queue must be avoided if we are nesting, or otherwise the same queued calls
669 // would be processed again and again.
670 if (bool_main_thread_inside_nested_process_queued_calls)
671 return;
672 // This must be before pthread_mutex_lock(), since pthread_mutex_lock() can call back to this
673 // function.
674 bool_main_thread_inside_nested_process_queued_calls = 1;
675 }
676
677 pthread_mutex_lock(&call_queue_lock);
678 CallQueue* q = GetQueue(pthread_self());
679 if (!q) {
680 pthread_mutex_unlock(&call_queue_lock);
681 if (emscripten_is_main_browser_thread())
682 bool_main_thread_inside_nested_process_queued_calls = 0;
683 return;
684 }
685
686 int head = emscripten_atomic_load_u32((void*)&q->call_queue_head);
687 int tail = emscripten_atomic_load_u32((void*)&q->call_queue_tail);
688 while (head != tail) {
689 // Assume that the call is heavy, so unlock access to the call queue while it is being
690 // performed.
691 pthread_mutex_unlock(&call_queue_lock);
692 _do_call(q->call_queue[head]);
693 pthread_mutex_lock(&call_queue_lock);
694
695 head = (head + 1) % CALL_QUEUE_SIZE;
696 emscripten_atomic_store_u32((void*)&q->call_queue_head, head);
697 tail = emscripten_atomic_load_u32((void*)&q->call_queue_tail);
698 }
699 pthread_mutex_unlock(&call_queue_lock);
700
701 // If the queue was full and we had waiters pending to get to put data to queue, wake them up.
702 emscripten_futex_wake((void*)&q->call_queue_head, 0x7FFFFFFF);
703
704 if (emscripten_is_main_browser_thread())
705 bool_main_thread_inside_nested_process_queued_calls = 0;
706 }
707
emscripten_main_thread_process_queued_calls()708 void EMSCRIPTEN_KEEPALIVE emscripten_main_thread_process_queued_calls() {
709 if (!emscripten_is_main_runtime_thread())
710 return;
711
712 emscripten_current_thread_process_queued_calls();
713 }
714
emscripten_sync_run_in_main_runtime_thread_(EM_FUNC_SIGNATURE sig,void * func_ptr,...)715 int emscripten_sync_run_in_main_runtime_thread_(EM_FUNC_SIGNATURE sig, void* func_ptr, ...) {
716 int numArguments = EM_FUNC_SIG_NUM_FUNC_ARGUMENTS(sig);
717 em_queued_call q = {sig, func_ptr};
718
719 EM_FUNC_SIGNATURE argumentsType = sig & EM_FUNC_SIG_ARGUMENTS_TYPE_MASK;
720 va_list args;
721 va_start(args, func_ptr);
722 for (int i = 0; i < numArguments; ++i) {
723 switch ((argumentsType & EM_FUNC_SIG_ARGUMENT_TYPE_SIZE_MASK)) {
724 case EM_FUNC_SIG_PARAM_I:
725 q.args[i].i = va_arg(args, int);
726 break;
727 case EM_FUNC_SIG_PARAM_I64:
728 q.args[i].i64 = va_arg(args, int64_t);
729 break;
730 case EM_FUNC_SIG_PARAM_F:
731 q.args[i].f = (float)va_arg(args, double);
732 break;
733 case EM_FUNC_SIG_PARAM_D:
734 q.args[i].d = va_arg(args, double);
735 break;
736 }
737 argumentsType >>= EM_FUNC_SIG_ARGUMENT_TYPE_SIZE_SHIFT;
738 }
739 va_end(args);
740 emscripten_sync_run_in_main_thread(&q);
741 return q.returnValue.i;
742 }
743
emscripten_run_in_main_runtime_thread_js(int index,int num_args,double * buffer,int sync)744 EMSCRIPTEN_KEEPALIVE double emscripten_run_in_main_runtime_thread_js(int index, int num_args, double* buffer, int sync) {
745 em_queued_call q;
746 em_queued_call *c;
747 if (sync) {
748 q.operationDone = 0;
749 q.satelliteData = 0;
750 c = &q;
751 } else {
752 c = em_queued_call_malloc();
753 }
754 c->calleeDelete = 1-sync;
755 c->functionEnum = EM_PROXIED_JS_FUNCTION;
756 c->functionPtr = (void*)index;
757 // We write out the JS doubles into args[], which must be of appropriate size - JS will assume that.
758 assert(sizeof(em_variant_val) == sizeof(double));
759 assert(num_args+1 <= EM_QUEUED_JS_CALL_MAX_ARGS);
760 c->args[0].i = num_args;
761 for (int i = 0; i < num_args; i++) {
762 c->args[i+1].d = buffer[i];
763 }
764
765 if (sync) {
766 emscripten_sync_run_in_main_thread(&q);
767 return q.returnValue.d;
768 } else {
769 // 'async' runs are fire and forget, where the caller detaches itself from the call object after
770 // returning here, and it is the callee's responsibility to free up the memory after the call
771 // has been performed.
772 emscripten_async_run_in_main_thread(c);
773 return 0;
774 }
775 }
776
emscripten_async_run_in_main_runtime_thread_(EM_FUNC_SIGNATURE sig,void * func_ptr,...)777 void emscripten_async_run_in_main_runtime_thread_(EM_FUNC_SIGNATURE sig, void* func_ptr, ...) {
778 int numArguments = EM_FUNC_SIG_NUM_FUNC_ARGUMENTS(sig);
779 em_queued_call* q = em_queued_call_malloc();
780 if (!q)
781 return;
782 q->functionEnum = sig;
783 q->functionPtr = func_ptr;
784
785 EM_FUNC_SIGNATURE argumentsType = sig & EM_FUNC_SIG_ARGUMENTS_TYPE_MASK;
786 va_list args;
787 va_start(args, func_ptr);
788 for (int i = 0; i < numArguments; ++i) {
789 switch ((argumentsType & EM_FUNC_SIG_ARGUMENT_TYPE_SIZE_MASK)) {
790 case EM_FUNC_SIG_PARAM_I:
791 q->args[i].i = va_arg(args, int);
792 break;
793 case EM_FUNC_SIG_PARAM_I64:
794 q->args[i].i64 = va_arg(args, int64_t);
795 break;
796 case EM_FUNC_SIG_PARAM_F:
797 q->args[i].f = (float)va_arg(args, double);
798 break;
799 case EM_FUNC_SIG_PARAM_D:
800 q->args[i].d = va_arg(args, double);
801 break;
802 }
803 argumentsType >>= EM_FUNC_SIG_ARGUMENT_TYPE_SIZE_SHIFT;
804 }
805 va_end(args);
806 // 'async' runs are fire and forget, where the caller detaches itself from the call object after
807 // returning here, and it is the callee's responsibility to free up the memory after the call has
808 // been performed.
809 q->calleeDelete = 1;
810 emscripten_async_run_in_main_thread(q);
811 }
812
emscripten_async_waitable_run_in_main_runtime_thread_(EM_FUNC_SIGNATURE sig,void * func_ptr,...)813 em_queued_call* emscripten_async_waitable_run_in_main_runtime_thread_(
814 EM_FUNC_SIGNATURE sig, void* func_ptr, ...) {
815 int numArguments = EM_FUNC_SIG_NUM_FUNC_ARGUMENTS(sig);
816 em_queued_call* q = em_queued_call_malloc();
817 if (!q)
818 return NULL;
819 q->functionEnum = sig;
820 q->functionPtr = func_ptr;
821
822 EM_FUNC_SIGNATURE argumentsType = sig & EM_FUNC_SIG_ARGUMENTS_TYPE_MASK;
823 va_list args;
824 va_start(args, func_ptr);
825 for (int i = 0; i < numArguments; ++i) {
826 switch ((argumentsType & EM_FUNC_SIG_ARGUMENT_TYPE_SIZE_MASK)) {
827 case EM_FUNC_SIG_PARAM_I:
828 q->args[i].i = va_arg(args, int);
829 break;
830 case EM_FUNC_SIG_PARAM_I64:
831 q->args[i].i64 = va_arg(args, int64_t);
832 break;
833 case EM_FUNC_SIG_PARAM_F:
834 q->args[i].f = (float)va_arg(args, double);
835 break;
836 case EM_FUNC_SIG_PARAM_D:
837 q->args[i].d = va_arg(args, double);
838 break;
839 }
840 argumentsType >>= EM_FUNC_SIG_ARGUMENT_TYPE_SIZE_SHIFT;
841 }
842 va_end(args);
843 // 'async waitable' runs are waited on by the caller, so the call object needs to remain alive for
844 // the caller to access it after the operation is done. The caller is responsible in cleaning up
845 // the object after done.
846 q->calleeDelete = 0;
847 emscripten_async_run_in_main_thread(q);
848 return q;
849 }
850
_emscripten_call_on_thread(int forceAsync,pthread_t targetThread,EM_FUNC_SIGNATURE sig,void * func_ptr,void * satellite,...)851 int EMSCRIPTEN_KEEPALIVE _emscripten_call_on_thread(
852 int forceAsync,
853 pthread_t targetThread, EM_FUNC_SIGNATURE sig, void* func_ptr, void* satellite, ...) {
854 int numArguments = EM_FUNC_SIG_NUM_FUNC_ARGUMENTS(sig);
855 em_queued_call* q = em_queued_call_malloc();
856 assert(q);
857 // TODO: handle errors in a better way, this pattern appears in several places
858 // in this file. The current behavior makes the calling thread hang as
859 // it waits (for synchronous calls).
860 // If we failed to allocate, return 0 which means we did not execute anything
861 // (we also never will in that case).
862 if (!q)
863 return 0;
864 q->functionEnum = sig;
865 q->functionPtr = func_ptr;
866 q->satelliteData = satellite;
867
868 EM_FUNC_SIGNATURE argumentsType = sig & EM_FUNC_SIG_ARGUMENTS_TYPE_MASK;
869 va_list args;
870 va_start(args, satellite);
871 for (int i = 0; i < numArguments; ++i) {
872 switch ((argumentsType & EM_FUNC_SIG_ARGUMENT_TYPE_SIZE_MASK)) {
873 case EM_FUNC_SIG_PARAM_I:
874 q->args[i].i = va_arg(args, int);
875 break;
876 case EM_FUNC_SIG_PARAM_I64:
877 q->args[i].i64 = va_arg(args, int64_t);
878 break;
879 case EM_FUNC_SIG_PARAM_F:
880 q->args[i].f = (float)va_arg(args, double);
881 break;
882 case EM_FUNC_SIG_PARAM_D:
883 q->args[i].d = va_arg(args, double);
884 break;
885 }
886 argumentsType >>= EM_FUNC_SIG_ARGUMENT_TYPE_SIZE_SHIFT;
887 }
888 va_end(args);
889
890 // 'async' runs are fire and forget, where the caller detaches itself from the call object after
891 // returning here, and it is the callee's responsibility to free up the memory after the call has
892 // been performed.
893 // Note that the call here might not be async if on the same thread, but for
894 // consistency use the same convention of calleeDelete.
895 q->calleeDelete = 1;
896 // The called function will not be async if we are on the same thread; force
897 // async if the user asked for that.
898 if (forceAsync) {
899 EM_ASM({
900 setTimeout(function() {
901 _do_emscripten_dispatch_to_thread($0, $1);
902 }, 0);
903 }, targetThread, q);
904 return 0;
905 } else {
906 return do_emscripten_dispatch_to_thread(targetThread, q);
907 }
908 }
909
llvm_memory_barrier()910 void llvm_memory_barrier() { emscripten_atomic_fence(); }
911
llvm_atomic_load_add_i32_p0i32(int * ptr,int delta)912 int llvm_atomic_load_add_i32_p0i32(int* ptr, int delta) {
913 return emscripten_atomic_add_u32(ptr, delta);
914 }
915
916 typedef struct main_args {
917 int argc;
918 char** argv;
919 } main_args;
920
921 extern int __call_main(int argc, char** argv);
922
__emscripten_thread_main(void * param)923 void* __emscripten_thread_main(void* param) {
924 emscripten_set_thread_name(pthread_self(),
925 "Application main thread"); // This is the main runtime thread for the application.
926 main_args* args = (main_args*)param;
927 return (void*)__call_main(args->argc, args->argv);
928 }
929
930 static main_args _main_arguments;
931
proxy_main(int argc,char ** argv)932 int proxy_main(int argc, char** argv) {
933 if (emscripten_has_threading_support()) {
934 pthread_attr_t attr;
935 pthread_attr_init(&attr);
936 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
937 // Use TOTAL_STACK for the stack size, which is the normal size of the stack
938 // that main() would have without PROXY_TO_PTHREAD.
939 pthread_attr_setstacksize(&attr, EM_ASM_INT({ return TOTAL_STACK }));
940 // Pass special ID -1 to the list of transferred canvases to denote that the thread creation
941 // should instead take a list of canvases that are specified from the command line with
942 // -s OFFSCREENCANVASES_TO_PTHREAD linker flag.
943 emscripten_pthread_attr_settransferredcanvases(&attr, (const char*)-1);
944 _main_arguments.argc = argc;
945 _main_arguments.argv = argv;
946 pthread_t thread;
947 int rc = pthread_create(&thread, &attr, __emscripten_thread_main, (void*)&_main_arguments);
948 pthread_attr_destroy(&attr);
949 if (rc) {
950 // Proceed by running main() on the main browser thread as a fallback.
951 return __call_main(_main_arguments.argc, _main_arguments.argv);
952 }
953 return 0;
954 } else {
955 return __call_main(_main_arguments.argc, _main_arguments.argv);
956 }
957 }
958
959 weak_alias(__pthread_testcancel, pthread_testcancel);
960