Skip to content

Commit aecd368

Browse files
authored
Fix races around pthread exit and join (#409)
1 parent a6f8713 commit aecd368

File tree

3 files changed

+26
-14
lines changed

3 files changed

+26
-14
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ ifeq ($(THREAD_MODEL), posix)
325325
# Specify the tls-model until LLVM 15 is released (which should contain
326326
# https://reviews.llvm.org/D130053).
327327
CFLAGS += -mthread-model posix -pthread -ftls-model=local-exec
328+
ASMFLAGS += -matomics
328329

329330
# Include cloudlib's directory to access the structure definition of clockid_t
330331
CFLAGS += -I$(LIBC_BOTTOM_HALF_CLOUDLIBC_SRC)

libc-top-half/musl/src/thread/pthread_create.c

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -164,14 +164,6 @@ static void __pthread_exit(void *result)
164164
self->prev->next = self->next;
165165
self->prev = self->next = self;
166166

167-
#ifndef __wasilibc_unmodified_upstream
168-
/* On Linux, the thread is created with CLONE_CHILD_CLEARTID,
169-
* and this lock will unlock by kernel when this thread terminates.
170-
* So we should unlock it here in WebAssembly.
171-
* See also set_tid_address(2) */
172-
__tl_unlock();
173-
#endif
174-
175167
#ifdef __wasilibc_unmodified_upstream
176168
if (state==DT_DETACHED && self->map_base) {
177169
/* Detached threads must block even implementation-internal
@@ -190,9 +182,6 @@ static void __pthread_exit(void *result)
190182
}
191183
#else
192184
if (state==DT_DETACHED && self->map_base) {
193-
// __syscall(SYS_exit) would unlock the thread, list
194-
// do it manually here
195-
__tl_unlock();
196185
free(self->map_base);
197186
// Can't use `exit()` here, because it is too high level
198187
return;
@@ -212,10 +201,15 @@ static void __pthread_exit(void *result)
212201
#ifdef __wasilibc_unmodified_upstream
213202
for (;;) __syscall(SYS_exit, 0);
214203
#else
215-
// __syscall(SYS_exit) would unlock the thread, list
216-
// do it manually here
217-
__tl_unlock();
218204
// Can't use `exit()` here, because it is too high level
205+
206+
/* On Linux, the thread is created with CLONE_CHILD_CLEARTID,
207+
* and the lock (__thread_list_lock) will be unlocked by kernel when
208+
* this thread terminates.
209+
* See also set_tid_address(2)
210+
*
211+
* In WebAssembly, we leave it to wasi_thread_start instead.
212+
*/
219213
#endif
220214
}
221215

libc-top-half/musl/src/thread/wasm32/wasi_thread_start.s

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,21 @@ wasi_thread_start:
2828
local.get 1 # start_arg
2929
call __wasi_thread_start_C
3030

31+
# Unlock thread list. (as CLONE_CHILD_CLEARTID would do for Linux)
32+
#
33+
# Note: once we unlock the thread list, our "map_base" can be freed
34+
# by a joining thread. It's safe as we are in ASM and no longer use
35+
# our C stack or pthread_t. It's impossible to do this safely in C
36+
# because there is no way to tell the C compiler not to use C stack.
37+
i32.const __thread_list_lock
38+
i32.const 0
39+
i32.atomic.store 0
40+
# As an optimization, we can check tl_lock_waiters here.
41+
# But for now, simply wake up unconditionally as
42+
# CLONE_CHILD_CLEARTID does.
43+
i32.const __thread_list_lock
44+
i32.const 1
45+
memory.atomic.notify 0
46+
drop
47+
3148
end_function

0 commit comments

Comments
 (0)