]>
Commit | Line | Data |
---|---|---|
320054e8 DG |
1 | #define _GNU_SOURCE |
2 | #include "pthread_impl.h" | |
3 | #include "stdio_impl.h" | |
4 | #include "libc.h" | |
5 | #include "lock.h" | |
241060c3 | 6 | #ifdef __wasilibc_unmodified_upstream |
320054e8 | 7 | #include <sys/mman.h> |
241060c3 | 8 | #endif |
320054e8 DG |
9 | #include <string.h> |
10 | #include <stddef.h> | |
241060c3 AB |
11 | #ifndef __wasilibc_unmodified_upstream |
12 | #include <stdatomic.h> | |
13 | #endif | |
320054e8 | 14 | |
a00bf321 | 15 | #include <stdalign.h> |
16 | ||
320054e8 DG |
17 | static void dummy_0() |
18 | { | |
19 | } | |
20 | weak_alias(dummy_0, __acquire_ptc); | |
21 | weak_alias(dummy_0, __release_ptc); | |
22 | weak_alias(dummy_0, __pthread_tsd_run_dtors); | |
23 | weak_alias(dummy_0, __do_orphaned_stdio_locks); | |
241060c3 | 24 | #ifdef __wasilibc_unmodified_upstream |
320054e8 | 25 | weak_alias(dummy_0, __dl_thread_cleanup); |
f41256b6 | 26 | weak_alias(dummy_0, __membarrier_init); |
241060c3 | 27 | #endif |
320054e8 | 28 | |
f41256b6 DG |
29 | static int tl_lock_count; |
30 | static int tl_lock_waiters; | |
31 | ||
32 | void __tl_lock(void) | |
320054e8 | 33 | { |
f41256b6 DG |
34 | int tid = __pthread_self()->tid; |
35 | int val = __thread_list_lock; | |
36 | if (val == tid) { | |
37 | tl_lock_count++; | |
38 | return; | |
39 | } | |
40 | while ((val = a_cas(&__thread_list_lock, 0, tid))) | |
41 | __wait(&__thread_list_lock, &tl_lock_waiters, val, 0); | |
42 | } | |
43 | ||
44 | void __tl_unlock(void) | |
45 | { | |
46 | if (tl_lock_count) { | |
47 | tl_lock_count--; | |
48 | return; | |
49 | } | |
50 | a_store(&__thread_list_lock, 0); | |
51 | if (tl_lock_waiters) __wake(&__thread_list_lock, 1, 0); | |
52 | } | |
53 | ||
54 | void __tl_sync(pthread_t td) | |
55 | { | |
56 | a_barrier(); | |
57 | int val = __thread_list_lock; | |
58 | if (!val) return; | |
59 | __wait(&__thread_list_lock, &tl_lock_waiters, val, 0); | |
60 | if (tl_lock_waiters) __wake(&__thread_list_lock, 1, 0); | |
320054e8 | 61 | } |
320054e8 DG |
62 | |
63 | _Noreturn void __pthread_exit(void *result) | |
64 | { | |
65 | pthread_t self = __pthread_self(); | |
66 | sigset_t set; | |
67 | ||
68 | self->canceldisable = 1; | |
69 | self->cancelasync = 0; | |
70 | self->result = result; | |
71 | ||
72 | while (self->cancelbuf) { | |
73 | void (*f)(void *) = self->cancelbuf->__f; | |
74 | void *x = self->cancelbuf->__x; | |
75 | self->cancelbuf = self->cancelbuf->__next; | |
76 | f(x); | |
77 | } | |
78 | ||
79 | __pthread_tsd_run_dtors(); | |
80 | ||
241060c3 | 81 | #ifdef __wasilibc_unmodified_upstream |
322bd4ff | 82 | __block_app_sigs(&set); |
241060c3 | 83 | #endif |
322bd4ff DG |
84 | |
85 | /* This atomic potentially competes with a concurrent pthread_detach | |
86 | * call; the loser is responsible for freeing thread resources. */ | |
87 | int state = a_cas(&self->detach_state, DT_JOINABLE, DT_EXITING); | |
88 | ||
89 | if (state==DT_DETACHED && self->map_base) { | |
90 | /* Since __unmapself bypasses the normal munmap code path, | |
91 | * explicitly wait for vmlock holders first. This must be | |
92 | * done before any locks are taken, to avoid lock ordering | |
93 | * issues that could lead to deadlock. */ | |
241060c3 | 94 | #ifdef __wasilibc_unmodified_upstream |
322bd4ff | 95 | __vm_wait(); |
241060c3 | 96 | #endif |
322bd4ff DG |
97 | } |
98 | ||
320054e8 DG |
99 | /* Access to target the exiting thread with syscalls that use |
100 | * its kernel tid is controlled by killlock. For detached threads, | |
101 | * any use past this point would have undefined behavior, but for | |
58795582 DG |
102 | * joinable threads it's a valid usage that must be handled. |
103 | * Signals must be blocked since pthread_kill must be AS-safe. */ | |
320054e8 DG |
104 | LOCK(self->killlock); |
105 | ||
58795582 DG |
106 | /* The thread list lock must be AS-safe, and thus depends on |
107 | * application signals being blocked above. */ | |
f41256b6 DG |
108 | __tl_lock(); |
109 | ||
110 | /* If this is the only thread in the list, don't proceed with | |
111 | * termination of the thread, but restore the previous lock and | |
112 | * signal state to prepare for exit to call atexit handlers. */ | |
113 | if (self->next == self) { | |
114 | __tl_unlock(); | |
f41256b6 | 115 | UNLOCK(self->killlock); |
322bd4ff | 116 | self->detach_state = state; |
241060c3 | 117 | #ifdef __wasilibc_unmodified_upstream |
58795582 | 118 | __restore_sigs(&set); |
241060c3 | 119 | #endif |
320054e8 DG |
120 | exit(0); |
121 | } | |
122 | ||
58795582 | 123 | /* At this point we are committed to thread termination. */ |
f41256b6 | 124 | |
241060c3 | 125 | #ifdef __wasilibc_unmodified_upstream |
320054e8 DG |
126 | /* Process robust list in userspace to handle non-pshared mutexes |
127 | * and the detached thread case where the robust list head will | |
128 | * be invalid when the kernel would process it. */ | |
129 | __vm_lock(); | |
241060c3 | 130 | #endif |
320054e8 DG |
131 | volatile void *volatile *rp; |
132 | while ((rp=self->robust_list.head) && rp != &self->robust_list.head) { | |
133 | pthread_mutex_t *m = (void *)((char *)rp | |
134 | - offsetof(pthread_mutex_t, _m_next)); | |
135 | int waiters = m->_m_waiters; | |
136 | int priv = (m->_m_type & 128) ^ 128; | |
137 | self->robust_list.pending = rp; | |
138 | self->robust_list.head = *rp; | |
139 | int cont = a_swap(&m->_m_lock, 0x40000000); | |
140 | self->robust_list.pending = 0; | |
141 | if (cont < 0 || waiters) | |
142 | __wake(&m->_m_lock, 1, priv); | |
143 | } | |
241060c3 | 144 | #ifdef __wasilibc_unmodified_upstream |
320054e8 | 145 | __vm_unlock(); |
241060c3 | 146 | #endif |
320054e8 DG |
147 | |
148 | __do_orphaned_stdio_locks(); | |
241060c3 | 149 | #ifdef __wasilibc_unmodified_upstream |
320054e8 | 150 | __dl_thread_cleanup(); |
241060c3 | 151 | #endif |
320054e8 | 152 | |
58795582 DG |
153 | /* Last, unlink thread from the list. This change will not be visible |
154 | * until the lock is released, which only happens after SYS_exit | |
155 | * has been called, via the exit futex address pointing at the lock. | |
156 | * This needs to happen after any possible calls to LOCK() that might | |
157 | * skip locking if process appears single-threaded. */ | |
158 | if (!--libc.threads_minus_1) libc.need_locks = -1; | |
159 | self->next->prev = self->prev; | |
160 | self->prev->next = self->next; | |
161 | self->prev = self->next = self; | |
162 | ||
a00bf321 | 163 | #ifndef __wasilibc_unmodified_upstream |
164 | /* On Linux, the thread is created with CLONE_CHILD_CLEARTID, | |
165 | * and this lock will unlock by kernel when this thread terminates. | |
166 | * So we should unlock it here in WebAssembly. | |
167 | * See also set_tid_address(2) */ | |
168 | __tl_unlock(); | |
169 | #endif | |
170 | ||
241060c3 | 171 | #ifdef __wasilibc_unmodified_upstream |
f41256b6 DG |
172 | if (state==DT_DETACHED && self->map_base) { |
173 | /* Detached threads must block even implementation-internal | |
174 | * signals, since they will not have a stack in their last | |
175 | * moments of existence. */ | |
176 | __block_all_sigs(&set); | |
320054e8 DG |
177 | |
178 | /* Robust list will no longer be valid, and was already | |
179 | * processed above, so unregister it with the kernel. */ | |
180 | if (self->robust_list.off) | |
181 | __syscall(SYS_set_robust_list, 0, 3*sizeof(long)); | |
182 | ||
320054e8 DG |
183 | /* The following call unmaps the thread's stack mapping |
184 | * and then exits without touching the stack. */ | |
185 | __unmapself(self->map_base, self->map_size); | |
186 | } | |
a00bf321 | 187 | #else |
188 | if (state==DT_DETACHED && self->map_base) { | |
189 | // __syscall(SYS_exit) would unlock the thread, list | |
190 | // do it manually here | |
191 | __tl_unlock(); | |
192 | free(self->map_base); | |
193 | // Can't use `exit()` here, because it is too high level | |
194 | for (;;) __wasi_proc_exit(0); | |
195 | } | |
241060c3 | 196 | #endif |
320054e8 | 197 | |
f41256b6 | 198 | /* Wake any joiner. */ |
322bd4ff | 199 | a_store(&self->detach_state, DT_EXITED); |
f41256b6 DG |
200 | __wake(&self->detach_state, 1, 1); |
201 | ||
320054e8 DG |
202 | /* After the kernel thread exits, its tid may be reused. Clear it |
203 | * to prevent inadvertent use and inform functions that would use | |
204 | * it that it's no longer available. */ | |
205 | self->tid = 0; | |
206 | UNLOCK(self->killlock); | |
207 | ||
241060c3 | 208 | #ifdef __wasilibc_unmodified_upstream |
320054e8 | 209 | for (;;) __syscall(SYS_exit, 0); |
241060c3 | 210 | #else |
a00bf321 | 211 | // __syscall(SYS_exit) would unlock the thread, list |
212 | // do it manually here | |
213 | __tl_unlock(); | |
214 | // Can't use `exit()` here, because it is too high level | |
215 | for (;;) __wasi_proc_exit(0); | |
241060c3 | 216 | #endif |
320054e8 DG |
217 | } |
218 | ||
219 | void __do_cleanup_push(struct __ptcb *cb) | |
220 | { | |
221 | struct pthread *self = __pthread_self(); | |
222 | cb->__next = self->cancelbuf; | |
223 | self->cancelbuf = cb; | |
224 | } | |
225 | ||
226 | void __do_cleanup_pop(struct __ptcb *cb) | |
227 | { | |
228 | __pthread_self()->cancelbuf = cb->__next; | |
229 | } | |
230 | ||
f41256b6 | 231 | struct start_args { |
241060c3 | 232 | #ifdef __wasilibc_unmodified_upstream |
f41256b6 DG |
233 | void *(*start_func)(void *); |
234 | void *start_arg; | |
79a9b408 | 235 | volatile int control; |
f41256b6 | 236 | unsigned long sig_mask[_NSIG/8/sizeof(long)]; |
241060c3 AB |
237 | #else |
238 | void *(*start_func)(void *); | |
239 | void *start_arg; | |
a00bf321 | 240 | void *tls_base; |
241060c3 | 241 | #endif |
f41256b6 DG |
242 | }; |
243 | ||
241060c3 | 244 | #ifdef __wasilibc_unmodified_upstream |
320054e8 DG |
245 | static int start(void *p) |
246 | { | |
f41256b6 | 247 | struct start_args *args = p; |
79a9b408 DG |
248 | int state = args->control; |
249 | if (state) { | |
250 | if (a_cas(&args->control, 1, 2)==1) | |
251 | __wait(&args->control, 0, 2, 1); | |
252 | if (args->control) { | |
241060c3 | 253 | #ifdef __wasilibc_unmodified_upstream |
79a9b408 DG |
254 | __syscall(SYS_set_tid_address, &args->control); |
255 | for (;;) __syscall(SYS_exit, 0); | |
241060c3 | 256 | #endif |
f41256b6 DG |
257 | } |
258 | } | |
241060c3 | 259 | #ifdef __wasilibc_unmodified_upstream |
f41256b6 | 260 | __syscall(SYS_rt_sigprocmask, SIG_SETMASK, &args->sig_mask, 0, _NSIG/8); |
241060c3 | 261 | #endif |
f41256b6 | 262 | __pthread_exit(args->start_func(args->start_arg)); |
320054e8 DG |
263 | return 0; |
264 | } | |
265 | ||
266 | static int start_c11(void *p) | |
267 | { | |
f41256b6 DG |
268 | struct start_args *args = p; |
269 | int (*start)(void*) = (int(*)(void*)) args->start_func; | |
270 | __pthread_exit((void *)(uintptr_t)start(args->start_arg)); | |
320054e8 DG |
271 | return 0; |
272 | } | |
241060c3 AB |
273 | #else |
274 | __attribute__((export_name("wasi_thread_start"))) | |
a00bf321 | 275 | _Noreturn void wasi_thread_start(int tid, void *p) |
241060c3 AB |
276 | { |
277 | struct start_args *args = p; | |
a00bf321 | 278 | __asm__(".globaltype __tls_base, i32\n" |
279 | "local.get %0\n" | |
280 | "global.set __tls_base\n" | |
281 | :: "r"(args->tls_base)); | |
282 | pthread_t self = __pthread_self(); | |
241060c3 AB |
283 | // Set the thread ID (TID) on the pthread structure. The TID is stored |
284 | // atomically since it is also stored by the parent thread; this way, | |
285 | // whichever thread (parent or child) reaches this point first can proceed | |
286 | // without waiting. | |
a00bf321 | 287 | atomic_store((atomic_int *) &(self->tid), tid); |
288 | // Set the stack pointer. | |
289 | __asm__(".globaltype __stack_pointer, i32\n" | |
290 | "local.get %0\n" | |
291 | "global.set __stack_pointer\n" | |
292 | :: "r"(self->stack)); | |
241060c3 | 293 | // Execute the user's start function. |
3209fc11 | 294 | __pthread_exit(args->start_func(args->start_arg)); |
241060c3 AB |
295 | } |
296 | #endif | |
320054e8 DG |
297 | |
298 | #define ROUND(x) (((x)+PAGE_SIZE-1)&-PAGE_SIZE) | |
299 | ||
300 | /* pthread_key_create.c overrides this */ | |
301 | static volatile size_t dummy = 0; | |
302 | weak_alias(dummy, __pthread_tsd_size); | |
303 | static void *dummy_tsd[1] = { 0 }; | |
304 | weak_alias(dummy_tsd, __pthread_tsd_main); | |
305 | ||
320054e8 DG |
306 | static FILE *volatile dummy_file = 0; |
307 | weak_alias(dummy_file, __stdin_used); | |
308 | weak_alias(dummy_file, __stdout_used); | |
309 | weak_alias(dummy_file, __stderr_used); | |
310 | ||
311 | static void init_file_lock(FILE *f) | |
312 | { | |
313 | if (f && f->lock<0) f->lock = 0; | |
314 | } | |
315 | ||
316 | int __pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attrp, void *(*entry)(void *), void *restrict arg) | |
317 | { | |
318 | int ret, c11 = (attrp == __ATTRP_C11_THREAD); | |
319 | size_t size, guard; | |
320 | struct pthread *self, *new; | |
321 | unsigned char *map = 0, *stack = 0, *tsd = 0, *stack_limit; | |
241060c3 | 322 | #ifdef __wasilibc_unmodified_upstream |
320054e8 DG |
323 | unsigned flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
324 | | CLONE_THREAD | CLONE_SYSVSEM | CLONE_SETTLS | |
325 | | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID | CLONE_DETACHED; | |
241060c3 | 326 | #endif |
320054e8 | 327 | pthread_attr_t attr = { 0 }; |
f41256b6 | 328 | sigset_t set; |
a00bf321 | 329 | #ifndef __wasilibc_unmodified_upstream |
330 | size_t tls_size = __builtin_wasm_tls_size(); | |
331 | size_t tls_align = __builtin_wasm_tls_align(); | |
332 | void* tls_base = __builtin_wasm_tls_base(); | |
333 | void* new_tls_base; | |
334 | size_t tls_offset; | |
335 | tls_size += tls_align; | |
336 | #endif | |
320054e8 | 337 | |
a00bf321 | 338 | #ifdef __wasilibc_unmodified_upstream |
320054e8 | 339 | if (!libc.can_do_threads) return ENOSYS; |
a00bf321 | 340 | #endif |
320054e8 DG |
341 | self = __pthread_self(); |
342 | if (!libc.threaded) { | |
343 | for (FILE *f=*__ofl_lock(); f; f=f->next) | |
344 | init_file_lock(f); | |
345 | __ofl_unlock(); | |
346 | init_file_lock(__stdin_used); | |
347 | init_file_lock(__stdout_used); | |
348 | init_file_lock(__stderr_used); | |
241060c3 | 349 | #ifdef __wasilibc_unmodified_upstream |
320054e8 | 350 | __syscall(SYS_rt_sigprocmask, SIG_UNBLOCK, SIGPT_SET, 0, _NSIG/8); |
241060c3 | 351 | #endif |
320054e8 | 352 | self->tsd = (void **)__pthread_tsd_main; |
241060c3 | 353 | #ifdef __wasilibc_unmodified_upstream |
f41256b6 | 354 | __membarrier_init(); |
241060c3 | 355 | #endif |
320054e8 DG |
356 | libc.threaded = 1; |
357 | } | |
358 | if (attrp && !c11) attr = *attrp; | |
359 | ||
360 | __acquire_ptc(); | |
361 | if (!attrp || c11) { | |
362 | attr._a_stacksize = __default_stacksize; | |
363 | attr._a_guardsize = __default_guardsize; | |
364 | } | |
365 | ||
320054e8 | 366 | if (attr._a_stackaddr) { |
a00bf321 | 367 | #ifdef __wasilibc_unmodified_upstream |
320054e8 | 368 | size_t need = libc.tls_size + __pthread_tsd_size; |
a00bf321 | 369 | #else |
370 | size_t need = tls_size + __pthread_tsd_size; | |
371 | #endif | |
320054e8 DG |
372 | size = attr._a_stacksize; |
373 | stack = (void *)(attr._a_stackaddr & -16); | |
374 | stack_limit = (void *)(attr._a_stackaddr - size); | |
375 | /* Use application-provided stack for TLS only when | |
376 | * it does not take more than ~12% or 2k of the | |
377 | * application's stack space. */ | |
378 | if (need < size/8 && need < 2048) { | |
379 | tsd = stack - __pthread_tsd_size; | |
a00bf321 | 380 | #ifdef __wasilibc_unmodified_upstream |
320054e8 | 381 | stack = tsd - libc.tls_size; |
a00bf321 | 382 | #else |
383 | stack = tsd - tls_size; | |
384 | #endif | |
320054e8 DG |
385 | memset(stack, 0, need); |
386 | } else { | |
387 | size = ROUND(need); | |
388 | } | |
389 | guard = 0; | |
390 | } else { | |
391 | guard = ROUND(attr._a_guardsize); | |
392 | size = guard + ROUND(attr._a_stacksize | |
a00bf321 | 393 | #ifdef __wasilibc_unmodified_upstream |
320054e8 | 394 | + libc.tls_size + __pthread_tsd_size); |
a00bf321 | 395 | #else |
396 | + tls_size + __pthread_tsd_size); | |
397 | #endif | |
320054e8 DG |
398 | } |
399 | ||
400 | if (!tsd) { | |
241060c3 | 401 | #ifdef __wasilibc_unmodified_upstream |
320054e8 DG |
402 | if (guard) { |
403 | map = __mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANON, -1, 0); | |
404 | if (map == MAP_FAILED) goto fail; | |
405 | if (__mprotect(map+guard, size-guard, PROT_READ|PROT_WRITE) | |
406 | && errno != ENOSYS) { | |
407 | __munmap(map, size); | |
408 | goto fail; | |
409 | } | |
410 | } else { | |
411 | map = __mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); | |
412 | if (map == MAP_FAILED) goto fail; | |
413 | } | |
241060c3 AB |
414 | #else |
415 | map = malloc(size); | |
416 | if (!map) goto fail; | |
417 | #endif | |
320054e8 DG |
418 | tsd = map + size - __pthread_tsd_size; |
419 | if (!stack) { | |
a00bf321 | 420 | #ifdef __wasilibc_unmodified_upstream |
320054e8 | 421 | stack = tsd - libc.tls_size; |
a00bf321 | 422 | #else |
423 | stack = tsd - tls_size; | |
424 | #endif | |
320054e8 DG |
425 | stack_limit = map + guard; |
426 | } | |
427 | } | |
428 | ||
a00bf321 | 429 | #ifdef __wasilibc_unmodified_upstream |
320054e8 | 430 | new = __copy_tls(tsd - libc.tls_size); |
a00bf321 | 431 | #else |
432 | new_tls_base = __copy_tls(tsd - tls_size); | |
433 | tls_offset = new_tls_base - tls_base; | |
434 | new = (void*)((uintptr_t)self + tls_offset); | |
435 | #endif | |
320054e8 DG |
436 | new->map_base = map; |
437 | new->map_size = size; | |
438 | new->stack = stack; | |
439 | new->stack_size = stack - stack_limit; | |
440 | new->guard_size = guard; | |
320054e8 DG |
441 | new->self = new; |
442 | new->tsd = (void *)tsd; | |
443 | new->locale = &libc.global_locale; | |
444 | if (attr._a_detach) { | |
445 | new->detach_state = DT_DETACHED; | |
320054e8 DG |
446 | } else { |
447 | new->detach_state = DT_JOINABLE; | |
448 | } | |
320054e8 | 449 | new->robust_list.head = &new->robust_list.head; |
322bd4ff | 450 | new->canary = self->canary; |
d4db3fa2 | 451 | new->sysinfo = self->sysinfo; |
320054e8 | 452 | |
f41256b6 DG |
453 | /* Setup argument structure for the new thread on its stack. |
454 | * It's safe to access from the caller only until the thread | |
455 | * list is unlocked. */ | |
a00bf321 | 456 | #ifdef __wasilibc_unmodified_upstream |
f41256b6 DG |
457 | stack -= (uintptr_t)stack % sizeof(uintptr_t); |
458 | stack -= sizeof(struct start_args); | |
459 | struct start_args *args = (void *)stack; | |
460 | args->start_func = entry; | |
461 | args->start_arg = arg; | |
79a9b408 | 462 | args->control = attr._a_sched ? 1 : 0; |
320054e8 | 463 | |
f41256b6 DG |
464 | /* Application signals (but not the synccall signal) must be |
465 | * blocked before the thread list lock can be taken, to ensure | |
466 | * that the lock is AS-safe. */ | |
467 | __block_app_sigs(&set); | |
468 | ||
469 | /* Ensure SIGCANCEL is unblocked in new thread. This requires | |
470 | * working with a copy of the set so we can restore the | |
471 | * original mask in the calling thread. */ | |
472 | memcpy(&args->sig_mask, &set, sizeof args->sig_mask); | |
473 | args->sig_mask[(SIGCANCEL-1)/8/sizeof(long)] &= | |
474 | ~(1UL<<((SIGCANCEL-1)%(8*sizeof(long)))); | |
241060c3 | 475 | #else |
a00bf321 | 476 | /* Align the stack to struct start_args */ |
477 | stack -= sizeof(struct start_args); | |
478 | stack -= (uintptr_t)stack % alignof(struct start_args); | |
479 | struct start_args *args = (void *)stack; | |
480 | ||
481 | /* Align the stack to 16 and store it */ | |
482 | new->stack = (void *)((uintptr_t) stack & -16); | |
483 | /* Correct the stack size */ | |
484 | new->stack_size = stack - stack_limit; | |
485 | ||
486 | args->start_func = entry; | |
487 | args->start_arg = arg; | |
488 | args->tls_base = (void*)new_tls_base; | |
241060c3 | 489 | #endif |
f41256b6 DG |
490 | |
491 | __tl_lock(); | |
58795582 | 492 | if (!libc.threads_minus_1++) libc.need_locks = 1; |
241060c3 | 493 | #ifdef __wasilibc_unmodified_upstream |
f41256b6 | 494 | ret = __clone((c11 ? start_c11 : start), stack, flags, args, &new->tid, TP_ADJ(new), &__thread_list_lock); |
241060c3 AB |
495 | #else |
496 | /* Instead of `__clone`, WASI uses a host API to instantiate a new version | |
497 | * of the current module and start executing the entry function. The | |
498 | * wasi-threads specification requires the module to export a | |
499 | * `wasi_thread_start` function, which is invoked with `args`. */ | |
500 | ret = __wasi_thread_spawn((void *) args); | |
501 | #endif | |
502 | ||
503 | #ifdef __wasilibc_unmodified_upstream | |
79a9b408 DG |
504 | /* All clone failures translate to EAGAIN. If explicit scheduling |
505 | * was requested, attempt it before unlocking the thread list so | |
506 | * that the failed thread is never exposed and so that we can | |
507 | * clean up all transient resource usage before returning. */ | |
508 | if (ret < 0) { | |
509 | ret = -EAGAIN; | |
510 | } else if (attr._a_sched) { | |
511 | ret = __syscall(SYS_sched_setscheduler, | |
512 | new->tid, attr._a_policy, &attr._a_prio); | |
513 | if (a_swap(&args->control, ret ? 3 : 0)==2) | |
514 | __wake(&args->control, 1, 1); | |
515 | if (ret) | |
516 | __wait(&args->control, 0, 3, 0); | |
517 | } | |
241060c3 AB |
518 | #else |
519 | /* `wasi_thread_spawn` will either return a host-provided thread ID (TID) | |
520 | * (`>= 0`) or an error code (`< 0`). As in the unmodified version, all | |
521 | * spawn failures translate to EAGAIN; unlike the modified version, there is | |
522 | * no need to "start up" the child thread--the host does this. If the spawn | |
523 | * did succeed, then we store the TID atomically, since this parent thread | |
524 | * is racing with the child thread to set this field; this way, whichever | |
525 | * thread reaches this point first can continue without waiting. */ | |
526 | if (ret < 0) { | |
527 | ret = -EAGAIN; | |
528 | } else { | |
a00bf321 | 529 | atomic_store((atomic_int *) &(new->tid), ret); |
241060c3 AB |
530 | } |
531 | #endif | |
79a9b408 | 532 | |
f41256b6 DG |
533 | if (ret >= 0) { |
534 | new->next = self->next; | |
535 | new->prev = self; | |
536 | new->next->prev = new; | |
537 | new->prev->next = new; | |
79a9b408 | 538 | } else { |
58795582 | 539 | if (!--libc.threads_minus_1) libc.need_locks = 0; |
320054e8 | 540 | } |
f41256b6 | 541 | __tl_unlock(); |
241060c3 | 542 | #ifdef __wasilibc_unmodified_upstream |
f41256b6 | 543 | __restore_sigs(&set); |
241060c3 | 544 | #endif |
f41256b6 | 545 | __release_ptc(); |
320054e8 DG |
546 | |
547 | if (ret < 0) { | |
241060c3 | 548 | #ifdef __wasilibc_unmodified_upstream |
320054e8 | 549 | if (map) __munmap(map, size); |
241060c3 AB |
550 | #else |
551 | free(map); | |
552 | #endif | |
79a9b408 | 553 | return -ret; |
320054e8 DG |
554 | } |
555 | ||
556 | *res = new; | |
557 | return 0; | |
558 | fail: | |
559 | __release_ptc(); | |
560 | return EAGAIN; | |
561 | } | |
562 | ||
563 | weak_alias(__pthread_exit, pthread_exit); | |
564 | weak_alias(__pthread_create, pthread_create); |