diff --git a/CMakeLists.txt b/CMakeLists.txt index 12d59c4fc..ce29792e9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,6 +88,9 @@ endif() if(TARGET_TRIPLE MATCHES "-threads$") set(THREADS ON) add_compile_options(-mthread-model posix -pthread -ftls-model=local-exec -matomics) +elseif(TARGET_TRIPLE MATCHES "-wasip3$") + set(THREADS OFF) + add_compile_options(-mthread-model posix -pthread -ftls-model=local-exec) else() set(THREADS OFF) add_compile_options(-mthread-model single) diff --git a/expected/wasm32-wasip1-threads/defined-symbols.txt b/expected/wasm32-wasip1-threads/defined-symbols.txt index 4fb5d53fc..4eb71cb2a 100644 --- a/expected/wasm32-wasip1-threads/defined-symbols.txt +++ b/expected/wasm32-wasip1-threads/defined-symbols.txt @@ -15,8 +15,6 @@ __acquire_ptc __aio_close __asctime_r __assert_fail -__at_quick_exit_lockptr -__atexit_lockptr __c_dot_utf8 __c_dot_utf8_locale __c_locale @@ -81,6 +79,7 @@ __ftello __ftello_unlocked __funcs_on_exit __funcs_on_quick_exit +__futexwait __futimesat __fwritable __fwritex @@ -92,6 +91,7 @@ __getopt_msg __gmtime_r __hwcap __inet_aton +__inhibit_ptc __init_ssp __init_tp __intscan @@ -148,7 +148,6 @@ __libc_free __libc_malloc __loc_is_allocated __locale_lock -__locale_lockptr __localtime_r __lock __lockfile @@ -219,7 +218,6 @@ __pthread_tsd_size __putenv __qsort_r __rand48_step -__random_lockptr __reallocarray __register_locked_file __release_ptc @@ -248,7 +246,6 @@ __stdin_used __stdio_close __stdio_exit __stdio_exit_needed -__stdio_ofl_lockptr __stdio_read __stdio_seek __stdio_write @@ -308,6 +305,7 @@ __unlockfile __uselocale __utc __wait +__wake __wasi_args_get __wasi_args_sizes_get __wasi_clock_res_get @@ -359,7 +357,6 @@ __wasi_thread_start_C __wasilibc_access __wasilibc_cwd __wasilibc_cwd_lock -__wasilibc_cwd_unlock __wasilibc_deinitialize_environ __wasilibc_dttoif __wasilibc_enable_futex_busywait_on_current_thread diff --git a/libc-bottom-half/CMakeLists.txt b/libc-bottom-half/CMakeLists.txt index a188128ae..68d926a16 100644 --- a/libc-bottom-half/CMakeLists.txt +++ b/libc-bottom-half/CMakeLists.txt @@ -173,6 +173,7 @@ endif() if (WASI STREQUAL "p3") list(APPEND bottom_half_sources + sources/__wasm_init_task.s sources/wasip3.c sources/wasip3_block_on.c sources/wasip3_stdio.c diff --git a/libc-bottom-half/cloudlibc/src/libc/sched/sched_yield.c b/libc-bottom-half/cloudlibc/src/libc/sched/sched_yield.c index fc13322b6..92a7fc3f4 100644 --- a/libc-bottom-half/cloudlibc/src/libc/sched/sched_yield.c +++ b/libc-bottom-half/cloudlibc/src/libc/sched/sched_yield.c @@ -7,7 +7,15 @@ #include int sched_yield(void) { -#ifdef __wasip1__ +#ifdef __wasm_libcall_thread_context__ + #ifdef __wasip3__ + wasip3_thread_yield(); + return 0; + #else + #error "Unknown WASI version" + #endif + +#elif defined(__wasip1__) __wasi_errno_t error = __wasi_sched_yield(); if (error != 0) { errno = error; diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/pwrite.c b/libc-bottom-half/cloudlibc/src/libc/unistd/pwrite.c index 3666ae256..060f1fb98 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/pwrite.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/pwrite.c @@ -83,7 +83,7 @@ ssize_t pwrite(int fildes, const void *buf, size_t nbyte, off_t offset) { &closed); filesystem_stream_u8_drop_writable(writer); - // Wait for the subtask to resolve now that the writer half is closed and if + // Wait for the future to resolve now that the writer half is closed and if // we failed to write bytes (0 bytes written) and the result is an error we // can return -1. filesystem_result_void_error_code_t result; diff --git a/libc-bottom-half/crt/crt1-command.c b/libc-bottom-half/crt/crt1-command.c index 8117973b7..a4f7dd6ee 100644 --- a/libc-bottom-half/crt/crt1-command.c +++ b/libc-bottom-half/crt/crt1-command.c @@ -9,6 +9,14 @@ extern void __wasm_call_ctors(void); extern int __main_void(void); extern void __wasm_call_dtors(void); +#ifdef __wasm_libcall_thread_context__ +// Force __wasm_init_task and __wasm_init_async_task to be linked in for wasip3 +extern void __wasm_init_task(void); +extern void __wasm_init_async_task(void); +__attribute__((used)) void *__wasm_init_task_ref = __wasm_init_task; +__attribute__((used)) void *__wasm_init_async_task_ref = __wasm_init_async_task; +#endif + #if defined(__wasip1__) __attribute__((export_name("_start"))) void _start(void) #elif defined(__wasip2__) diff --git a/libc-bottom-half/crt/crt1-reactor.c b/libc-bottom-half/crt/crt1-reactor.c index bd693245e..f77fdf49f 100644 --- a/libc-bottom-half/crt/crt1-reactor.c +++ b/libc-bottom-half/crt/crt1-reactor.c @@ -4,6 +4,14 @@ extern void __wasi_init_tp(void); extern void __wasm_call_ctors(void); +#ifdef __wasm_libcall_thread_context__ +// Force __wasm_init_task and __wasm_init_async_task to be linked in for wasip3 +extern void __wasm_init_task(void); +extern void __wasm_init_async_task(void); +__attribute__((used)) static void *__wasm_init_task_ref = __wasm_init_task; +__attribute__((used)) static void *__wasm_init_async_task_ref = __wasm_init_async_task; +#endif + __attribute__((export_name("_initialize"))) void _initialize(void) { #if defined(_REENTRANT) static volatile atomic_int initialized = 0; diff --git a/libc-bottom-half/sources/__wasm_init_task.s b/libc-bottom-half/sources/__wasm_init_task.s new file mode 100644 index 000000000..83a63d097 --- /dev/null +++ b/libc-bottom-half/sources/__wasm_init_task.s @@ -0,0 +1,69 @@ + .text + + .export_name __wasm_init_task, __wasm_init_task + .export_name __wasm_init_async_task, __wasm_init_async_task + + .globaltype __init_stack_pointer, i32, immutable + .globaltype __init_tls_base, i32, immutable + .globaltype __tls_size, i32, immutable + .globaltype __tls_align, i32, immutable + + .functype __wasm_set_stack_pointer (i32) -> () + .functype __wasm_set_tls_base (i32) -> () + + .functype malloc (i32) -> (i32) + .functype __copy_tls (i32) -> (i32) + .functype __allocate_tls () -> (i32) + + .globl __wasm_init_task + .type __wasm_init_task,@function + .globl __wasm_init_async_task + .type __wasm_init_async_task,@function + + .functype __wasi_init_tp() -> () + +__wasm_init_task: + .functype __wasm_init_task () -> () + + global.get __init_stack_pointer + call __wasm_set_stack_pointer + + global.get __init_tls_base + call __wasm_set_tls_base + + # Allocate a new TLS area + call __allocate_tls + call __copy_tls + call __wasm_set_tls_base + + call __wasi_init_tp + + end_function + +__wasm_init_async_task: + .functype __wasm_init_async_task () -> () + + # malloc and __init_tls may use the stack pointer and TLS base, so set those first + # to the statically-allocated values used for synchronous tasks. + global.get __init_stack_pointer + call __wasm_set_stack_pointer + + global.get __init_tls_base + call __wasm_set_tls_base + + # Allocate a new stack + # Constant copied from __default_stacksize, TODO(wasip3) find a way + # to reference this constant without linking failing for shared libraries, + # i.e. a position-independent data reference. + i32.const 131072 + call malloc + call __wasm_set_stack_pointer + + # Allocate a new TLS area + call __allocate_tls + call __copy_tls + call __wasm_set_tls_base + + call __wasi_init_tp + + end_function \ No newline at end of file diff --git a/libc-bottom-half/sources/chdir.c b/libc-bottom-half/sources/chdir.c index d7383e7ed..6f88f2bc1 100644 --- a/libc-bottom-half/sources/chdir.c +++ b/libc-bottom-half/sources/chdir.c @@ -8,15 +8,9 @@ #include #include #include +#include "lock.h" -#ifdef _REENTRANT -void __wasilibc_cwd_lock(void); -void __wasilibc_cwd_unlock(void); -static volatile int lock[1]; -#else -#define __wasilibc_cwd_lock() (void)0 -#define __wasilibc_cwd_unlock() (void)0 -#endif +DECLARE_WEAK_LOCK(__wasilibc_cwd_lock, extern); extern char *__wasilibc_cwd; static int __wasilibc_cwd_mallocd = 0; @@ -66,10 +60,10 @@ int chdir(const char *path) { // And set our new malloc'd buffer into the global cwd, freeing the // previous one if necessary. - __wasilibc_cwd_lock(); + WEAK_LOCK(__wasilibc_cwd_lock); char *prev_cwd = __wasilibc_cwd; __wasilibc_cwd = new_cwd; - __wasilibc_cwd_unlock(); + WEAK_UNLOCK(__wasilibc_cwd_lock); if (__wasilibc_cwd_mallocd) free(prev_cwd); __wasilibc_cwd_mallocd = 1; @@ -100,7 +94,7 @@ static const char *make_absolute(const char *path) { // Otherwise we'll take the current directory, add a `/`, and then add the // input `path`. Note that this doesn't do any normalization (like removing // `/./`). - __wasilibc_cwd_lock(); + WEAK_LOCK(__wasilibc_cwd_lock); size_t cwd_len = strlen(__wasilibc_cwd); size_t path_len = path ? strlen(path) : 0; int need_slash = __wasilibc_cwd[cwd_len - 1] == '/' ? 0 : 1; @@ -108,14 +102,14 @@ static const char *make_absolute(const char *path) { if (alloc_len > make_absolute_len) { char *tmp = realloc(make_absolute_buf, alloc_len); if (tmp == NULL) { - __wasilibc_cwd_unlock(); + WEAK_UNLOCK(__wasilibc_cwd_lock); return NULL; } make_absolute_buf = tmp; make_absolute_len = alloc_len; } strcpy(make_absolute_buf, __wasilibc_cwd); - __wasilibc_cwd_unlock(); + WEAK_UNLOCK(__wasilibc_cwd_lock); #ifdef _REENTRANT if (path[0] == 0 || !strcmp(path, ".") || !strcmp(path, "./")) { @@ -135,12 +129,12 @@ static const char *make_absolute(const char *path) { int __wasilibc_find_relpath_alloc(const char *path, const char **abs_prefix, char **relative_buf, size_t *relative_buf_len, int can_realloc) { - LOCK(lock); + WEAK_LOCK(__wasilibc_cwd_lock); // First, make our path absolute taking the cwd into account. const char *abspath = make_absolute(path); if (abspath == NULL) { - UNLOCK(lock); + WEAK_UNLOCK(__wasilibc_cwd_lock); errno = ENOMEM; return -1; } @@ -151,20 +145,20 @@ int __wasilibc_find_relpath_alloc(const char *path, const char **abs_prefix, const char *rel; int fd = __wasilibc_find_abspath(abspath, abs_prefix, &rel); if (fd == -1) { - UNLOCK(lock); + WEAK_UNLOCK(__wasilibc_cwd_lock); return -1; } size_t rel_len = strlen(rel); if (*relative_buf_len < rel_len + 1) { if (!can_realloc) { - UNLOCK(lock); + WEAK_UNLOCK(__wasilibc_cwd_lock); errno = ERANGE; return -1; } char *tmp = realloc(*relative_buf, rel_len + 1); if (tmp == NULL) { - UNLOCK(lock); + WEAK_UNLOCK(__wasilibc_cwd_lock); errno = ENOMEM; return -1; } @@ -172,6 +166,6 @@ int __wasilibc_find_relpath_alloc(const char *path, const char **abs_prefix, *relative_buf_len = rel_len + 1; } strcpy(*relative_buf, rel); - UNLOCK(lock); + WEAK_UNLOCK(__wasilibc_cwd_lock); return fd; } diff --git a/libc-bottom-half/sources/getcwd.c b/libc-bottom-half/sources/getcwd.c index 8b0b0b746..b5622695c 100644 --- a/libc-bottom-half/sources/getcwd.c +++ b/libc-bottom-half/sources/getcwd.c @@ -5,34 +5,28 @@ #include "lock.h" char *__wasilibc_cwd = "/"; +DECLARE_WEAK_LOCK(__wasilibc_cwd_lock); -#ifdef _REENTRANT -static volatile int lock[1]; -void __wasilibc_cwd_lock(void) { LOCK(lock); } -void __wasilibc_cwd_unlock(void) { UNLOCK(lock); } -#else -#define __wasilibc_cwd_lock() (void)0 -#define __wasilibc_cwd_unlock() (void)0 -#endif char *getcwd(char *buf, size_t size) { - __wasilibc_cwd_lock(); + // Critical section contains no yield points, so we can use weak locks. + WEAK_LOCK(__wasilibc_cwd_lock); if (!buf) { buf = strdup(__wasilibc_cwd); if (!buf) { errno = ENOMEM; - __wasilibc_cwd_unlock(); + WEAK_UNLOCK(__wasilibc_cwd_lock); return NULL; } } else { size_t len = strlen(__wasilibc_cwd); if (size < len + 1) { errno = ERANGE; - __wasilibc_cwd_unlock(); + WEAK_UNLOCK(__wasilibc_cwd_lock); return NULL; } strcpy(buf, __wasilibc_cwd); } - __wasilibc_cwd_unlock(); + WEAK_UNLOCK(__wasilibc_cwd_lock); return buf; } diff --git a/libc-bottom-half/sources/preopens.c b/libc-bottom-half/sources/preopens.c index eaa76df31..59809e4c7 100644 --- a/libc-bottom-half/sources/preopens.c +++ b/libc-bottom-half/sources/preopens.c @@ -21,6 +21,11 @@ #include #endif +/// Access to the the above preopen must be protected in the presence of +/// threads. +DECLARE_STRONG_LOCK(lock, static); + + #if defined(__wasip1__) typedef struct { __wasi_fd_t fd; @@ -104,12 +109,6 @@ static preopen *preopens; static size_t num_preopens; static size_t preopen_capacity; -/// Access to the the above preopen must be protected in the presence of -/// threads. -#ifdef _REENTRANT -static volatile int lock[1]; -#endif - #ifdef NDEBUG #define assert_invariants() // assertions disabled #else @@ -228,11 +227,11 @@ static bool prefix_matches(const char *prefix, size_t prefix_len, /// This function takes ownership of `prefix`. static int internal_register_preopened_fd(__wasi_fd_t fd, const char *relprefix) { - LOCK(lock); + STRONG_LOCK(lock); int r = internal_register_preopened_fd_unlocked(fd, relprefix); - UNLOCK(lock); + STRONG_UNLOCK(lock); return r; } @@ -271,7 +270,7 @@ int __wasilibc_find_abspath(const char *path, const char **abs_prefix, // recently added preopens take precedence over less recently addded ones. size_t match_len = 0; int fd = -1; - LOCK(lock); + STRONG_LOCK(lock); for (size_t i = num_preopens; i > 0; --i) { const preopen *pre = &preopens[i - 1]; const char *prefix = pre->prefix; @@ -286,7 +285,7 @@ int __wasilibc_find_abspath(const char *path, const char **abs_prefix, *abs_prefix = prefix; } } - UNLOCK(lock); + STRONG_UNLOCK(lock); if (fd == -1) { errno = ENOENT; @@ -314,11 +313,11 @@ void __wasilibc_populate_preopens(void) { return; } - LOCK(lock); + STRONG_LOCK(lock); // Check whether another thread initialized the preopens already. if (preopens_populated) { - UNLOCK(lock); + STRONG_UNLOCK(lock); return; } @@ -389,7 +388,7 @@ void __wasilibc_populate_preopens(void) { // Preopens are now initialized. preopens_populated = true; - UNLOCK(lock); + STRONG_UNLOCK(lock); return; #ifdef __wasip1__ @@ -401,7 +400,7 @@ void __wasilibc_populate_preopens(void) { } void __wasilibc_reset_preopens(void) { - LOCK(lock); + STRONG_LOCK(lock); if (num_preopens) { for (unsigned i = 0; i < num_preopens; ++i) { @@ -422,5 +421,5 @@ void __wasilibc_reset_preopens(void) { descriptor_table_clear(); #endif - UNLOCK(lock); + STRONG_UNLOCK(lock); } diff --git a/libc-top-half/CMakeLists.txt b/libc-top-half/CMakeLists.txt index bc07d1ca6..e90781b7c 100644 --- a/libc-top-half/CMakeLists.txt +++ b/libc-top-half/CMakeLists.txt @@ -411,6 +411,7 @@ if (THREADS) musl/src/thread/wasi-threads/__lock.c musl/src/thread/wasi-threads/__wait.c musl/src/thread/wasi-threads/__timedwait.c + musl/src/thread/wasi-threads/lock_ptc.c musl/src/thread/wasi-threads/pthread_barrier_destroy.c musl/src/thread/wasi-threads/pthread_barrier_init.c musl/src/thread/wasi-threads/pthread_barrier_wait.c @@ -449,7 +450,73 @@ if (THREADS) musl/src/thread/wasi-threads/sem_wait.c musl/src/thread/wasi-threads/wasi_thread_start.s musl/src/thread/wasi-threads/__wasilibc_busywait.c + musl/src/thread/common/mtx_timedlock.c + musl/src/thread/common/mtx_init.c + musl/src/thread/common/mtx_destroy.c ) +elseif(WASI STREQUAL "p3") + # cooperative threads implementation for WASI Preview 3 + list(APPEND top_half_sources + musl/src/stdio/__lockfile.c + musl/src/stdio/flockfile.c + musl/src/stdio/ftrylockfile.c + musl/src/stdio/funlockfile.c + musl/src/thread/coop-threads/__libcall_thread_context.s + musl/src/thread/coop-threads/__lock.c + musl/src/thread/coop-threads/__wait.c + musl/src/thread/coop-threads/__wasi_coop_thread_start.s + musl/src/thread/coop-threads/lock_ptc.c + musl/src/thread/coop-threads/mtx_lock.c + musl/src/thread/coop-threads/mtx_trylock.c + musl/src/thread/coop-threads/pthread_barrier_destroy.c + musl/src/thread/coop-threads/pthread_barrier_init.c + musl/src/thread/coop-threads/pthread_barrier_wait.c + musl/src/thread/coop-threads/pthread_cond_broadcast.c + musl/src/thread/coop-threads/pthread_cond_destroy.c + musl/src/thread/coop-threads/pthread_cond_init.c + musl/src/thread/coop-threads/pthread_cond_signal.c + musl/src/thread/coop-threads/pthread_cond_timedwait.c + musl/src/thread/coop-threads/pthread_cond_wait.c + musl/src/thread/coop-threads/pthread_create.c + musl/src/thread/coop-threads/pthread_detach.c + musl/src/thread/coop-threads/pthread_getcpuclockid.c + musl/src/thread/coop-threads/pthread_getname_np.c + musl/src/thread/coop-threads/pthread_getschedparam.c + musl/src/thread/coop-threads/pthread_join.c + musl/src/thread/coop-threads/pthread_kill.c + musl/src/thread/coop-threads/pthread_mutex_consistent.c + musl/src/thread/coop-threads/pthread_mutex_lock.c + musl/src/thread/coop-threads/pthread_mutex_setprioceiling.c + musl/src/thread/coop-threads/pthread_mutex_timedlock.c + musl/src/thread/coop-threads/pthread_mutex_trylock.c + musl/src/thread/coop-threads/pthread_mutex_unlock.c + musl/src/thread/coop-threads/pthread_once.c + musl/src/thread/coop-threads/pthread_rwlock_rdlock.c + musl/src/thread/coop-threads/pthread_rwlock_timedrdlock.c + musl/src/thread/coop-threads/pthread_rwlock_timedwrlock.c + musl/src/thread/coop-threads/pthread_rwlock_tryrdlock.c + musl/src/thread/coop-threads/pthread_rwlock_trywrlock.c + musl/src/thread/coop-threads/pthread_rwlock_unlock.c + musl/src/thread/coop-threads/pthread_rwlock_wrlock.c + musl/src/thread/coop-threads/pthread_setconcurrency.c + musl/src/thread/coop-threads/pthread_setname_np.c + musl/src/thread/coop-threads/pthread_setschedparam.c + musl/src/thread/coop-threads/pthread_setschedprio.c + musl/src/thread/coop-threads/pthread_sigmask.c + musl/src/thread/coop-threads/pthread_spin_lock.c + musl/src/thread/coop-threads/pthread_spin_trylock.c + musl/src/thread/coop-threads/pthread_spin_unlock.c + musl/src/thread/coop-threads/sem_destroy.c + musl/src/thread/coop-threads/sem_getvalue.c + musl/src/thread/coop-threads/sem_init.c + musl/src/thread/coop-threads/sem_open.c + musl/src/thread/coop-threads/sem_post.c + musl/src/thread/coop-threads/sem_timedwait.c + musl/src/thread/coop-threads/sem_trywait.c + musl/src/thread/coop-threads/sem_wait.c + musl/src/thread/common/mtx_timedlock.c + musl/src/thread/common/mtx_init.c + musl/src/thread/common/mtx_destroy.c) else() # pthreads stubs for single-threaded environment list(APPEND top_half_sources diff --git a/libc-top-half/musl/include/alltypes.h.in b/libc-top-half/musl/include/alltypes.h.in index dcd474ff3..60203839e 100644 --- a/libc-top-half/musl/include/alltypes.h.in +++ b/libc-top-half/musl/include/alltypes.h.in @@ -1,6 +1,7 @@ #define __LITTLE_ENDIAN 1234 #define __BIG_ENDIAN 4321 #define __USE_TIME_BITS64 1 +#include TYPEDEF unsigned _Addr size_t; TYPEDEF unsigned _Addr uintptr_t; @@ -106,13 +107,23 @@ TYPEDEF unsigned socklen_t; TYPEDEF unsigned short sa_family_t; TYPEDEF struct { union { int __i[sizeof(long)==8?14:9]; volatile int __vi[sizeof(long)==8?14:9]; unsigned long __s[sizeof(long)==8?7:9]; } __u; } pthread_attr_t; +#ifdef __wasm_libcall_thread_context__ +struct __waitlist_node; +TYPEDEF struct { int _m_type; int _m_lock; int _m_count; struct __waitlist_node *_m_waiters; } pthread_mutex_t; +TYPEDEF struct { int _m_type; int _m_lock; int _m_count; struct __waitlist_node *_m_waiters; } mtx_t; +TYPEDEF struct { struct __waitlist_node *_c_waiters; } pthread_cond_t; +TYPEDEF struct { struct __waitlist_node *_c_waiters; } cnd_t; +TYPEDEF struct { long long _rw_lock; struct __waitlist_node *_rw_waiters; } pthread_rwlock_t; +TYPEDEF struct { int _b_limit; int _b_count; struct __waitlist_node *_b_waiters; } pthread_barrier_t; +#else TYPEDEF struct { union { int __i[sizeof(long)==8?10:6]; volatile int __vi[sizeof(long)==8?10:6]; volatile void *volatile __p[sizeof(long)==8?5:6]; } __u; } pthread_mutex_t; TYPEDEF struct { union { int __i[sizeof(long)==8?10:6]; volatile int __vi[sizeof(long)==8?10:6]; volatile void *volatile __p[sizeof(long)==8?5:6]; } __u; } mtx_t; TYPEDEF struct { union { int __i[12]; volatile int __vi[12]; void *__p[12*sizeof(int)/sizeof(void*)]; } __u; } pthread_cond_t; TYPEDEF struct { union { int __i[12]; volatile int __vi[12]; void *__p[12*sizeof(int)/sizeof(void*)]; } __u; } cnd_t; TYPEDEF struct { union { int __i[sizeof(long)==8?14:8]; volatile int __vi[sizeof(long)==8?14:8]; void *__p[sizeof(long)==8?7:8]; } __u; } pthread_rwlock_t; TYPEDEF struct { union { int __i[sizeof(long)==8?8:5]; volatile int __vi[sizeof(long)==8?8:5]; void *__p[sizeof(long)==8?4:5]; } __u; } pthread_barrier_t; +#endif #undef _Addr #undef _Int64 -#undef _Reg +#undef _Reg \ No newline at end of file diff --git a/libc-top-half/musl/include/pthread.h b/libc-top-half/musl/include/pthread.h index 111c1fbf8..19b6563e0 100644 --- a/libc-top-half/musl/include/pthread.h +++ b/libc-top-half/musl/include/pthread.h @@ -55,9 +55,16 @@ extern "C" { #define PTHREAD_PROCESS_SHARED 1 +#ifdef __wasm_libcall_thread_context__ +#define PTHREAD_MUTEX_INITIALIZER {0, 0, 0, 0} +#define PTHREAD_RWLOCK_INITIALIZER {0, 0} +#define PTHREAD_COND_INITIALIZER {0} +#define PTHREAD_BARRIER_INITIALIZER {0, 0, 0} +#else #define PTHREAD_MUTEX_INITIALIZER {{{0}}} #define PTHREAD_RWLOCK_INITIALIZER {{{0}}} #define PTHREAD_COND_INITIALIZER {{{0}}} +#endif #define PTHREAD_ONCE_INIT 0 @@ -107,11 +114,13 @@ int pthread_equal(pthread_t, pthread_t); int pthread_setcancelstate(int, int *); int pthread_setcanceltype(int, int *); void pthread_testcancel(void); -#ifdef __wasilibc_unmodified_upstream /* WASI has no cancellation support. */ +/* wasi-threads takes the approach of not declaring unsupported functions. + For greater compatibility, we stub them for cooperative threading. */ +#ifdef __wasm_libcall_thread_context__ /* WASI has no cancellation support. */ int pthread_cancel(pthread_t); #endif -#ifdef __wasilibc_unmodified_upstream /* WASI has no CPU scheduling support. */ +#ifdef __wasm_libcall_thread_context__ /* WASI has no CPU scheduling support. */ int pthread_getschedparam(pthread_t, int *__restrict, struct sched_param *__restrict); int pthread_setschedparam(pthread_t, int, const struct sched_param *); #endif @@ -177,7 +186,7 @@ int pthread_attr_getscope(const pthread_attr_t *__restrict, int *__restrict); int pthread_attr_setscope(pthread_attr_t *, int); int pthread_attr_getschedpolicy(const pthread_attr_t *__restrict, int *__restrict); int pthread_attr_setschedpolicy(pthread_attr_t *, int); -#ifdef __wasilibc_unmodified_upstream /* WASI has no CPU scheduling support. */ +#ifdef __wasm_libcall_thread_context__ /* WASI has no CPU scheduling support. */ int pthread_attr_getschedparam(const pthread_attr_t *__restrict, struct sched_param *__restrict); int pthread_attr_setschedparam(pthread_attr_t *__restrict, const struct sched_param *__restrict); #endif diff --git a/libc-top-half/musl/include/semaphore.h b/libc-top-half/musl/include/semaphore.h index 3690f4960..056fe9f5e 100644 --- a/libc-top-half/musl/include/semaphore.h +++ b/libc-top-half/musl/include/semaphore.h @@ -14,9 +14,17 @@ extern "C" { #define SEM_FAILED ((sem_t *)0) +#ifdef __wasm_libcall_thread_context__ +struct __waitlist_node; +typedef struct { + int __count; + struct __waitlist_node *__waiters; +} sem_t; +#else typedef struct { volatile int __val[4*sizeof(long)/sizeof(int)]; } sem_t; +#endif int sem_close(sem_t *); int sem_destroy(sem_t *); diff --git a/libc-top-half/musl/include/unistd.h b/libc-top-half/musl/include/unistd.h index d19871d09..b06f84957 100644 --- a/libc-top-half/musl/include/unistd.h +++ b/libc-top-half/musl/include/unistd.h @@ -359,7 +359,9 @@ pid_t gettid(void); #define _POSIX_BARRIERS _POSIX_VERSION #define _POSIX_SPIN_LOCKS _POSIX_VERSION #define _POSIX_READER_WRITER_LOCKS _POSIX_VERSION -#ifdef __wasilibc_unmodified_upstream /* WASI has no POSIX async I/O, semaphores, or shared memory */ +#ifdef __wasm_libcall_thread_context__ +#define _POSIX_SEMAPHORES _POSIX_VERSION +#elif defined(__wasilibc_unmodified_upstream) /* WASI has no POSIX async I/O, semaphores, or shared memory */ #define _POSIX_ASYNCHRONOUS_IO _POSIX_VERSION #define _POSIX_SEMAPHORES _POSIX_VERSION #define _POSIX_SHARED_MEMORY_OBJECTS _POSIX_VERSION diff --git a/libc-top-half/musl/src/conf/sysconf.c b/libc-top-half/musl/src/conf/sysconf.c index 2fdbe9850..3ca8b7add 100644 --- a/libc-top-half/musl/src/conf/sysconf.c +++ b/libc-top-half/musl/src/conf/sysconf.c @@ -21,7 +21,7 @@ #define JT_MQ_PRIO_MAX JT(3) #endif #define JT_PAGE_SIZE JT(4) -#ifdef __wasilibc_unmodified_upstream // WASI has no semaphores +#ifdef __wasm_libcall_thread_context__ // wasi-threads has no semaphores #define JT_SEM_VALUE_MAX JT(5) #endif #define JT_NPROCESSORS_CONF JT(6) @@ -88,7 +88,7 @@ long sysconf(int name) // Not supported on wasi. [_SC_RTSIG_MAX] = -1, #endif -#ifdef __wasilibc_unmodified_upstream // WASI has no semaphores +#ifdef __wasm_libcall_thread_context__ // wasi-threads has no semaphores [_SC_SEM_NSEMS_MAX] = SEM_NSEMS_MAX, [_SC_SEM_VALUE_MAX] = JT_SEM_VALUE_MAX, #else @@ -138,10 +138,10 @@ long sysconf(int name) [_SC_THREAD_THREADS_MAX] = -1, [_SC_THREAD_ATTR_STACKADDR] = VER, [_SC_THREAD_ATTR_STACKSIZE] = VER, - [_SC_THREAD_PRIORITY_SCHEDULING] = VER, + [_SC_THREAD_PRIORITY_SCHEDULING] = -1, [_SC_THREAD_PRIO_INHERIT] = -1, [_SC_THREAD_PRIO_PROTECT] = -1, - [_SC_THREAD_PROCESS_SHARED] = VER, + [_SC_THREAD_PROCESS_SHARED] = -1, [_SC_NPROCESSORS_CONF] = JT_NPROCESSORS_CONF, [_SC_NPROCESSORS_ONLN] = JT_NPROCESSORS_ONLN, [_SC_PHYS_PAGES] = JT_PHYS_PAGES, @@ -249,7 +249,7 @@ long sysconf(int name) #endif case JT_PAGE_SIZE & 255: return PAGE_SIZE; -#ifdef __wasilibc_unmodified_upstream // WASI has no semaphores +#if defined(__wasilibc_unmodified_upstream) || defined(__wasm_libcall_thread_context__) // wasi-threads has no semaphores case JT_SEM_VALUE_MAX & 255: return SEM_VALUE_MAX; #endif diff --git a/libc-top-half/musl/src/env/__init_tls.c b/libc-top-half/musl/src/env/__init_tls.c index 7f0d92931..a03b4b7ca 100644 --- a/libc-top-half/musl/src/env/__init_tls.c +++ b/libc-top-half/musl/src/env/__init_tls.c @@ -12,10 +12,10 @@ #include "libc.h" #include "atomic.h" #include "syscall.h" +#include +#include "lock.h" -#if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) -volatile int __thread_list_lock; -#endif +DECLARE_WEAK_LOCK(__thread_list_lock); #ifndef __wasilibc_unmodified_upstream @@ -40,6 +40,23 @@ struct stack_bounds { size_t size; }; +static inline unsigned char *get_stack_pointer() { + unsigned char *sp; +#ifdef __wasm_libcall_thread_context__ + __asm__( + ".functype __wasm_get_stack_pointer () -> (i32)\n" + "call __wasm_get_stack_pointer\n" + "local.set %0\n" + : "=r"(sp)); +#else + __asm__(".globaltype __stack_pointer, i32\n" + "global.get __stack_pointer\n" + "local.set %0\n" + : "=r"(sp)); +#endif + return sp; +} + static inline struct stack_bounds get_stack_bounds() { struct stack_bounds bounds; @@ -52,19 +69,14 @@ static inline struct stack_bounds get_stack_bounds() * how wasm-ld lays out things. For pic, just give up. */ #if !defined(__pic__) - unsigned char *sp; - __asm__( - ".globaltype __stack_pointer, i32\n" - "global.get __stack_pointer\n" - "local.set %0\n" - : "=r"(sp)); - if (sp > &__global_base) { - bounds.base = &__heap_base; - bounds.size = &__heap_base - &__data_end; - } else { - bounds.base = &__global_base; - bounds.size = (size_t)&__global_base; - } + unsigned char *sp = get_stack_pointer(); + if (sp > &__global_base) { + bounds.base = &__heap_base; + bounds.size = &__heap_base - &__data_end; + } else { + bounds.base = &__global_base; + bounds.size = (size_t)&__global_base; + } #else bounds.base = 0; bounds.size = 0; @@ -97,20 +109,27 @@ int __init_tp(void *p) td->stack = bounds.base; td->stack_size = bounds.size; td->guard_size = 0; -#ifdef _REENTRANT - td->detach_state = DT_JOINABLE; - /* - * Initialize the TID to a value which doesn't conflict with - * host-allocated TIDs, so that TID-based locks can work. - * - * Note: - * - Host-allocated TIDs range from 1 to 0x1fffffff. (inclusive) - * - __tl_lock and __lockfile uses TID 0 as "unlocked". - * - __lockfile relies on the fact the most significant two bits - * of TIDs are 0. - */ - td->tid = 0x3fffffff; -#endif + #if defined(__wasm_libcall_thread_context__) + td->detach_state = DT_JOINABLE; + #ifdef __wasip3__ + td->tid = wasip3_thread_index(); + #else + #error "Unknown WASI version" + #endif + #elif defined(_REENTRANT) + td->detach_state = DT_JOINABLE; + /* + * Initialize the TID to a value which doesn't conflict with + * host-allocated TIDs, so that TID-based locks can work. + * + * Note: + * - Host-allocated TIDs range from 1 to 0x1fffffff. (inclusive) + * - __tl_lock and __lockfile uses TID 0 as "unlocked". + * - __lockfile relies on the fact the most significant two bits + * of TIDs are 0. + */ + td->tid = 0x3fffffff; + #endif #endif #if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) td->locale = &libc.global_locale; @@ -178,9 +197,14 @@ void *__copy_tls(unsigned char *mem) mem += tls_align; mem -= (uintptr_t)mem & (tls_align-1); __wasm_init_tls(mem); + #ifdef __wasm_libcall_thread_context__ + void __wasm_set_tls_base(volatile void *ptr); + __wasm_set_tls_base(tls_base); + #else __asm__("local.get %0\n" "global.set __tls_base\n" :: "r"(tls_base)); + #endif return mem; #endif } @@ -268,3 +292,11 @@ static void static_init_tls(size_t *aux) weak_alias(static_init_tls, __init_tls); #endif + +#ifdef _REENTRANT +void* __allocate_tls() { + void* mem; + posix_memalign(&mem, __builtin_wasm_tls_align(), __builtin_wasm_tls_size()); + return mem; +} +#endif \ No newline at end of file diff --git a/libc-top-half/musl/src/exit/abort_lock.c b/libc-top-half/musl/src/exit/abort_lock.c index 3af72c7b6..6ab59771c 100644 --- a/libc-top-half/musl/src/exit/abort_lock.c +++ b/libc-top-half/musl/src/exit/abort_lock.c @@ -1,3 +1,4 @@ #include "pthread_impl.h" +#include "lock.h" -volatile int __abort_lock[1]; +DECLARE_STRONG_LOCK(__abort_lock); diff --git a/libc-top-half/musl/src/exit/at_quick_exit.c b/libc-top-half/musl/src/exit/at_quick_exit.c index 429d0b033..f6b863036 100644 --- a/libc-top-half/musl/src/exit/at_quick_exit.c +++ b/libc-top-half/musl/src/exit/at_quick_exit.c @@ -7,29 +7,26 @@ static void (*funcs[COUNT])(void); static int count; -#if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) -static volatile int lock[1]; -volatile int *const __at_quick_exit_lockptr = lock; -#endif +DECLARE_STRONG_LOCK(lock, static); void __funcs_on_quick_exit() { void (*func)(void); - LOCK(lock); + STRONG_LOCK(lock); while (count > 0) { func = funcs[--count]; - UNLOCK(lock); + STRONG_UNLOCK(lock); func(); - LOCK(lock); + STRONG_LOCK(lock); } } int at_quick_exit(void (*func)(void)) { int r = 0; - LOCK(lock); + STRONG_LOCK(lock); if (count == 32) r = -1; else funcs[count++] = func; - UNLOCK(lock); + STRONG_UNLOCK(lock); return r; } diff --git a/libc-top-half/musl/src/exit/atexit.c b/libc-top-half/musl/src/exit/atexit.c index e614351b9..e4a8176c3 100644 --- a/libc-top-half/musl/src/exit/atexit.c +++ b/libc-top-half/musl/src/exit/atexit.c @@ -22,26 +22,23 @@ static struct fl static int finished_atexit; static int slot; -#if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) -static volatile int lock[1]; -volatile int *const __atexit_lockptr = lock; -#endif +DECLARE_WEAK_LOCK(lock, static); void __funcs_on_exit() { void (*func)(void *), *arg; - LOCK(lock); + WEAK_LOCK(lock); for (; head; head=head->next, slot=COUNT) while(slot-->0) { func = head->f[slot]; arg = head->a[slot]; - UNLOCK(lock); + WEAK_UNLOCK(lock); func(arg); - LOCK(lock); + WEAK_LOCK(lock); } /* Unlock to prevent deadlock if a global dtor * attempts to call atexit. */ finished_atexit = 1; - UNLOCK(lock); + WEAK_UNLOCK(lock); } void __cxa_finalize(void *dso) @@ -50,12 +47,12 @@ void __cxa_finalize(void *dso) int __cxa_atexit(void (*func)(void *), void *arg, void *dso) { - LOCK(lock); + WEAK_LOCK(lock); /* Prevent dtors from registering further atexit * handlers that would never be run. */ if (finished_atexit) { - UNLOCK(lock); + WEAK_UNLOCK(lock); return -1; } @@ -66,7 +63,7 @@ int __cxa_atexit(void (*func)(void *), void *arg, void *dso) if (slot==COUNT) { struct fl *new_fl = calloc(sizeof(struct fl), 1); if (!new_fl) { - UNLOCK(lock); + WEAK_UNLOCK(lock); return -1; } new_fl->next = head; @@ -79,7 +76,7 @@ int __cxa_atexit(void (*func)(void *), void *arg, void *dso) head->a[slot] = arg; slot++; - UNLOCK(lock); + WEAK_UNLOCK(lock); return 0; } diff --git a/libc-top-half/musl/src/internal/locale_impl.h b/libc-top-half/musl/src/internal/locale_impl.h index 88f5dcf8f..93d3de4df 100644 --- a/libc-top-half/musl/src/internal/locale_impl.h +++ b/libc-top-half/musl/src/internal/locale_impl.h @@ -4,6 +4,7 @@ #include #include #include "libc.h" +#include "lock.h" #if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) #include "pthread_impl.h" #endif @@ -17,9 +18,7 @@ struct __locale_map { const struct __locale_map *next; }; -#if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) -extern hidden volatile int __locale_lock[1]; -#endif +DECLARE_WEAK_LOCK(__locale_lock, extern hidden); extern hidden const struct __locale_map __c_dot_utf8; extern hidden const struct __locale_struct __c_locale; diff --git a/libc-top-half/musl/src/internal/lock.h b/libc-top-half/musl/src/internal/lock.h index 29787fb1f..5bc84ede0 100644 --- a/libc-top-half/musl/src/internal/lock.h +++ b/libc-top-half/musl/src/internal/lock.h @@ -1,15 +1,52 @@ #ifndef LOCK_H #define LOCK_H -#if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) +// Defines internal locking primitives. The main elements defined here are: +// - __lock_t: the type of a lock variable +// - __lock_ptr_t: the type of a pointer to a lock variable +// - __LOCK_INIT: initializer for a lock variable +// - STRONG_LOCK/STRONG_UNLOCK: functions to acquire/release a lock for a critical section that contains +// potential context switch points, and thus must be executed even in cooperative threading mode. +// - WEAK_LOCK/WEAK_UNLOCK: functions to acquire/release a lock for a critical section that does not contain any context switch points, +// and thus can be no-ops in cooperative threading mode. + + +#ifdef __wasm_libcall_thread_context__ +struct __waitlist_node; +struct __coop_lock { + int owner; // tid of owning thread, or 0 if unlocked + struct __waitlist_node *waiters; +}; + +#define DECLARE_STRONG_LOCK(name, ...) __VA_ARGS__ struct __coop_lock name +#define DECLARE_WEAK_LOCK(name, ...) +#define __COOP_LOCK_INIT {0, NULL} +#define __LOCK_INIT __COOP_LOCK_INIT + +hidden void __lock(struct __coop_lock *lock); +hidden void __unlock(struct __coop_lock *lock); +#define STRONG_LOCK(x) __lock(&x) +#define STRONG_UNLOCK(x) __unlock(&x) +#define WEAK_LOCK(x) ((void)0) +#define WEAK_UNLOCK(x) ((void)0) +#elif defined(_REENTRANT) +#define DECLARE_STRONG_LOCK(name, ...) __VA_ARGS__ volatile int name +#define DECLARE_WEAK_LOCK(name, ...) __VA_ARGS__ volatile int name +#define __LOCK_INIT {0} hidden void __lock(volatile int *); hidden void __unlock(volatile int *); -#define LOCK(x) __lock(x) -#define UNLOCK(x) __unlock(x) +#define STRONG_LOCK(x) __lock(&x) +#define STRONG_UNLOCK(x) __unlock(&x) +#define WEAK_LOCK(x) __lock(&x) +#define WEAK_UNLOCK(x) __unlock(&x) #else -// No locking needed. -#define LOCK(x) ((void)0) -#define UNLOCK(x) ((void)0) +#define DECLARE_STRONG_LOCK(name, ...) +#define DECLARE_WEAK_LOCK(name, ...) +#define __LOCK_INIT {0} +#define STRONG_LOCK(x) ((void)0) +#define STRONG_UNLOCK(x) ((void)0) +#define WEAK_LOCK(x) ((void)0) +#define WEAK_UNLOCK(x) ((void)0) #endif #endif diff --git a/libc-top-half/musl/src/internal/pthread_impl.h b/libc-top-half/musl/src/internal/pthread_impl.h index 9c538bae7..32489f608 100644 --- a/libc-top-half/musl/src/internal/pthread_impl.h +++ b/libc-top-half/musl/src/internal/pthread_impl.h @@ -18,6 +18,8 @@ #include "futex.h" #include "pthread_arch.h" +#include "lock.h" +#include #define pthread __pthread @@ -40,7 +42,7 @@ struct pthread { #endif /* Part 2 -- implementation details, non-ABI. */ - int tid; + int tid; int errno_val; volatile int detach_state; volatile int cancel; @@ -63,9 +65,12 @@ struct pthread { int h_errno_val; volatile int timer_id; locale_t locale; - volatile int killlock[1]; + DECLARE_WEAK_LOCK(killlock); char *dlerror_buf; void *stdio_locks; + #ifdef __wasm_libcall_thread_context__ + struct __waitlist_node *joiner_waiters; + #endif /* Part 3 -- the positions of these fields relative to * the end of the structure is external and internal ABI. */ @@ -91,6 +96,7 @@ enum { #define _a_sched __u.__i[3*__SU+1] #define _a_policy __u.__i[3*__SU+2] #define _a_prio __u.__i[3*__SU+3] +#ifndef __wasm_libcall_thread_context__ #define _m_type __u.__i[0] #define _m_lock __u.__vi[1] #define _m_waiters __u.__vi[2] @@ -113,6 +119,7 @@ enum { #define _b_count __u.__vi[3] #define _b_waiters2 __u.__vi[4] #define _b_inst __u.__p[3] +#endif #ifndef TP_OFFSET #define TP_OFFSET 0 @@ -172,37 +179,25 @@ hidden int __libc_sigaction(int, const struct sigaction *, struct sigaction *); #endif hidden void __unmapself(void *, size_t); +#ifdef __wasm_libcall_thread_context__ +struct __waitlist_node { + uint32_t tid; + struct __waitlist_node *next; +}; +hidden void __waitlist_wait_on(struct __waitlist_node **list); +hidden void __waitlist_wake_one(struct __waitlist_node **list); +hidden void __waitlist_wake_all(struct __waitlist_node **list); +#else + #ifndef __wasilibc_unmodified_upstream hidden int __wasilibc_futex_wait(volatile void *, int, int, int64_t); #endif hidden int __timedwait(volatile int *, int, clockid_t, const struct timespec *, int); hidden int __timedwait_cp(volatile int *, int, clockid_t, const struct timespec *, int); hidden void __wait(volatile int *, volatile int *, int, int); -static inline void __wake(volatile void *addr, int cnt, int priv) -{ - if (priv) priv = FUTEX_PRIVATE; - if (cnt<0) cnt = INT_MAX; -#ifdef __wasilibc_unmodified_upstream - __syscall(SYS_futex, addr, FUTEX_WAKE|priv, cnt) != -ENOSYS || - __syscall(SYS_futex, addr, FUTEX_WAKE, cnt); -#else -#ifdef _REENTRANT - __builtin_wasm_memory_atomic_notify((int*)addr, cnt); -#else - (void) addr; -#endif -#endif -} -static inline void __futexwait(volatile void *addr, int val, int priv) -{ -#ifdef __wasilibc_unmodified_upstream - if (priv) priv = FUTEX_PRIVATE; - __syscall(SYS_futex, addr, FUTEX_WAIT|priv, val, 0) != -ENOSYS || - __syscall(SYS_futex, addr, FUTEX_WAIT, val, 0); -#else - __wait(addr, NULL, val, priv); +hidden void __wake(volatile void *addr, int cnt, int priv); +hidden void __futexwait(volatile void *addr, int val, int priv); #endif -} hidden void __acquire_ptc(void); hidden void __release_ptc(void); @@ -212,9 +207,8 @@ hidden void __tl_lock(void); hidden void __tl_unlock(void); hidden void __tl_sync(pthread_t); -extern hidden volatile int __thread_list_lock; - -extern hidden volatile int __abort_lock[1]; +DECLARE_WEAK_LOCK(__thread_list_lock, extern hidden); +DECLARE_STRONG_LOCK(__abort_lock, extern hidden); extern hidden unsigned __default_stacksize; extern hidden unsigned __default_guardsize; diff --git a/libc-top-half/musl/src/internal/stdio_impl.h b/libc-top-half/musl/src/internal/stdio_impl.h index 7f19dd951..9c0f0f86a 100644 --- a/libc-top-half/musl/src/internal/stdio_impl.h +++ b/libc-top-half/musl/src/internal/stdio_impl.h @@ -8,10 +8,20 @@ #define UNGET 8 -#if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) +#include "lock.h" + +#ifdef __wasm_libcall_thread_context__ +#define FFINALLOCK(f) __lockfile((f)) +#define FLOCK(f) int __need_unlock = __lockfile((f)) +#define FUNLOCK(f) do { if (__need_unlock) __unlockfile((f)); } while (0) +#define __STDIO_LOCK_INIT {0, 0} +#define __STDIO_LOCK_RESET(lock) do { (lock).owner = 0; (lock).waiters = NULL; } while (0) +#elif defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) #define FFINALLOCK(f) ((f)->lock>=0 ? __lockfile((f)) : 0) #define FLOCK(f) int __need_unlock = ((f)->lock>=0 ? __lockfile((f)) : 0) #define FUNLOCK(f) do { if (__need_unlock) __unlockfile((f)); } while (0) +#define __STDIO_LOCK_INIT -1 +#define __STDIO_LOCK_RESET(lock) do { (lock) = -1; } while (0) #else // No locking needed. #define FFINALLOCK(f) ((void)(f)) @@ -51,7 +61,7 @@ struct _IO_FILE { #endif int mode; #if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) - volatile int lock; + DECLARE_STRONG_LOCK(lock); #endif int lbf; void *cookie; @@ -73,8 +83,8 @@ extern hidden FILE *volatile __stdout_used; extern hidden FILE *volatile __stderr_used; #if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) -hidden int __lockfile(FILE *); -hidden void __unlockfile(FILE *); +int __lockfile(FILE *); +void __unlockfile(FILE *); #endif hidden size_t __stdio_read(FILE *, unsigned char *, size_t); diff --git a/libc-top-half/musl/src/locale/locale_map.c b/libc-top-half/musl/src/locale/locale_map.c index ebfb158f4..0d84be29e 100644 --- a/libc-top-half/musl/src/locale/locale_map.c +++ b/libc-top-half/musl/src/locale/locale_map.c @@ -30,10 +30,7 @@ static const char envvars[][12] = { "LC_MESSAGES", }; -#if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) -volatile int __locale_lock[1]; -volatile int *const __locale_lockptr = __locale_lock; -#endif +DECLARE_WEAK_LOCK(__locale_lock); const struct __locale_map *__get_locale(int cat, const char *val) { diff --git a/libc-top-half/musl/src/locale/newlocale.c b/libc-top-half/musl/src/locale/newlocale.c index 11c778557..b9b16923b 100644 --- a/libc-top-half/musl/src/locale/newlocale.c +++ b/libc-top-half/musl/src/locale/newlocale.c @@ -63,9 +63,9 @@ static locale_t do_newlocale(int mask, const char *name, locale_t loc) locale_t __newlocale(int mask, const char *name, locale_t loc) { - LOCK(__locale_lock); + WEAK_LOCK(__locale_lock); loc = do_newlocale(mask, name, loc); - UNLOCK(__locale_lock); + WEAK_UNLOCK(__locale_lock); return loc; } diff --git a/libc-top-half/musl/src/locale/setlocale.c b/libc-top-half/musl/src/locale/setlocale.c index 360c44376..5d12ad7f7 100644 --- a/libc-top-half/musl/src/locale/setlocale.c +++ b/libc-top-half/musl/src/locale/setlocale.c @@ -13,7 +13,7 @@ char *setlocale(int cat, const char *name) if ((unsigned)cat > LC_ALL) return 0; - LOCK(__locale_lock); + WEAK_LOCK(__locale_lock); /* For LC_ALL, setlocale is required to return a string which * encodes the current setting for all categories. The format of @@ -35,7 +35,7 @@ char *setlocale(int cat, const char *name) } lm = __get_locale(i, part); if (lm == LOC_MAP_FAILED) { - UNLOCK(__locale_lock); + WEAK_UNLOCK(__locale_lock); return 0; } tmp_locale.cat[i] = lm; @@ -56,14 +56,14 @@ char *setlocale(int cat, const char *name) s += l+1; } *--s = 0; - UNLOCK(__locale_lock); + WEAK_UNLOCK(__locale_lock); return same==LC_ALL ? (char *)part : buf; } if (name) { lm = __get_locale(cat, name); if (lm == LOC_MAP_FAILED) { - UNLOCK(__locale_lock); + WEAK_UNLOCK(__locale_lock); return 0; } libc.global_locale.cat[cat] = lm; @@ -72,7 +72,7 @@ char *setlocale(int cat, const char *name) } char *ret = lm ? (char *)lm->name : "C"; - UNLOCK(__locale_lock); + WEAK_UNLOCK(__locale_lock); return ret; } diff --git a/libc-top-half/musl/src/malloc/oldmalloc/malloc.c b/libc-top-half/musl/src/malloc/oldmalloc/malloc.c index 25d00d44d..3fe31f49b 100644 --- a/libc-top-half/musl/src/malloc/oldmalloc/malloc.c +++ b/libc-top-half/musl/src/malloc/oldmalloc/malloc.c @@ -27,6 +27,16 @@ static struct { /* Synchronization tools */ +#ifdef __wasm_libcall_thread_context__ +/* The implementation has no yield points, so locks can be no-ops. */ +static inline void lock(volatile int *lk) +{ +} + +static inline void unlock(volatile int *lk) +{ +} +#else static inline void lock(volatile int *lk) { int need_locks = libc.need_locks; @@ -43,6 +53,7 @@ static inline void unlock(volatile int *lk) if (lk[1]) __wake(lk, 1, 1); } } +#endif static inline void lock_bin(int i) { diff --git a/libc-top-half/musl/src/prng/random.c b/libc-top-half/musl/src/prng/random.c index daac028d8..3be96df15 100644 --- a/libc-top-half/musl/src/prng/random.c +++ b/libc-top-half/musl/src/prng/random.c @@ -23,10 +23,7 @@ static int n = 31; static int i = 3; static int j = 0; static uint32_t *x = init+1; -#if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) -static volatile int lock[1]; -volatile int *const __random_lockptr = lock; -#endif +DECLARE_WEAK_LOCK(lock, static); static uint32_t lcg31(uint32_t x) { return (1103515245*x + 12345) & 0x7fffffff; @@ -67,9 +64,9 @@ static void __srandom(unsigned seed) { } void srandom(unsigned seed) { - LOCK(lock); + WEAK_LOCK(lock); __srandom(seed); - UNLOCK(lock); + WEAK_UNLOCK(lock); } char *initstate(unsigned seed, char *state, size_t size) { @@ -77,7 +74,7 @@ char *initstate(unsigned seed, char *state, size_t size) { if (size < 8) return 0; - LOCK(lock); + WEAK_LOCK(lock); old = savestate(); if (size < 32) n = 0; @@ -92,24 +89,24 @@ char *initstate(unsigned seed, char *state, size_t size) { x = (uint32_t*)state + 1; __srandom(seed); savestate(); - UNLOCK(lock); + WEAK_UNLOCK(lock); return old; } char *setstate(char *state) { void *old; - LOCK(lock); + WEAK_LOCK(lock); old = savestate(); loadstate((uint32_t*)state); - UNLOCK(lock); + WEAK_UNLOCK(lock); return old; } long random(void) { long k; - LOCK(lock); + WEAK_LOCK(lock); if (n == 0) { k = x[0] = lcg31(x[0]); goto end; @@ -121,6 +118,6 @@ long random(void) { if (++j == n) j = 0; end: - UNLOCK(lock); + WEAK_UNLOCK(lock); return k; } diff --git a/libc-top-half/musl/src/stdio/__fdopen.c b/libc-top-half/musl/src/stdio/__fdopen.c index 5c8df495e..e37ae0d40 100644 --- a/libc-top-half/musl/src/stdio/__fdopen.c +++ b/libc-top-half/musl/src/stdio/__fdopen.c @@ -75,7 +75,7 @@ FILE *__fdopen(int fd, const char *mode) f->close = __stdio_close; #if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) - if (!libc.threaded) f->lock = -1; + if (!libc.threaded) __STDIO_LOCK_RESET(f->lock); #endif /* Add new FILE to open file list */ diff --git a/libc-top-half/musl/src/stdio/__fopen_rb_ca.c b/libc-top-half/musl/src/stdio/__fopen_rb_ca.c index 192050b00..aaf4e0d12 100644 --- a/libc-top-half/musl/src/stdio/__fopen_rb_ca.c +++ b/libc-top-half/musl/src/stdio/__fopen_rb_ca.c @@ -25,7 +25,7 @@ FILE *__fopen_rb_ca(const char *filename, FILE *f, unsigned char *buf, size_t le f->seek = __stdio_seek; f->close = __stdio_close; #if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) - f->lock = -1; + __STDIO_LOCK_RESET(f->lock); #endif return f; diff --git a/libc-top-half/musl/src/stdio/__lockfile.c b/libc-top-half/musl/src/stdio/__lockfile.c index 0f60a1499..8c20464d5 100644 --- a/libc-top-half/musl/src/stdio/__lockfile.c +++ b/libc-top-half/musl/src/stdio/__lockfile.c @@ -1,6 +1,29 @@ #include "stdio_impl.h" #include "pthread_impl.h" +#ifdef __wasm_libcall_thread_context__ +#include "lock.h" +int __lockfile(FILE *f) +{ + #ifdef __wasip3__ + int tid = wasip3_thread_index(); + #else + #error "Unknown WASI version" + #endif + + // Allow recursive locking + if (f->lock.owner == tid) + return 0; + + STRONG_LOCK(f->lock); + return 1; +} + +void __unlockfile(FILE *f) +{ + STRONG_UNLOCK(f->lock); +} +#else int __lockfile(FILE *f) { int owner = f->lock, tid = __pthread_self()->tid; @@ -21,3 +44,4 @@ void __unlockfile(FILE *f) if (a_swap(&f->lock, 0) & MAYBE_WAITERS) __wake(&f->lock, 1, 1); } +#endif \ No newline at end of file diff --git a/libc-top-half/musl/src/stdio/fmemopen.c b/libc-top-half/musl/src/stdio/fmemopen.c index 3ee57b9ea..821157dc6 100644 --- a/libc-top-half/musl/src/stdio/fmemopen.c +++ b/libc-top-half/musl/src/stdio/fmemopen.c @@ -130,7 +130,7 @@ FILE *fmemopen(void *restrict buf, size_t size, const char *restrict mode) f->f.close = mclose; #if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) - if (!libc.threaded) f->f.lock = -1; + if (!libc.threaded) __STDIO_LOCK_RESET(f->f.lock); #endif return __ofl_add(&f->f); diff --git a/libc-top-half/musl/src/stdio/ftrylockfile.c b/libc-top-half/musl/src/stdio/ftrylockfile.c index 50650585b..2854b6c33 100644 --- a/libc-top-half/musl/src/stdio/ftrylockfile.c +++ b/libc-top-half/musl/src/stdio/ftrylockfile.c @@ -4,9 +4,15 @@ void __do_orphaned_stdio_locks() { - FILE *f; - for (f=__pthread_self()->stdio_locks; f; f=f->next_locked) - a_store(&f->lock, 0x40000000); + FILE *f; + for (f=__pthread_self()->stdio_locks; f; f=f->next_locked) { + #ifdef __wasm_libcall_thread_context__ + f->lock.owner = 0; + __waitlist_wake_all(&f->lock.waiters); + #else + a_store(&f->lock, 0x40000000); + #endif + } } void __unlist_locked_file(FILE *f) @@ -27,6 +33,31 @@ void __register_locked_file(FILE *f, pthread_t self) self->stdio_locks = f; } +#ifdef __wasm_libcall_thread_context__ +int ftrylockfile(FILE *f) +{ + #ifdef __wasip3__ + uint32_t self_tid = wasip3_thread_index(); + #else + #error "Unknown WASI version" + #endif + + if (f->lock.owner == self_tid) { + if (f->lockcount == LONG_MAX) + return -1; + f->lockcount++; + return 0; + } + + // Try to acquire the lock + if (f->lock.owner != 0) + return -1; + + f->lock.owner = self_tid; + f->lockcount = 1; + return 0; +} +#else int ftrylockfile(FILE *f) { pthread_t self = __pthread_self(); @@ -44,3 +75,4 @@ int ftrylockfile(FILE *f) __register_locked_file(f, self); return 0; } +#endif \ No newline at end of file diff --git a/libc-top-half/musl/src/stdio/getc.h b/libc-top-half/musl/src/stdio/getc.h index e62e3f0da..f97fe6333 100644 --- a/libc-top-half/musl/src/stdio/getc.h +++ b/libc-top-half/musl/src/stdio/getc.h @@ -7,17 +7,28 @@ __attribute__((__noinline__)) #endif static int locking_getc(FILE *f) { +#ifdef __wasm_libcall_thread_context__ + __lockfile(f); + int c = getc_unlocked(f); + __unlockfile(f); + return c; +#else if (a_cas(&f->lock, 0, MAYBE_WAITERS-1)) __lockfile(f); int c = getc_unlocked(f); if (a_swap(&f->lock, 0) & MAYBE_WAITERS) __wake(&f->lock, 1, 1); return c; +#endif } #endif static inline int do_getc(FILE *f) { -#if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) +#ifdef __wasm_libcall_thread_context__ + if (f->lock.owner == wasip3_thread_index()) + return getc_unlocked(f); + return locking_getc(f); +#elif defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) int l = f->lock; if (l < 0 || l && (l & ~MAYBE_WAITERS) == __pthread_self()->tid) return getc_unlocked(f); diff --git a/libc-top-half/musl/src/stdio/ofl.c b/libc-top-half/musl/src/stdio/ofl.c index 33a8aa50b..f9f430b6c 100644 --- a/libc-top-half/musl/src/stdio/ofl.c +++ b/libc-top-half/musl/src/stdio/ofl.c @@ -3,18 +3,15 @@ #include "fork_impl.h" static FILE *ofl_head; -#if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) -static volatile int ofl_lock[1]; -volatile int *const __stdio_ofl_lockptr = ofl_lock; -#endif +DECLARE_STRONG_LOCK(ofl_lock, static); FILE **__ofl_lock() { - LOCK(ofl_lock); + STRONG_LOCK(ofl_lock); return &ofl_head; } void __ofl_unlock() { - UNLOCK(ofl_lock); + STRONG_UNLOCK(ofl_lock); } diff --git a/libc-top-half/musl/src/stdio/open_memstream.c b/libc-top-half/musl/src/stdio/open_memstream.c index aa5022065..34f93e563 100644 --- a/libc-top-half/musl/src/stdio/open_memstream.c +++ b/libc-top-half/musl/src/stdio/open_memstream.c @@ -102,7 +102,7 @@ FILE *open_memstream(char **bufp, size_t *sizep) f->f.mode = -1; #if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) - if (!libc.threaded) f->f.lock = -1; + if (!libc.threaded) __STDIO_LOCK_RESET(f->f.lock); #endif return __ofl_add(&f->f); diff --git a/libc-top-half/musl/src/stdio/open_wmemstream.c b/libc-top-half/musl/src/stdio/open_wmemstream.c index 198d5d439..7484ada2c 100644 --- a/libc-top-half/musl/src/stdio/open_wmemstream.c +++ b/libc-top-half/musl/src/stdio/open_wmemstream.c @@ -107,7 +107,7 @@ FILE *open_wmemstream(wchar_t **bufp, size_t *sizep) f->f.close = wms_close; #if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) - if (!libc.threaded) f->f.lock = -1; + if (!libc.threaded) __STDIO_LOCK_RESET(f->f.lock); #endif fwide(&f->f, 1); diff --git a/libc-top-half/musl/src/stdio/putc.h b/libc-top-half/musl/src/stdio/putc.h index 2cc63d2db..e05efb09d 100644 --- a/libc-top-half/musl/src/stdio/putc.h +++ b/libc-top-half/musl/src/stdio/putc.h @@ -7,17 +7,33 @@ __attribute__((__noinline__)) #endif static int locking_putc(int c, FILE *f) { +#ifdef __wasm_libcall_thread_context__ + __lockfile(f); + c = putc_unlocked(c, f); + __unlockfile(f); + return c; +#else if (a_cas(&f->lock, 0, MAYBE_WAITERS-1)) __lockfile(f); c = putc_unlocked(c, f); if (a_swap(&f->lock, 0) & MAYBE_WAITERS) __wake(&f->lock, 1, 1); return c; +#endif } #endif static inline int do_putc(int c, FILE *f) { -#if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) +#ifdef __wasm_libcall_thread_context__ + #ifdef __wasip3__ + int tid = wasip3_thread_index(); + #else + #error "Unknown WASI version" + #endif + if (f->lock.owner == tid) + return putc_unlocked(c, f); + return locking_putc(c, f); +#elif defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) int l = f->lock; if (l < 0 || l && (l & ~MAYBE_WAITERS) == __pthread_self()->tid) return putc_unlocked(c, f); diff --git a/libc-top-half/musl/src/stdio/stderr.c b/libc-top-half/musl/src/stdio/stderr.c index 5f24549f8..a14ba5637 100644 --- a/libc-top-half/musl/src/stdio/stderr.c +++ b/libc-top-half/musl/src/stdio/stderr.c @@ -13,7 +13,7 @@ hidden FILE __stderr_FILE = { .seek = __stdio_seek, .close = __stdio_close, #if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) - .lock = -1, + .lock = __STDIO_LOCK_INIT, #endif }; FILE *const stderr = &__stderr_FILE; diff --git a/libc-top-half/musl/src/stdio/stdin.c b/libc-top-half/musl/src/stdio/stdin.c index 68e1c3f64..ddc1a892a 100644 --- a/libc-top-half/musl/src/stdio/stdin.c +++ b/libc-top-half/musl/src/stdio/stdin.c @@ -12,7 +12,7 @@ hidden FILE __stdin_FILE = { .seek = __stdio_seek, .close = __stdio_close, #if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) - .lock = -1, + .lock = __STDIO_LOCK_INIT, #endif }; FILE *const stdin = &__stdin_FILE; diff --git a/libc-top-half/musl/src/stdio/stdout.c b/libc-top-half/musl/src/stdio/stdout.c index e0e2bced9..914f5a456 100644 --- a/libc-top-half/musl/src/stdio/stdout.c +++ b/libc-top-half/musl/src/stdio/stdout.c @@ -13,7 +13,7 @@ hidden FILE __stdout_FILE = { .seek = __stdio_seek, .close = __stdio_close, #if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) - .lock = -1, + .lock = __STDIO_LOCK_INIT, #endif }; FILE *const stdout = &__stdout_FILE; diff --git a/libc-top-half/musl/src/stdio/vdprintf.c b/libc-top-half/musl/src/stdio/vdprintf.c index cef0a1af2..4ab32308a 100644 --- a/libc-top-half/musl/src/stdio/vdprintf.c +++ b/libc-top-half/musl/src/stdio/vdprintf.c @@ -6,7 +6,7 @@ int vdprintf(int fd, const char *restrict fmt, va_list ap) .fd = fd, .lbf = EOF, .write = __stdio_write, .buf = (void *)fmt, .buf_size = 0, #if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) - .lock = -1 + .lock = __STDIO_LOCK_INIT, #endif }; return vfprintf(&f, fmt, ap); diff --git a/libc-top-half/musl/src/stdio/vsnprintf.c b/libc-top-half/musl/src/stdio/vsnprintf.c index 48c3aef7e..4d8366baf 100644 --- a/libc-top-half/musl/src/stdio/vsnprintf.c +++ b/libc-top-half/musl/src/stdio/vsnprintf.c @@ -41,7 +41,7 @@ int vsnprintf(char *restrict s, size_t n, const char *restrict fmt, va_list ap) .lbf = EOF, .write = sn_write, #if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) - .lock = -1, + .lock = __STDIO_LOCK_INIT, #endif .buf = buf, .cookie = &c, diff --git a/libc-top-half/musl/src/stdio/vsscanf.c b/libc-top-half/musl/src/stdio/vsscanf.c index 0e5b48265..883e1d8c4 100644 --- a/libc-top-half/musl/src/stdio/vsscanf.c +++ b/libc-top-half/musl/src/stdio/vsscanf.c @@ -19,10 +19,9 @@ int vsscanf(const char *restrict s, const char *restrict fmt, va_list ap) { FILE f = { .buf = (void *)s, .cookie = (void *)s, + .read = string_read, #if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) - .read = string_read, .lock = -1 -#else - .read = string_read + .lock = __STDIO_LOCK_INIT #endif }; return vfscanf(&f, fmt, ap); diff --git a/libc-top-half/musl/src/stdio/vswprintf.c b/libc-top-half/musl/src/stdio/vswprintf.c index cab94cba7..d17da02ba 100644 --- a/libc-top-half/musl/src/stdio/vswprintf.c +++ b/libc-top-half/musl/src/stdio/vswprintf.c @@ -44,7 +44,7 @@ int vswprintf(wchar_t *restrict s, size_t n, const wchar_t *restrict fmt, va_lis .lbf = EOF, .write = sw_write, #if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) - .lock = -1, + .lock = __STDIO_LOCK_INIT, #endif .buf = buf, .buf_size = sizeof buf, diff --git a/libc-top-half/musl/src/stdio/vswscanf.c b/libc-top-half/musl/src/stdio/vswscanf.c index ea827102d..feef4e451 100644 --- a/libc-top-half/musl/src/stdio/vswscanf.c +++ b/libc-top-half/musl/src/stdio/vswscanf.c @@ -30,10 +30,9 @@ int vswscanf(const wchar_t *restrict s, const wchar_t *restrict fmt, va_list ap) FILE f = { .buf = buf, .buf_size = sizeof buf, .cookie = (void *)s, + .read = wstring_read, #if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) - .read = wstring_read, .lock = -1 -#else - .read = wstring_read + .lock = __STDIO_LOCK_INIT #endif }; return vfwscanf(&f, fmt, ap); diff --git a/libc-top-half/musl/src/stdlib/wcstod.c b/libc-top-half/musl/src/stdlib/wcstod.c index 97b894eb2..9c853e155 100644 --- a/libc-top-half/musl/src/stdlib/wcstod.c +++ b/libc-top-half/musl/src/stdlib/wcstod.c @@ -44,7 +44,7 @@ static long double wcstox(const wchar_t *s, wchar_t **p, int prec) f.rpos = f.rend = f.buf = buf + 4; f.buf_size = sizeof buf - 4; #if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) - f.lock = -1; + __STDIO_LOCK_RESET(f.lock); #endif f.read = do_read; while (iswspace(*t)) t++; diff --git a/libc-top-half/musl/src/stdlib/wcstol.c b/libc-top-half/musl/src/stdlib/wcstol.c index 3aefd06f4..2771b3cf0 100644 --- a/libc-top-half/musl/src/stdlib/wcstol.c +++ b/libc-top-half/musl/src/stdlib/wcstol.c @@ -38,7 +38,7 @@ static unsigned long long wcstox(const wchar_t *s, wchar_t **p, int base, unsign f.rpos = f.rend = f.buf = buf + 4; f.buf_size = sizeof buf - 4; #if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) - f.lock = -1; + __STDIO_LOCK_RESET(f.lock); #endif f.read = do_read; while (iswspace(*t)) t++; diff --git a/libc-top-half/musl/src/thread/wasi-threads/call_once.c b/libc-top-half/musl/src/thread/common/call_once.c similarity index 100% rename from libc-top-half/musl/src/thread/wasi-threads/call_once.c rename to libc-top-half/musl/src/thread/common/call_once.c diff --git a/libc-top-half/musl/src/thread/wasi-threads/clone.c b/libc-top-half/musl/src/thread/common/clone.c similarity index 100% rename from libc-top-half/musl/src/thread/wasi-threads/clone.c rename to libc-top-half/musl/src/thread/common/clone.c diff --git a/libc-top-half/musl/src/thread/wasi-threads/cnd_broadcast.c b/libc-top-half/musl/src/thread/common/cnd_broadcast.c similarity index 100% rename from libc-top-half/musl/src/thread/wasi-threads/cnd_broadcast.c rename to libc-top-half/musl/src/thread/common/cnd_broadcast.c diff --git a/libc-top-half/musl/src/thread/wasi-threads/cnd_destroy.c b/libc-top-half/musl/src/thread/common/cnd_destroy.c similarity index 100% rename from libc-top-half/musl/src/thread/wasi-threads/cnd_destroy.c rename to libc-top-half/musl/src/thread/common/cnd_destroy.c diff --git a/libc-top-half/musl/src/thread/wasi-threads/cnd_init.c b/libc-top-half/musl/src/thread/common/cnd_init.c similarity index 100% rename from libc-top-half/musl/src/thread/wasi-threads/cnd_init.c rename to libc-top-half/musl/src/thread/common/cnd_init.c diff --git a/libc-top-half/musl/src/thread/wasi-threads/cnd_signal.c b/libc-top-half/musl/src/thread/common/cnd_signal.c similarity index 100% rename from libc-top-half/musl/src/thread/wasi-threads/cnd_signal.c rename to libc-top-half/musl/src/thread/common/cnd_signal.c diff --git a/libc-top-half/musl/src/thread/wasi-threads/cnd_timedwait.c b/libc-top-half/musl/src/thread/common/cnd_timedwait.c similarity index 100% rename from libc-top-half/musl/src/thread/wasi-threads/cnd_timedwait.c rename to libc-top-half/musl/src/thread/common/cnd_timedwait.c diff --git a/libc-top-half/musl/src/thread/wasi-threads/cnd_wait.c b/libc-top-half/musl/src/thread/common/cnd_wait.c similarity index 100% rename from libc-top-half/musl/src/thread/wasi-threads/cnd_wait.c rename to libc-top-half/musl/src/thread/common/cnd_wait.c diff --git a/libc-top-half/musl/src/thread/wasi-threads/mtx_destroy.c b/libc-top-half/musl/src/thread/common/mtx_destroy.c similarity index 100% rename from libc-top-half/musl/src/thread/wasi-threads/mtx_destroy.c rename to libc-top-half/musl/src/thread/common/mtx_destroy.c diff --git a/libc-top-half/musl/src/thread/wasi-threads/mtx_init.c b/libc-top-half/musl/src/thread/common/mtx_init.c similarity index 100% rename from libc-top-half/musl/src/thread/wasi-threads/mtx_init.c rename to libc-top-half/musl/src/thread/common/mtx_init.c diff --git a/libc-top-half/musl/src/thread/wasi-threads/mtx_timedlock.c b/libc-top-half/musl/src/thread/common/mtx_timedlock.c similarity index 100% rename from libc-top-half/musl/src/thread/wasi-threads/mtx_timedlock.c rename to libc-top-half/musl/src/thread/common/mtx_timedlock.c diff --git a/libc-top-half/musl/src/thread/wasi-threads/mtx_unlock.c b/libc-top-half/musl/src/thread/common/mtx_unlock.c similarity index 100% rename from libc-top-half/musl/src/thread/wasi-threads/mtx_unlock.c rename to libc-top-half/musl/src/thread/common/mtx_unlock.c diff --git a/libc-top-half/musl/src/thread/common/pthread_attr_get.c b/libc-top-half/musl/src/thread/common/pthread_attr_get.c index 503c5b85a..98b7b0802 100644 --- a/libc-top-half/musl/src/thread/common/pthread_attr_get.c +++ b/libc-top-half/musl/src/thread/common/pthread_attr_get.c @@ -77,9 +77,9 @@ int pthread_condattr_getclock(const pthread_condattr_t *restrict a, clockid_t *r #else int pthread_condattr_getclock(const pthread_condattr_t *restrict a, clockid_t *restrict clk) { - if (a->__attr & 0x7fffffff == CLOCKID_REALTIME) + if ((a->__attr & 0x7fffffff) == CLOCKID_REALTIME) *clk = CLOCK_REALTIME; - if (a->__attr & 0x7fffffff == CLOCKID_MONOTONIC) + else if ((a->__attr & 0x7fffffff) == CLOCKID_MONOTONIC) *clk = CLOCK_MONOTONIC; return 0; } diff --git a/libc-top-half/musl/src/thread/common/pthread_condattr_init.c b/libc-top-half/musl/src/thread/common/pthread_condattr_init.c index a41741b4e..2451c47fd 100644 --- a/libc-top-half/musl/src/thread/common/pthread_condattr_init.c +++ b/libc-top-half/musl/src/thread/common/pthread_condattr_init.c @@ -3,5 +3,6 @@ int pthread_condattr_init(pthread_condattr_t *a) { *a = (pthread_condattr_t){0}; + pthread_condattr_setclock(a, CLOCK_REALTIME); return 0; } diff --git a/libc-top-half/musl/src/thread/wasi-threads/pthread_getconcurrency.c b/libc-top-half/musl/src/thread/common/pthread_getconcurrency.c similarity index 100% rename from libc-top-half/musl/src/thread/wasi-threads/pthread_getconcurrency.c rename to libc-top-half/musl/src/thread/common/pthread_getconcurrency.c diff --git a/libc-top-half/musl/src/thread/common/pthread_rwlock_init.c b/libc-top-half/musl/src/thread/common/pthread_rwlock_init.c index a2c0b478c..32961af59 100644 --- a/libc-top-half/musl/src/thread/common/pthread_rwlock_init.c +++ b/libc-top-half/musl/src/thread/common/pthread_rwlock_init.c @@ -3,6 +3,8 @@ int pthread_rwlock_init(pthread_rwlock_t *restrict rw, const pthread_rwlockattr_t *restrict a) { *rw = (pthread_rwlock_t){0}; + #ifndef __wasm_libcall_thread_context__ if (a) rw->_rw_shared = a->__attr[0]*128; + #endif return 0; } diff --git a/libc-top-half/musl/src/thread/common/thrd_create.c b/libc-top-half/musl/src/thread/common/thrd_create.c new file mode 100644 index 000000000..76a683dbf --- /dev/null +++ b/libc-top-half/musl/src/thread/common/thrd_create.c @@ -0,0 +1,12 @@ +#include "pthread_impl.h" +#include + +int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) +{ + int ret = __pthread_create(thr, __ATTRP_C11_THREAD, (void *(*)(void *))func, arg); + switch (ret) { + case 0: return thrd_success; + case EAGAIN: return thrd_nomem; + default: return thrd_error; + } +} diff --git a/libc-top-half/musl/src/thread/common/thrd_exit.c b/libc-top-half/musl/src/thread/common/thrd_exit.c new file mode 100644 index 000000000..9b291ae3d --- /dev/null +++ b/libc-top-half/musl/src/thread/common/thrd_exit.c @@ -0,0 +1,8 @@ +#include +#include +#include + +_Noreturn void thrd_exit(int result) +{ + __pthread_exit((void*)(intptr_t)result); +} diff --git a/libc-top-half/musl/src/thread/common/thrd_join.c b/libc-top-half/musl/src/thread/common/thrd_join.c new file mode 100644 index 000000000..0d5fd302d --- /dev/null +++ b/libc-top-half/musl/src/thread/common/thrd_join.c @@ -0,0 +1,11 @@ +#include +#include +#include + +int thrd_join(thrd_t t, int *res) +{ + void *pthread_res; + __pthread_join(t, &pthread_res); + if (res) *res = (int)(intptr_t)pthread_res; + return thrd_success; +} diff --git a/libc-top-half/musl/src/thread/common/thrd_sleep copy.c b/libc-top-half/musl/src/thread/common/thrd_sleep copy.c new file mode 100644 index 000000000..97de53455 --- /dev/null +++ b/libc-top-half/musl/src/thread/common/thrd_sleep copy.c @@ -0,0 +1,14 @@ +#include +#include +#include +#include "syscall.h" + +int thrd_sleep(const struct timespec *req, struct timespec *rem) +{ + int ret = -__clock_nanosleep(CLOCK_REALTIME, 0, req, rem); + switch (ret) { + case 0: return 0; + case -EINTR: return -1; /* value specified by C11 */ + default: return -2; + } +} diff --git a/libc-top-half/musl/src/thread/common/thrd_yield.c b/libc-top-half/musl/src/thread/common/thrd_yield.c new file mode 100644 index 000000000..171690f9d --- /dev/null +++ b/libc-top-half/musl/src/thread/common/thrd_yield.c @@ -0,0 +1,7 @@ +#include +#include "syscall.h" + +void thrd_yield() +{ + sched_yield(); +} diff --git a/libc-top-half/musl/src/thread/wasi-threads/tss_create.c b/libc-top-half/musl/src/thread/common/tss_create.c similarity index 100% rename from libc-top-half/musl/src/thread/wasi-threads/tss_create.c rename to libc-top-half/musl/src/thread/common/tss_create.c diff --git a/libc-top-half/musl/src/thread/wasi-threads/tss_delete.c b/libc-top-half/musl/src/thread/common/tss_delete.c similarity index 100% rename from libc-top-half/musl/src/thread/wasi-threads/tss_delete.c rename to libc-top-half/musl/src/thread/common/tss_delete.c diff --git a/libc-top-half/musl/src/thread/wasi-threads/tss_set.c b/libc-top-half/musl/src/thread/common/tss_set.c similarity index 100% rename from libc-top-half/musl/src/thread/wasi-threads/tss_set.c rename to libc-top-half/musl/src/thread/common/tss_set.c diff --git a/libc-top-half/musl/src/thread/coop-threads/__libcall_thread_context.s b/libc-top-half/musl/src/thread/coop-threads/__libcall_thread_context.s new file mode 100644 index 000000000..4959746b4 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/__libcall_thread_context.s @@ -0,0 +1,45 @@ +# LLVM emits calls to the following functions when the libcall-thread-context feature is enabled (default on WASIp3) + + .globl __wasm_get_stack_pointer + .type __wasm_get_stack_pointer,@function + .globl __wasm_set_stack_pointer + .type __wasm_set_stack_pointer,@function + .globl __wasm_get_tls_base + .type __wasm_get_tls_base,@function + .globl __wasm_set_tls_base + .type __wasm_set_tls_base,@function + + .functype __wasm_component_model_builtin_context_get_0 () -> (i32) + .import_module __wasm_component_model_builtin_context_get_0, "$root" + .import_name __wasm_component_model_builtin_context_get_0, "[context-get-0]" + .functype __wasm_component_model_builtin_context_set_0 (i32) -> () + .import_module __wasm_component_model_builtin_context_set_0, "$root" + .import_name __wasm_component_model_builtin_context_set_0, "[context-set-0]" + .functype __wasm_component_model_builtin_context_get_1 () -> (i32) + .import_module __wasm_component_model_builtin_context_get_1, "$root" + .import_name __wasm_component_model_builtin_context_get_1, "[context-get-1]" + .functype __wasm_component_model_builtin_context_set_1 (i32) -> () + .import_module __wasm_component_model_builtin_context_set_1, "$root" + .import_name __wasm_component_model_builtin_context_set_1, "[context-set-1]" + +__wasm_get_stack_pointer: + .functype __wasm_get_stack_pointer () -> (i32) + call __wasm_component_model_builtin_context_get_0 + end_function + +__wasm_set_stack_pointer: + .functype __wasm_set_stack_pointer (i32) -> () + local.get 0 + call __wasm_component_model_builtin_context_set_0 + end_function + +__wasm_get_tls_base: + .functype __wasm_get_tls_base () -> (i32) + call __wasm_component_model_builtin_context_get_1 + end_function + +__wasm_set_tls_base: + .functype __wasm_set_tls_base (i32) -> () + local.get 0 + call __wasm_component_model_builtin_context_set_1 + end_function diff --git a/libc-top-half/musl/src/thread/coop-threads/__lock.c b/libc-top-half/musl/src/thread/coop-threads/__lock.c new file mode 100644 index 000000000..9baeceff0 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/__lock.c @@ -0,0 +1,38 @@ +#include "pthread_impl.h" +#include "lock.h" +#include + +#ifndef __wasip3__ +#error "Unknown WASI version" +#endif + +void __lock(struct __coop_lock *lock) +{ + int tid = wasip3_thread_index(); + if (lock->owner == tid) { + /* Trap on recursive locking. */ + __builtin_trap(); + } + + /* Loop until we acquire the lock. */ + while (lock->owner != 0) { + __waitlist_wait_on(&lock->waiters); + /* After waking, the lock might still be held by another + * thread that was scheduled before us, so loop back. */ + } + + lock->owner = tid; +} + +void __unlock(struct __coop_lock *lock) +{ + int tid = wasip3_thread_index(); + if (lock->owner != tid) { + /* We're trying to unlock a lock we don't own. */ + __builtin_trap(); + } + + lock->owner = 0; + /* Awake one waiter; the others will be resumed on future unlocks. */ + __waitlist_wake_one(&lock->waiters); +} \ No newline at end of file diff --git a/libc-top-half/musl/src/thread/coop-threads/__wait.c b/libc-top-half/musl/src/thread/coop-threads/__wait.c new file mode 100644 index 000000000..2bd3defd8 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/__wait.c @@ -0,0 +1,54 @@ +#include "pthread_impl.h" +#include +#include + +#ifndef __wasip3__ +#error "Unknown WASI version" +#endif + +void __waitlist_wait_on(struct __waitlist_node **list) +{ + struct __waitlist_node node = { + .tid = wasip3_thread_index(), + .next = *list, + }; + *list = &node; + + wasip3_thread_suspend(); + + // Another thread may have modified *list (and node.next) while we were + // suspended. Prevent the compiler from caching any memory values across + // the suspend point. This is a compiler-only fence: no instructions are + // emitted, so no atomics feature is required. + __atomic_signal_fence(memory_order_seq_cst); +} + +void __waitlist_wake_one(struct __waitlist_node **list) +{ + if (*list == NULL) { + return; + } + struct __waitlist_node *node = *list; + *list = node->next; + wasip3_thread_yield_to_suspended(node->tid); +} + +void __waitlist_wake_all(struct __waitlist_node **list) +{ + struct __waitlist_node **prev = list; + struct __waitlist_node *curr = *list; + + while (curr) { + uint32_t tid = curr->tid; + *prev = curr->next; + // As a scheduling optimization, we always yield directly to the last + // suspended thread instead of just scheduling it to run at some point. + if (curr->next == NULL) { + wasip3_thread_yield_to_suspended(tid); + } + else { + wasip3_thread_unsuspend(tid); + } + curr = *prev; + } +} \ No newline at end of file diff --git a/libc-top-half/musl/src/thread/coop-threads/__wasi_coop_thread_start.s b/libc-top-half/musl/src/thread/coop-threads/__wasi_coop_thread_start.s new file mode 100644 index 000000000..90a46a92f --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/__wasi_coop_thread_start.s @@ -0,0 +1,29 @@ + .text + + .export_name __wasi_coop_thread_start, __wasi_coop_thread_start + + .globaltype __init_stack_pointer, i32 + .functype __wasi_coop_thread_start_C (i32) -> () + .functype __wasm_set_tls_base (i32) -> () + .functype __wasm_set_stack_pointer (i32) -> () + + .hidden __wasi_coop_thread_start + .globl __wasi_coop_thread_start + .type __wasi_coop_thread_start,@function + +__wasi_coop_thread_start: + .functype __wasi_coop_thread_start (i32) -> () + + # Set up the minimum C environment. + local.get 0 # start_arg + i32.load 0 # stack + call __wasm_set_stack_pointer + + local.get 0 # start_arg + i32.load 4 # tls_base + call __wasm_set_tls_base + # Make the C function do the rest of work. + local.get 0 # start_arg + call __wasi_coop_thread_start_C + + end_function \ No newline at end of file diff --git a/libc-top-half/musl/src/thread/coop-threads/lock_ptc.c b/libc-top-half/musl/src/thread/coop-threads/lock_ptc.c new file mode 100644 index 000000000..28c7a831c --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/lock_ptc.c @@ -0,0 +1,3 @@ +void __inhibit_ptc(){} +void __acquire_ptc(){} +void __release_ptc(){} \ No newline at end of file diff --git a/libc-top-half/musl/src/thread/coop-threads/mtx_lock.c b/libc-top-half/musl/src/thread/coop-threads/mtx_lock.c new file mode 100644 index 000000000..94a7e506f --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/mtx_lock.c @@ -0,0 +1,14 @@ +#include "pthread_impl.h" +#include + +int mtx_lock(mtx_t *m) +{ + if (m->_m_type == PTHREAD_MUTEX_NORMAL && m->_m_lock == 0) { + m->_m_lock = EBUSY; + return thrd_success; + } + /* Calling mtx_timedlock with a null pointer is an extension. + * It is convenient, here to avoid duplication of the logic + * for return values. */ + return mtx_timedlock(m, 0); +} diff --git a/libc-top-half/musl/src/thread/coop-threads/mtx_trylock.c b/libc-top-half/musl/src/thread/coop-threads/mtx_trylock.c new file mode 100644 index 000000000..54ba9c628 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/mtx_trylock.c @@ -0,0 +1,21 @@ +#include "pthread_impl.h" +#include + +int mtx_trylock(mtx_t *m) +{ + if (m->_m_type == PTHREAD_MUTEX_NORMAL) { + if (m->_m_lock == 0) { + m->_m_lock = EBUSY; + return thrd_success; + } + else { + return thrd_busy; + } + } + int ret = __pthread_mutex_trylock((pthread_mutex_t *)m); + switch (ret) { + default: return thrd_error; + case 0: return thrd_success; + case EBUSY: return thrd_busy; + } +} diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_barrier_destroy.c b/libc-top-half/musl/src/thread/coop-threads/pthread_barrier_destroy.c new file mode 100644 index 000000000..0199620b2 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_barrier_destroy.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_barrier_destroy(pthread_barrier_t *b) +{ + return 0; +} \ No newline at end of file diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_barrier_init.c b/libc-top-half/musl/src/thread/coop-threads/pthread_barrier_init.c new file mode 100644 index 000000000..2c5ba7eb7 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_barrier_init.c @@ -0,0 +1,8 @@ +#include "pthread_impl.h" + +int pthread_barrier_init(pthread_barrier_t *restrict b, const pthread_barrierattr_t *restrict a, unsigned count) +{ + if (count == 0 || count > INT_MAX) return EINVAL; + *b = (pthread_barrier_t){ ._b_limit = count, ._b_count = 0, ._b_waiters = 0 }; + return 0; +} \ No newline at end of file diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_barrier_wait.c b/libc-top-half/musl/src/thread/coop-threads/pthread_barrier_wait.c new file mode 100644 index 000000000..b858320b5 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_barrier_wait.c @@ -0,0 +1,23 @@ +#include "pthread_impl.h" + +int pthread_barrier_wait(pthread_barrier_t *b) +{ + // Trivial case: only one thread needs to reach barrier + if (b->_b_limit == 1) { + return PTHREAD_BARRIER_SERIAL_THREAD; + } + + // Increment count of waiting threads + b->_b_count++; + + // If all threads have arrived, wake them all + if (b->_b_count == b->_b_limit) { + b->_b_count = 0; + __waitlist_wake_all(&b->_b_waiters); + return PTHREAD_BARRIER_SERIAL_THREAD; + } + + // Otherwise, wait for remaining threads to enter + __waitlist_wait_on(&b->_b_waiters); + return 0; +} diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_cond_broadcast.c b/libc-top-half/musl/src/thread/coop-threads/pthread_cond_broadcast.c new file mode 100644 index 000000000..17f5c773f --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_cond_broadcast.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +int pthread_cond_broadcast(pthread_cond_t *c) +{ + __waitlist_wake_all(&c->_c_waiters); + return 0; +} diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_cond_destroy.c b/libc-top-half/musl/src/thread/coop-threads/pthread_cond_destroy.c new file mode 100644 index 000000000..e2ea8c30c --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_cond_destroy.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_cond_destroy(pthread_cond_t *c) +{ + return 0; +} \ No newline at end of file diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_cond_init.c b/libc-top-half/musl/src/thread/coop-threads/pthread_cond_init.c new file mode 100644 index 000000000..146a270ed --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_cond_init.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +int pthread_cond_init(pthread_cond_t *restrict c, const pthread_condattr_t *restrict a) +{ + *c = (pthread_cond_t){0}; + return 0; +} diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_cond_signal.c b/libc-top-half/musl/src/thread/coop-threads/pthread_cond_signal.c new file mode 100644 index 000000000..17c248c28 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_cond_signal.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +int pthread_cond_signal(pthread_cond_t *c) +{ + __waitlist_wake_one(&c->_c_waiters); + return 0; +} \ No newline at end of file diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_cond_timedwait.c b/libc-top-half/musl/src/thread/coop-threads/pthread_cond_timedwait.c new file mode 100644 index 000000000..121501329 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_cond_timedwait.c @@ -0,0 +1,15 @@ +#include "pthread_impl.h" + +int __pthread_cond_timedwait(pthread_cond_t *restrict c, pthread_mutex_t *restrict m, const struct timespec *restrict ts) +{ + // TODO(wasip3) timed waits + if (ts) { + return ETIMEDOUT; + } + + pthread_mutex_unlock(m); + __waitlist_wait_on(&c->_c_waiters); + return pthread_mutex_lock(m); +} + +weak_alias(__pthread_cond_timedwait, pthread_cond_timedwait); \ No newline at end of file diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_cond_wait.c b/libc-top-half/musl/src/thread/coop-threads/pthread_cond_wait.c new file mode 100644 index 000000000..8735bf147 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_cond_wait.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_cond_wait(pthread_cond_t *restrict c, pthread_mutex_t *restrict m) +{ + return pthread_cond_timedwait(c, m, 0); +} diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_create.c b/libc-top-half/musl/src/thread/coop-threads/pthread_create.c new file mode 100644 index 000000000..bd75faeda --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_create.c @@ -0,0 +1,310 @@ +#define _GNU_SOURCE +#include "libc.h" +#include "lock.h" +#include "pthread_impl.h" +#include "stdio_impl.h" +#include +#include +#include + +#include +#include +#include + +// These are implemented elsewhere +static void dummy_0() {} +weak_alias(dummy_0, __pthread_tsd_run_dtors); +weak_alias(dummy_0, __do_orphaned_stdio_locks); + +static void *map_base_deferred_free; + +static void process_map_base_deferred_free() { + free(map_base_deferred_free); + map_base_deferred_free = NULL; +} + +// There is currently no thread.exit intrinsic, so pthread_exit +// cannot terminate a thread early. As such, we do not expose it to users. +// TODO(wasip3) revisit this if we add a thread.exit intrinsic +static void __pthread_exit(void *result) +{ + pthread_t self = __pthread_self(); + + self->canceldisable = 1; + self->cancelasync = 0; + self->result = result; + + while (self->cancelbuf) { + void (*f)(void *) = self->cancelbuf->__f; + void *x = self->cancelbuf->__x; + self->cancelbuf = self->cancelbuf->__next; + f(x); + } + + __pthread_tsd_run_dtors(); + + if (self->detach_state == DT_JOINABLE) { + self->detach_state = DT_EXITING; + } + int state = self->detach_state; + + /* If this is the only thread in the list, don't proceed with + * termination of the thread, just exit immediately */ + if (self->next == self) { + self->detach_state = state; + exit(0); + } + + // At this point we are committed to thread termination. + + /* After the kernel thread exits, its tid may be reused. Clear it + * to prevent inadvertent use and inform functions that would use + * it that it's no longer available. */ + self->tid = 0; + + __do_orphaned_stdio_locks(); + + // This is the point where we would handle robust mutexes if we supported them + + // Unlink the thread from the thread list. + if (!--libc.threads_minus_1) + libc.need_locks = -1; + self->next->prev = self->prev; + self->prev->next = self->next; + self->prev = self->next = self; + + if (state == DT_DETACHED && self->map_base) { + /* As we use malloc/free which is considerably more complex + * than mmap/munmap to call and can even require a valid + * thread context, it's difficult to implement __unmapself. + * + * Here we take an alternative approach which simply defers + * the deallocation. An obvious downside of this approach is + * that it keeps the stack longer. (possibly forever.) + * To avoid wasting too much memory, we only defer a single + * item at most. */ + process_map_base_deferred_free(); + map_base_deferred_free = self->map_base; + // Here is where we would call the thread.exit intrinsic + return; + } + + self->detach_state = DT_EXITED; + + // There may be a thread waiting for this thread to exit, in which case + // we wake it up. + __waitlist_wake_all(&self->joiner_waiters); +} + +void __do_cleanup_push(struct __ptcb *cb) { + struct pthread *self = __pthread_self(); + cb->__next = self->cancelbuf; + self->cancelbuf = cb; +} + +void __do_cleanup_pop(struct __ptcb *cb) { + __pthread_self()->cancelbuf = cb->__next; +} + +struct start_args { + /* + * Note: the offset of the "stack" and "tls_base" members + * in this structure is hardcoded in wasip3_thread_start. + */ + void *stack; + void *tls_base; + void *(*start_func)(void *); + void *start_arg; +}; + +/* + * We want to ensure __wasi_coop_thread_start is linked whenever + * pthread_create is used. The following reference is to ensure that. + * Otherwise, the linker doesn't notice the dependency because + * __wasi_coop_thread_start is used indirectly via a wasm export. + */ +void __wasi_coop_thread_start(void *context); +hidden void *__dummy_reference = __wasi_coop_thread_start; + +hidden void __wasi_coop_thread_start_C(void *context) { + #ifdef __wasip3__ + int tid = wasip3_thread_index(); + #else + #error "Unknown WASI version" + #endif + struct start_args *args = context; + __pthread_self()->tid = tid; + __pthread_exit(args->start_func(args->start_arg)); +} + +/* + * As we allocate stack with malloc() instead of mmap/mprotect, + * there is no point to round it up to PAGE_SIZE. + * Instead, round up to a sane alignment. + * Note: PAGE_SIZE is rather big on WASM. (65536) + */ +#define ROUND(x) (((x) + 16 - 1) & -16) + +/* pthread_key_create.c overrides this */ +static volatile size_t dummy = 0; +weak_alias(dummy, __pthread_tsd_size); +static void *dummy_tsd[1] = {0}; +weak_alias(dummy_tsd, __pthread_tsd_main); + +static FILE *volatile dummy_file = 0; +weak_alias(dummy_file, __stdin_used); +weak_alias(dummy_file, __stdout_used); +weak_alias(dummy_file, __stderr_used); + +int __pthread_create(pthread_t *restrict res, + const pthread_attr_t *restrict attrp, + void *(*entry)(void *), void *restrict arg) { + int ret, c11 = (attrp == __ATTRP_C11_THREAD); + size_t size, guard; + struct pthread *self, *new; + unsigned char *map = 0, *stack = 0, *tsd = 0, *stack_limit; + + pthread_attr_t attr = {0}; + + size_t tls_size = __builtin_wasm_tls_size(); + size_t tls_align = __builtin_wasm_tls_align(); + void *tls_base = __builtin_wasm_tls_base(); + void *new_tls_base; + size_t tls_offset; + /* We'll need to allocate space for a correctly-aligned TLS block, + so adjust the size accordingly. */ + tls_size += tls_align; + + self = __pthread_self(); + + if (!libc.threaded) { + self->tsd = (void **)__pthread_tsd_main; + libc.threaded = 1; + } + if (attrp && !c11) + attr = *attrp; + + if (!attrp || c11) { + attr._a_stacksize = __default_stacksize; + attr._a_guardsize = __default_guardsize; + } + + if (attr._a_stackaddr) { + size_t need = tls_size + __pthread_tsd_size; + size = attr._a_stacksize; + stack = (void *)(attr._a_stackaddr & -16); + stack_limit = (void *)(attr._a_stackaddr - size); + /* Use application-provided stack for TLS only when + * it does not take more than ~12% or 2k of the + * application's stack space. */ + if (need < size / 8 && need < 2048) { + tsd = stack - __pthread_tsd_size; + stack = tsd - tls_size; + memset(stack, 0, need); + } else { + size = ROUND(need); + } + guard = 0; + } else { + guard = ROUND(attr._a_guardsize); + size = guard + ROUND(attr._a_stacksize + + tls_size + __pthread_tsd_size); + } + + if (!tsd) { + /* Process the deferred free request if any before + * allocationg a new one. Hopefully it enables a reuse of the memory. + * + * Note: We can't perform a simple "handoff" becasue allocation + * sizes might be different. (eg. the stack size might differ) */ + __tl_lock(); + process_map_base_deferred_free(); + __tl_unlock(); + map = malloc(size); + printf("pthread_create: map=%p size=%zu\n", map, size); + if (!map) + goto fail; + tsd = map + size - __pthread_tsd_size; + memset(tsd, 0, __pthread_tsd_size); + if (!stack) { + stack = tsd - tls_size; + stack_limit = map + guard; + } + } + + new_tls_base = __copy_tls(tsd - tls_size); + + /* Compute pthread struct offset from old TLS base, apply to new TLS base */ + tls_offset = (uintptr_t)self - (uintptr_t)tls_base; + new = (void *)((uintptr_t)new_tls_base + tls_offset); + + new->map_base = map; + new->map_size = size; + new->stack = stack; + new->stack_size = stack - stack_limit; + new->guard_size = guard; + new->tsd = (void *)tsd; + new->locale = &libc.global_locale; + new->self = new; + if (attr._a_detach) { + new->detach_state = DT_DETACHED; + } else { + new->detach_state = DT_JOINABLE; + } + new->robust_list.head = &new->robust_list.head; + new->canary = self->canary; + new->sysinfo = self->sysinfo; + + // Setup argument structure for the new thread on its stack. + + /* Align the stack to struct start_args */ + stack -= sizeof(struct start_args); + stack -= (uintptr_t)stack % alignof(struct start_args); + struct start_args *args = (void *)stack; + + /* Align the stack to 16 and store it */ + new->stack = (void *)((uintptr_t)stack & -16); + /* Correct the stack size */ + new->stack_size = stack - stack_limit; + + args->stack = new->stack; /* just for convenience of asm trampoline */ + args->start_func = entry; + args->start_arg = arg; + args->tls_base = (void *)new_tls_base; + + if (!libc.threads_minus_1++) + libc.need_locks = 1; + /* Instead of `__clone`, WASIP3 uses a host API to instantiate a new version + * of the current module and start executing the entry function. */ + ret = wasip3_thread_new_indirect(__wasi_coop_thread_start, (void *)args); + + new->tid = ret; + + // Link the new thread into the thread list + if (ret >= 0) { + new->next = self->next; + new->prev = self; + new->next->prev = new; + new->prev->next = new; + } else { + if (!--libc.threads_minus_1) + libc.need_locks = 0; + } + + if (ret < 0) { + free(map); + return -ret; + } + + // Eagerly run the newly-created thread + // TODO(wasip3): make this configurable, perhaps through scheduler options? + wasip3_thread_yield_to_suspended(new->tid); + + *res = new; + return 0; +fail: + return EAGAIN; +} + +// TODO(wasip3) add an alias for pthread_exit when we want to expose it to users +weak_alias(__pthread_create, pthread_create); diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_detach.c b/libc-top-half/musl/src/thread/coop-threads/pthread_detach.c new file mode 100644 index 000000000..b7c58a636 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_detach.c @@ -0,0 +1,22 @@ +#include "pthread_impl.h" +#include + +static int __pthread_detach(pthread_t t) +{ + int state = t->detach_state; + + // Detaching from an already-detached thread is an error + if (state == DT_DETACHED) { + return EINVAL; + } + + if (state == DT_EXITING || state == DT_EXITED) { + __pthread_join(t, 0); + } + + t->detach_state = DT_DETACHED; + return 0; +} + +weak_alias(__pthread_detach, pthread_detach); +weak_alias(__pthread_detach, thrd_detach); diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_getcpuclockid.c b/libc-top-half/musl/src/thread/coop-threads/pthread_getcpuclockid.c new file mode 100644 index 000000000..0bc217bda --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_getcpuclockid.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_getcpuclockid(pthread_t t, clockid_t *clockid) +{ + return ENOTSUP; +} diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_getname_np.c b/libc-top-half/musl/src/thread/coop-threads/pthread_getname_np.c new file mode 100644 index 000000000..946d03a5e --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_getname_np.c @@ -0,0 +1,7 @@ + +#include "pthread_impl.h" + +int pthread_getname_np(pthread_t thread, char *name, size_t len) +{ + return ENOTSUP; +} diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_getschedparam.c b/libc-top-half/musl/src/thread/coop-threads/pthread_getschedparam.c new file mode 100644 index 000000000..24a153328 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_getschedparam.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_getschedparam(pthread_t t, int *restrict policy, struct sched_param *restrict param) +{ + return ENOTSUP; +} \ No newline at end of file diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_join.c b/libc-top-half/musl/src/thread/coop-threads/pthread_join.c new file mode 100644 index 000000000..b8b8400d8 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_join.c @@ -0,0 +1,49 @@ +#define _GNU_SOURCE +#include "pthread_impl.h" + +static int __pthread_timedjoin_np(pthread_t t, void **res, const struct timespec *at) +{ + int state = t->detach_state; + + /* Cannot join a detached thread */ + if (state >= DT_DETACHED) { + return EINVAL; + } + + /* If already exited, just return the result */ + if (state == DT_EXITED) { + if (res) *res = t->result; + if (t->map_base) free(t->map_base); + return 0; + } + + /* Timeouts not supported for cooperative threading */ + if (at) { + return ETIMEDOUT; + } + + if (t->joiner_waiters) { + // Only one thread can wait to join at a time + return EINVAL; + } + + __waitlist_wait_on(&t->joiner_waiters); + + if (res) *res = t->result; + if (t->map_base) free(t->map_base); + return 0; +} + +int __pthread_join(pthread_t t, void **res) +{ + return __pthread_timedjoin_np(t, res, 0); +} + +static int __pthread_tryjoin_np(pthread_t t, void **res) +{ + return t->detach_state==DT_JOINABLE ? EBUSY : __pthread_join(t, res); +} + +weak_alias(__pthread_tryjoin_np, pthread_tryjoin_np); +weak_alias(__pthread_timedjoin_np, pthread_timedjoin_np); +weak_alias(__pthread_join, pthread_join); diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_kill.c b/libc-top-half/musl/src/thread/coop-threads/pthread_kill.c new file mode 100644 index 000000000..546c12af9 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_kill.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_kill(pthread_t t, int sig) +{ + return ENOTSUP; +} diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_mutex_consistent.c b/libc-top-half/musl/src/thread/coop-threads/pthread_mutex_consistent.c new file mode 100644 index 000000000..dcf4dc5c7 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_mutex_consistent.c @@ -0,0 +1,8 @@ +#include "pthread_impl.h" +#include "atomic.h" + +int pthread_mutex_consistent(pthread_mutex_t *m) +{ + // Robust mutexes are not supported in cooperative threading mode + return ENOTSUP; +} diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_mutex_lock.c b/libc-top-half/musl/src/thread/coop-threads/pthread_mutex_lock.c new file mode 100644 index 000000000..7038a707e --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_mutex_lock.c @@ -0,0 +1,31 @@ +#include "pthread_impl.h" + +int __pthread_mutex_lock(pthread_mutex_t *m) +{ + int tid = wasip3_thread_index(); + int type = m->_m_type & 15; + + /* Check for recursive lock */ + if (m->_m_lock == tid) { + if (type == PTHREAD_MUTEX_RECURSIVE) { + m->_m_count++; + return 0; + } + if (type == PTHREAD_MUTEX_ERRORCHECK) { + return EDEADLK; + } + /* Normal mutex: deadlock */ + __builtin_trap(); + } + + /* Wait until unlocked */ + while (m->_m_lock != 0) { + __waitlist_wait_on(&m->_m_waiters); + } + + m->_m_lock = tid; + m->_m_count = 1; + return 0; +} + +weak_alias(__pthread_mutex_lock, pthread_mutex_lock); \ No newline at end of file diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_mutex_setprioceiling.c b/libc-top-half/musl/src/thread/coop-threads/pthread_mutex_setprioceiling.c new file mode 100644 index 000000000..b0bc993c5 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_mutex_setprioceiling.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_mutex_setprioceiling(pthread_mutex_t *restrict m, int ceiling, int *restrict old) +{ + return ENOTSUP; +} diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_mutex_timedlock.c b/libc-top-half/musl/src/thread/coop-threads/pthread_mutex_timedlock.c new file mode 100644 index 000000000..7f3f4a612 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_mutex_timedlock.c @@ -0,0 +1,43 @@ +#include "pthread_impl.h" + +int __pthread_mutex_timedlock(pthread_mutex_t *restrict m, const struct timespec *restrict at) +{ + int tid = wasip3_thread_index(); + int type = m->_m_type & 15; + + /* Fast path: if available, acquire regardless of timeout */ + if (m->_m_lock == 0) { + m->_m_lock = tid; + m->_m_count = 1; + return 0; + } + + /* Check for recursive lock */ + if (m->_m_lock == tid) { + if (type == PTHREAD_MUTEX_RECURSIVE) { + m->_m_count++; + return 0; + } + if (type == PTHREAD_MUTEX_ERRORCHECK) { + return EDEADLK; + } + /* Normal mutex: deadlock */ + __builtin_trap(); + } + + /* Would need to block, but timeouts not supported */ + if (at) { + return ETIMEDOUT; + } + + /* Wait indefinitely */ + while (m->_m_lock != 0) { + __waitlist_wait_on(&m->_m_waiters); + } + + m->_m_lock = tid; + m->_m_count = 1; + return 0; +} + +weak_alias(__pthread_mutex_timedlock, pthread_mutex_timedlock); diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_mutex_trylock.c b/libc-top-half/musl/src/thread/coop-threads/pthread_mutex_trylock.c new file mode 100644 index 000000000..a412d2d90 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_mutex_trylock.c @@ -0,0 +1,33 @@ +#include "pthread_impl.h" + +int __pthread_mutex_trylock(pthread_mutex_t *m) +{ + int tid = wasip3_thread_index(); + int type = m->_m_type & 15; + + /* Check for recursive lock */ + if (m->_m_lock == tid) { + if (type == PTHREAD_MUTEX_RECURSIVE) { + if (m->_m_count >= INT_MAX) return EAGAIN; + m->_m_count++; + return 0; + } + if (type == PTHREAD_MUTEX_ERRORCHECK) { + return EBUSY; + } + /* Normal mutex: already locked */ + return EBUSY; + } + + /* Try to acquire the lock */ + if (m->_m_lock != 0) { + return EBUSY; + } + m->_m_lock = tid; + m->_m_count = 1; + + return 0; +} + + +weak_alias(__pthread_mutex_trylock, pthread_mutex_trylock); diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_mutex_unlock.c b/libc-top-half/musl/src/thread/coop-threads/pthread_mutex_unlock.c new file mode 100644 index 000000000..9b98cd2ca --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_mutex_unlock.c @@ -0,0 +1,30 @@ +#include "pthread_impl.h" + +int __pthread_mutex_unlock(pthread_mutex_t *m) +{ + int tid = wasip3_thread_index(); + int type = m->_m_type & 15; + + /* Check ownership */ + if (m->_m_lock != tid) { + if (type == PTHREAD_MUTEX_ERRORCHECK || type == PTHREAD_MUTEX_RECURSIVE) { + return EPERM; + } + /* Normal mutex: undefined behavior */ + __builtin_trap(); + } + + /* Handle recursive unlock */ + if (type == PTHREAD_MUTEX_RECURSIVE && m->_m_count > 1) { + m->_m_count--; + return 0; + } + + /* Unlock */ + m->_m_lock = 0; + m->_m_count = 0; + __waitlist_wake_one(&m->_m_waiters); + return 0; +} + +weak_alias(__pthread_mutex_unlock, pthread_mutex_unlock); diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_once.c b/libc-top-half/musl/src/thread/coop-threads/pthread_once.c new file mode 100644 index 000000000..28e7e22ca --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_once.c @@ -0,0 +1,43 @@ +#include "pthread_impl.h" +enum { + PTHREAD_ONCE_STATE_NOT_RUN = 0, + PTHREAD_ONCE_STATE_RUNNING = 1, + PTHREAD_ONCE_STATE_COMPLETED = 2 +}; + +static struct { + pthread_once_t *control; + struct __waitlist_node *waiters; +} once_state; + +int __pthread_once(pthread_once_t *control, void (*init)(void)) +{ + /* Fast path: already completed */ + if (*control == PTHREAD_ONCE_STATE_COMPLETED) { + return 0; + } + + /* Try to become the initializer */ + if (*control == PTHREAD_ONCE_STATE_NOT_RUN) { + *control = PTHREAD_ONCE_STATE_RUNNING; + once_state.control = control; + + /* init may do something that blocks, in which case other threads + * may try to run pthread_once on the same control. They will + * see state PTHREAD_ONCE_STATE_RUNNING and wait on our waitlist. */ + init(); + + *control = PTHREAD_ONCE_STATE_COMPLETED; + __waitlist_wake_all(&once_state.waiters); + return 0; + } + + /* Another thread is initializing, wait for completion */ + while (*control == PTHREAD_ONCE_STATE_RUNNING) { + __waitlist_wait_on(&once_state.waiters); + } + + return 0; +} + +weak_alias(__pthread_once, pthread_once); diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_rwlock_rdlock.c b/libc-top-half/musl/src/thread/coop-threads/pthread_rwlock_rdlock.c new file mode 100644 index 000000000..8546c07d2 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_rwlock_rdlock.c @@ -0,0 +1,8 @@ +#include "pthread_impl.h" + +int __pthread_rwlock_rdlock(pthread_rwlock_t *rw) +{ + return __pthread_rwlock_timedrdlock(rw, 0); +} + +weak_alias(__pthread_rwlock_rdlock, pthread_rwlock_rdlock); diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_rwlock_timedrdlock.c b/libc-top-half/musl/src/thread/coop-threads/pthread_rwlock_timedrdlock.c new file mode 100644 index 000000000..dcc1b7379 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_rwlock_timedrdlock.c @@ -0,0 +1,27 @@ +#include "pthread_impl.h" + +int __pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rw, const struct timespec *restrict at) +{ + /* Fast path: if no writer, increment readers */ + if (rw->_rw_lock >= 0) { + rw->_rw_lock++; + return 0; + } + + /* Would need to wait for writer, but timeouts not supported */ + if (at) { + errno = ENOSYS; + return -1; + } + + /* Wait until no writer holds the lock */ + while (rw->_rw_lock < 0) { + __waitlist_wait_on(&rw->_rw_waiters); + } + + /* Acquired, increment reader count */ + rw->_rw_lock++; + return 0; +} + +weak_alias(__pthread_rwlock_timedrdlock, pthread_rwlock_timedrdlock); diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_rwlock_timedwrlock.c b/libc-top-half/musl/src/thread/coop-threads/pthread_rwlock_timedwrlock.c new file mode 100644 index 000000000..0dd2317ba --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_rwlock_timedwrlock.c @@ -0,0 +1,33 @@ +#include "pthread_impl.h" + +int __pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rw, const struct timespec *restrict at) +{ + int tid = wasip3_thread_index(); + + /* Check for deadlock: trying to write-lock already owned write lock */ + if (rw->_rw_lock == -tid) { + return EDEADLK; + } + + /* Fast path: if no readers or writers, acquire write lock */ + if (rw->_rw_lock == 0) { + rw->_rw_lock = -tid; /* -tid indicates write lock owned by this thread */ + return 0; + } + + /* Would need to wait, but timeouts not supported */ + if (at) { + errno = ENOSYS; + return -1; + } + + /* Wait until no readers and no writers */ + while (rw->_rw_lock != 0) { + __waitlist_wait_on(&rw->_rw_waiters); + } + + /* Acquired, mark as write-locked with owner */ + rw->_rw_lock = -tid; + return 0; +} + diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_rwlock_tryrdlock.c b/libc-top-half/musl/src/thread/coop-threads/pthread_rwlock_tryrdlock.c new file mode 100644 index 000000000..3943e838e --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_rwlock_tryrdlock.c @@ -0,0 +1,13 @@ +#include "pthread_impl.h" + +int __pthread_rwlock_tryrdlock(pthread_rwlock_t *rw) +{ + /* If no writer holds the lock, increment reader count */ + if (rw->_rw_lock >= 0) { + rw->_rw_lock++; + return 0; + } + return EBUSY; +} + +weak_alias(__pthread_rwlock_tryrdlock, pthread_rwlock_tryrdlock); diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_rwlock_trywrlock.c b/libc-top-half/musl/src/thread/coop-threads/pthread_rwlock_trywrlock.c new file mode 100644 index 000000000..2f002cbd7 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_rwlock_trywrlock.c @@ -0,0 +1,20 @@ +#include "pthread_impl.h" + +int __pthread_rwlock_trywrlock(pthread_rwlock_t *rw) +{ + int tid = wasip3_thread_index(); + + /* Check for deadlock: trying to write-lock already owned write lock */ + if (rw->_rw_lock == -tid) { + return EDEADLK; + } + + /* Try to acquire write lock */ + if (rw->_rw_lock != 0) { + return EBUSY; + } + + rw->_rw_lock = -tid; + return 0; +} +weak_alias(__pthread_rwlock_trywrlock, pthread_rwlock_trywrlock); diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_rwlock_unlock.c b/libc-top-half/musl/src/thread/coop-threads/pthread_rwlock_unlock.c new file mode 100644 index 000000000..8ace05356 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_rwlock_unlock.c @@ -0,0 +1,25 @@ +#include "pthread_impl.h" + +int __pthread_rwlock_unlock(pthread_rwlock_t *rw) +{ + if (rw->_rw_lock > 0) { + /* Releasing read lock */ + rw->_rw_lock--; + if (rw->_rw_lock == 0) { + /* Last reader, wake waiting writers */ + __waitlist_wake_all(&rw->_rw_waiters); + } + } else if (rw->_rw_lock < 0) { + /* Releasing write lock (stored as -tid) */ + rw->_rw_lock = 0; + /* Wake all waiters (readers and writers compete) */ + __waitlist_wake_all(&rw->_rw_waiters); + } else { + /* Unlocking when not locked - trap */ + __builtin_trap(); + } + + return 0; +} + +weak_alias(__pthread_rwlock_unlock, pthread_rwlock_unlock); diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_rwlock_wrlock.c b/libc-top-half/musl/src/thread/coop-threads/pthread_rwlock_wrlock.c new file mode 100644 index 000000000..46a3b3a5b --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_rwlock_wrlock.c @@ -0,0 +1,8 @@ +#include "pthread_impl.h" + +int __pthread_rwlock_wrlock(pthread_rwlock_t *rw) +{ + return __pthread_rwlock_timedwrlock(rw, 0); +} + +weak_alias(__pthread_rwlock_wrlock, pthread_rwlock_wrlock); diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_setconcurrency.c b/libc-top-half/musl/src/thread/coop-threads/pthread_setconcurrency.c new file mode 100644 index 000000000..091abf98c --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_setconcurrency.c @@ -0,0 +1,9 @@ +#include +#include + +int pthread_setconcurrency(int val) +{ + if (val < 0) return EINVAL; + if (val > 0) return EAGAIN; + return 0; +} diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_setname_np.c b/libc-top-half/musl/src/thread/coop-threads/pthread_setname_np.c new file mode 100644 index 000000000..19ae3c7d1 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_setname_np.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_setname_np(pthread_t thread, const char *name) +{ + return ENOTSUP; +} diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_setschedparam.c b/libc-top-half/musl/src/thread/coop-threads/pthread_setschedparam.c new file mode 100644 index 000000000..d1e0d98ac --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_setschedparam.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_setschedparam(pthread_t t, int policy, const struct sched_param *param) +{ + return ENOTSUP; +} diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_setschedprio.c b/libc-top-half/musl/src/thread/coop-threads/pthread_setschedprio.c new file mode 100644 index 000000000..8c662c729 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_setschedprio.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_setschedprio(pthread_t t, int prio) +{ + return ENOTSUP; +} diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_sigmask.c b/libc-top-half/musl/src/thread/coop-threads/pthread_sigmask.c new file mode 100644 index 000000000..0aab2544e --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_sigmask.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +struct sigset_t; +int pthread_sigmask(int how, const sigset_t *restrict set, sigset_t *restrict old) +{ + return ENOTSUP; +} diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_spin_lock.c b/libc-top-half/musl/src/thread/coop-threads/pthread_spin_lock.c new file mode 100644 index 000000000..42073d47f --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_spin_lock.c @@ -0,0 +1,8 @@ +#include "pthread_impl.h" + +int pthread_spin_lock(pthread_spinlock_t *s) +{ + if (*s) return EDEADLK; + *s = EBUSY; + return 0; +} diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_spin_trylock.c b/libc-top-half/musl/src/thread/coop-threads/pthread_spin_trylock.c new file mode 100644 index 000000000..c3ffdc602 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_spin_trylock.c @@ -0,0 +1,8 @@ +#include "pthread_impl.h" + +int pthread_spin_trylock(pthread_spinlock_t *s) +{ + if (*s) return EBUSY; + *s = EBUSY; + return 0; +} diff --git a/libc-top-half/musl/src/thread/coop-threads/pthread_spin_unlock.c b/libc-top-half/musl/src/thread/coop-threads/pthread_spin_unlock.c new file mode 100644 index 000000000..f9d8a23dc --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/pthread_spin_unlock.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +int pthread_spin_unlock(pthread_spinlock_t *s) +{ + *s = 0; + return 0; +} diff --git a/libc-top-half/musl/src/thread/coop-threads/sem_destroy.c b/libc-top-half/musl/src/thread/coop-threads/sem_destroy.c new file mode 100644 index 000000000..f4aced5da --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/sem_destroy.c @@ -0,0 +1,6 @@ +#include + +int sem_destroy(sem_t *sem) +{ + return 0; +} diff --git a/libc-top-half/musl/src/thread/coop-threads/sem_getvalue.c b/libc-top-half/musl/src/thread/coop-threads/sem_getvalue.c new file mode 100644 index 000000000..bf66ed5d4 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/sem_getvalue.c @@ -0,0 +1,7 @@ +#include + +int sem_getvalue(sem_t *restrict sem, int *restrict valp) +{ + *valp = sem->__count; + return 0; +} diff --git a/libc-top-half/musl/src/thread/coop-threads/sem_init.c b/libc-top-half/musl/src/thread/coop-threads/sem_init.c new file mode 100644 index 000000000..ff0cba32b --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/sem_init.c @@ -0,0 +1,15 @@ +#include +#include +#include +#include + +int sem_init(sem_t *sem, int pshared, unsigned value) +{ + if (value > SEM_VALUE_MAX) { + errno = EINVAL; + return -1; + } + sem->__count = value; + sem->__waiters = NULL; + return 0; +} diff --git a/libc-top-half/musl/src/thread/coop-threads/sem_open.c b/libc-top-half/musl/src/thread/coop-threads/sem_open.c new file mode 100644 index 000000000..4645a774c --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/sem_open.c @@ -0,0 +1,13 @@ +#include +#include +#include + +sem_t *sem_open(const char *name, int flags, ...) +{ + return NULL; +} + +int sem_close(sem_t *sem) +{ + return ENOTSUP; +} diff --git a/libc-top-half/musl/src/thread/coop-threads/sem_post.c b/libc-top-half/musl/src/thread/coop-threads/sem_post.c new file mode 100644 index 000000000..03ff00c31 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/sem_post.c @@ -0,0 +1,23 @@ +#include +#include +#include "pthread_impl.h" + +int sem_post(sem_t *sem) +{ + int val = sem->__count; + + if (val == SEM_VALUE_MAX) { + errno = EOVERFLOW; + return -1; + } + + /* Increment count */ + sem->__count = val + 1; + + /* If count was negative (waiters present), wake one */ + if (val < 0) { + __waitlist_wake_one(&sem->__waiters); + } + + return 0; +} \ No newline at end of file diff --git a/libc-top-half/musl/src/thread/coop-threads/sem_timedwait.c b/libc-top-half/musl/src/thread/coop-threads/sem_timedwait.c new file mode 100644 index 000000000..ff0415a45 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/sem_timedwait.c @@ -0,0 +1,34 @@ +#include +#include +#include "pthread_impl.h" + +int sem_timedwait(sem_t *restrict sem, const struct timespec *restrict at) +{ + pthread_testcancel(); + + /* Fast path: if permit is available, take it regardless of timeout */ + if (sem->__count > 0) { + sem->__count--; + return 0; + } + + /* Would need to block, but timeouts not supported in cooperative threading. + * There's no way to interrupt a suspended thread after a timeout. */ + if (at) { + errno = ENOSYS; + return -1; + } + + /* Decrement count to indicate we're waiting (goes negative) */ + sem->__count--; + + /* Loop until a permit becomes available */ + while (sem->__count < 0) { + /* No permits available, wait on the waitlist */ + __waitlist_wait_on(&sem->__waiters); + /* After waking, recheck - another thread might have taken the permit */ + } + + /* We've been given a permit (count incremented by sem_post) */ + return 0; +} \ No newline at end of file diff --git a/libc-top-half/musl/src/thread/coop-threads/sem_trywait.c b/libc-top-half/musl/src/thread/coop-threads/sem_trywait.c new file mode 100644 index 000000000..eee581940 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/sem_trywait.c @@ -0,0 +1,13 @@ +#include +#include +#include "pthread_impl.h" + +int sem_trywait(sem_t *sem) +{ + if (sem->__count > 0) { + sem->__count--; + return 0; + } + errno = EAGAIN; + return -1; +} diff --git a/libc-top-half/musl/src/thread/coop-threads/sem_wait.c b/libc-top-half/musl/src/thread/coop-threads/sem_wait.c new file mode 100644 index 000000000..264194f97 --- /dev/null +++ b/libc-top-half/musl/src/thread/coop-threads/sem_wait.c @@ -0,0 +1,6 @@ +#include + +int sem_wait(sem_t *sem) +{ + return sem_timedwait(sem, 0); +} diff --git a/libc-top-half/musl/src/thread/wasi-threads/__wait.c b/libc-top-half/musl/src/thread/wasi-threads/__wait.c index 9484c0f3d..50a2b3510 100644 --- a/libc-top-half/musl/src/thread/wasi-threads/__wait.c +++ b/libc-top-half/musl/src/thread/wasi-threads/__wait.c @@ -64,3 +64,30 @@ void __wait(volatile int *addr, volatile int *waiters, int val, int priv) } if (waiters) a_dec(waiters); } + +void __wake(volatile void *addr, int cnt, int priv) +{ + if (priv) priv = FUTEX_PRIVATE; + if (cnt<0) cnt = INT_MAX; +#ifdef __wasilibc_unmodified_upstream + __syscall(SYS_futex, addr, FUTEX_WAKE|priv, cnt) != -ENOSYS || + __syscall(SYS_futex, addr, FUTEX_WAKE, cnt); +#else +#ifdef _REENTRANT + __builtin_wasm_memory_atomic_notify((int*)addr, cnt); +#else + (void) addr; +#endif +#endif +} + +void __futexwait(volatile void *addr, int val, int priv) +{ +#ifdef __wasilibc_unmodified_upstream + if (priv) priv = FUTEX_PRIVATE; + __syscall(SYS_futex, addr, FUTEX_WAIT|priv, val, 0) != -ENOSYS || + __syscall(SYS_futex, addr, FUTEX_WAIT, val, 0); +#else + __wait(addr, NULL, val, priv); +#endif +} \ No newline at end of file diff --git a/libc-top-half/musl/src/thread/wasi-threads/lock_ptc.c b/libc-top-half/musl/src/thread/wasi-threads/lock_ptc.c index 7adedab75..ec0e05bbe 100644 --- a/libc-top-half/musl/src/thread/wasi-threads/lock_ptc.c +++ b/libc-top-half/musl/src/thread/wasi-threads/lock_ptc.c @@ -15,4 +15,4 @@ void __acquire_ptc() void __release_ptc() { pthread_rwlock_unlock(&lock); -} +} \ No newline at end of file diff --git a/libc-top-half/musl/src/thread/wasi-threads/pthread_create.c b/libc-top-half/musl/src/thread/wasi-threads/pthread_create.c index 879b48c1f..f6ff846cf 100644 --- a/libc-top-half/musl/src/thread/wasi-threads/pthread_create.c +++ b/libc-top-half/musl/src/thread/wasi-threads/pthread_create.c @@ -118,7 +118,7 @@ static void __pthread_exit(void *result) * any use past this point would have undefined behavior, but for * joinable threads it's a valid usage that must be handled. * Signals must be blocked since pthread_kill must be AS-safe. */ - LOCK(self->killlock); + WEAK_LOCK(self->killlock); /* The thread list lock must be AS-safe, and thus depends on * application signals being blocked above. */ @@ -129,7 +129,7 @@ static void __pthread_exit(void *result) * signal state to prepare for exit to call atexit handlers. */ if (self->next == self) { __tl_unlock(); - UNLOCK(self->killlock); + WEAK_UNLOCK(self->killlock); self->detach_state = state; #ifdef __wasilibc_unmodified_upstream __restore_sigs(&set); @@ -147,7 +147,7 @@ static void __pthread_exit(void *result) * remaining locks (except thread list) are held if we end up * resetting need_locks below. */ self->tid = 0; - UNLOCK(self->killlock); + WEAK_UNLOCK(self->killlock); #ifdef __wasilibc_unmodified_upstream /* Process robust list in userspace to handle non-pshared mutexes diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e7aeae35a..67ad36a5b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -17,6 +17,19 @@ FetchContent_MakeAvailable(libc-test) set(LIBC_TEST "${libc-test_SOURCE_DIR}") message(STATUS "libc-test source directory: ${LIBC_TEST}") +# ======== Clone Open POSIX Test Suite ========================== + +FetchContent_Declare( + posix-testsuite + GIT_REPOSITORY https://github.com/bytecodealliance/open-posix-test-suite + GIT_TAG dc7c7d54fc4f6219bb4c7af348be2290335bf17e + GIT_SHALLOW true +) +FetchContent_MakeAvailable(posix-testsuite) +set(POSIX_TESTSUITE "${posix-testsuite_SOURCE_DIR}") +message(STATUS "Open POSIX Test Suite source directory: ${POSIX_TESTSUITE}") + + # ========= Download wasmtime as a test runner ================== include(ba-download) @@ -86,6 +99,10 @@ function(add_test_executable executable_name src) -Wl,--import-memory,--export-memory,--shared-memory,--max-memory=1073741824) endif() + if (WASI STREQUAL "p3") + target_link_options(${executable_name} PRIVATE -pthread) + endif() + if(arg_SHARED) set_pic(${executable_name}) # Skip wit-component when linking to manually run `wasm-tools component @@ -158,6 +175,11 @@ function(register_test test_name executable_name) if (TARGET_TRIPLE MATCHES "-threads") list(APPEND wasmtime_args --wasi threads --wasm shared-memory) endif() + if (WASI STREQUAL "p3") + list(APPEND wasmtime_args --wasm component-model-async) + list(APPEND wasmtime_args --wasm component-model-threading) + list(APPEND wasmtime_args --wasi p3) + endif() if (arg_SETJMP) list(APPEND wasmtime_args --wasm exceptions) target_compile_options(${executable_name} PRIVATE @@ -215,7 +237,7 @@ function(add_test_pair test_name test_file) add_test_executable(${test_name} ${test_file} ${ARGN}) register_test(${test_name} ${test_name} ${ARGN}) - if(NOT arg_NOSHARED AND BUILD_SHARED AND NOT WASI MATCHES "p1") + if(NOT arg_NOSHARED AND BUILD_SHARED AND NOT WASI MATCHES "p1" AND NOT WASI MATCHES "p3") add_test_executable("shared_${test_name}" ${test_file} SHARED ${ARGN}) register_test("shared_${test_name}" "shared_${test_name}" ${ARGN}) endif() @@ -514,6 +536,392 @@ if (TARGET_TRIPLE MATCHES "-threads") set_tests_properties(libc_test_functional_pthread_cond.wasm PROPERTIES LABELS v8fail) endif() + +# ========= Open POSIX Test Suite tests ======================== + +function(add_posix_test test_file) + cmake_path(REPLACE_EXTENSION test_file wasm OUTPUT_VARIABLE test_name) + string(REPLACE "/" "_" test_name ${test_name}) + set(test_name "open_posix_test_${test_name}") + set(test_file "${POSIX_TESTSUITE}/${test_file}") + + add_test_executable(${test_name} "${test_file}" ${ARGN} LDFLAGS -Wl,--export-table) + register_test(${test_name} ${test_name} ${ARGN}) + target_include_directories(${test_name} PRIVATE ${POSIX_TESTSUITE}/include) +endfunction() + +if (WASI STREQUAL "p3") + # unsupported(fork): conformance/interfaces/pthread_atfork/* + + add_posix_test(conformance/interfaces/pthread_attr_destroy/1-1.c) + add_posix_test(conformance/interfaces/pthread_attr_destroy/2-1.c) + add_posix_test(conformance/interfaces/pthread_attr_destroy/3-1.c) + + add_posix_test(conformance/interfaces/pthread_attr_getdetachstate/1-1.c) + add_posix_test(conformance/interfaces/pthread_attr_getdetachstate/1-2.c) + + # unsupported(scheduling): conformance/interfaces/pthread_attr_getinheritsched/* + # unsupported(scheduling): conformance/interfaces/pthread_attr_getschedparam/* + # unsupported(scheduling): conformance/interfaces/pthread_attr_getschedpolicy/* + # unsupported(scheduling): conformance/interfaces/pthread_attr_getscope/* + + add_posix_test(conformance/interfaces/pthread_attr_getstack/1-1.c) + add_posix_test(conformance/interfaces/pthread_attr_getstacksize/1-1.c) + + add_posix_test(conformance/interfaces/pthread_attr_init/1-1.c) + add_posix_test(conformance/interfaces/pthread_attr_init/2-1.c) + add_posix_test(conformance/interfaces/pthread_attr_init/3-1.c) + add_posix_test(conformance/interfaces/pthread_attr_init/4-1.c) + + add_posix_test(conformance/interfaces/pthread_attr_setdetachstate/1-1.c) + add_posix_test(conformance/interfaces/pthread_attr_setdetachstate/1-2.c) + add_posix_test(conformance/interfaces/pthread_attr_setdetachstate/2-1.c) + add_posix_test(conformance/interfaces/pthread_attr_setdetachstate/4-1.c) + + # unsupported(scheduling): conformance/interfaces/pthread_attr_setinheritsched/* + # unsupported(scheduling): conformance/interfaces/pthread_attr_setschedparam/* + # unsupported(scheduling): conformance/interfaces/pthread_attr_setschedpolicy/* + # unsupported(scheduling): conformance/interfaces/pthread_attr_setscope/* + + add_posix_test(conformance/interfaces/pthread_attr_setstack/1-1.c) + # bad test(untouched stack): conformance/interfaces/pthread_attr_setstack/2-1.c + add_posix_test(conformance/interfaces/pthread_attr_setstack/4-1.c) + # bad test(untouched stack): conformance/interfaces/pthread_attr_setstack/6-1.c + add_posix_test(conformance/interfaces/pthread_attr_setstack/7-1.c) + + add_posix_test(conformance/interfaces/pthread_attr_setstacksize/1-1.c) + # bad test(untouched stack): conformance/interfaces/pthread_attr_setstacksize/2-1.c + # bad test(stack/page size): conformance/interfaces/pthread_attr_setstacksize/4-1.c + + add_posix_test(conformance/interfaces/pthread_barrier_destroy/1-1.c) + # unsupported(cancellation): conformance/interfaces/pthread_barrier_destroy/2-1.c + add_posix_test(conformance/interfaces/pthread_barrier_init/1-1.c) + add_posix_test(conformance/interfaces/pthread_barrier_init/3-1.c) + # bad test(undefined behaviour): conformance/interfaces/pthread_barrier_init/4-1.c + add_posix_test(conformance/interfaces/pthread_barrier_wait/1-1.c) + add_posix_test(conformance/interfaces/pthread_barrier_wait/2-1.c) + # unsupported(signals): conformance/interfaces/pthread_barrier_wait/3-1.c + # unsupported(signals): conformance/interfaces/pthread_barrier_wait/3-2.c + # bad test(undefined behaviour): conformance/interfaces/pthread_barrier_wait/6-1.c + + # unsupported(process-shared): conformance/interfaces/pthread_barrierattr_destroy/* + # unsupported(process-shared): conformance/interfaces/pthread_barrierattr_getpshared/* + # unsupported(process-shared): conformance/interfaces/pthread_barrierattr_init/* + # unsupported(process-shared): conformance/interfaces/pthread_barrierattr_setpshared/* + + # unsupported(cancellation): conformance/interfaces/pthread_cancel/* + + # temporarily unsupported(clocks,pthread_exit): conformance/interfaces/pthread_cleanup_pop/1-1.c + # temporarily unsupported(clocks,pthread_exit): conformance/interfaces/pthread_cleanup_pop/1-2.c + # temporarily unsupported(pthread_exit): conformance/interfaces/pthread_cleanup_pop/1-3.c + + # temporarily unsupported(pthread_exit): conformance/interfaces/pthread_cleanup_push/1-1.c + # unsupported(cancellation): conformance/interfaces/pthread_cleanup_push/1-2.c + # unsupported(cancellation): conformance/interfaces/pthread_cleanup_push/1-3.c + + add_posix_test(conformance/interfaces/pthread_cond_broadcast/1-1.c) + # temporarily unsupported(timed waits): conformance/interfaces/pthread_cond_broadcast/1-2.c + add_posix_test(conformance/interfaces/pthread_cond_broadcast/2-1.c) + # temporarily unsupported(timed waits): conformance/interfaces/pthread_cond_broadcast/2-2.c + # unsupported(mmap): conformance/interfaces/pthread_cond_broadcast/2-3.c + add_posix_test(conformance/interfaces/pthread_cond_broadcast/4-1.c) + # unsupported(signals): conformance/interfaces/pthread_cond_broadcast/4-2.c + + add_posix_test(conformance/interfaces/pthread_cond_destroy/1-1.c) + # temporarily unsupported(timed waits): conformance/interfaces/pthread_cond_destroy/2-1.c + add_posix_test(conformance/interfaces/pthread_cond_destroy/3-1.c) + add_posix_test(conformance/interfaces/pthread_cond_destroy/speculative/4-1.c) + + add_posix_test(conformance/interfaces/pthread_cond_init/1-1.c) + # unsupported(set time): conformance/interfaces/pthread_cond_init/1-2.c + # unsupported(processes): conformance/interfaces/pthread_cond_init/1-3.c + add_posix_test(conformance/interfaces/pthread_cond_init/2-1.c) + # unsupported(set time): conformance/interfaces/pthread_cond_init/2-2.c + add_posix_test(conformance/interfaces/pthread_cond_init/3-1.c) + # unsupported(fork): conformance/interfaces/pthread_cond_init/4-1.c + # unsupported(signals): conformance/interfaces/pthread_cond_init/4-2.c + + add_posix_test(conformance/interfaces/pthread_cond_signal/1-1.c) + # temporarily unsupported(timed waits): conformance/interfaces/pthread_cond_signal/1-2.c + add_posix_test(conformance/interfaces/pthread_cond_signal/2-1.c) + # temporarily unsupported(timed waits): conformance/interfaces/pthread_cond_signal/2-2.c + add_posix_test(conformance/interfaces/pthread_cond_signal/4-1.c) + # unsupported(signals): conformance/interfaces/pthread_cond_signal/4-2.c + + # temporarily unsupported(timed wait): conformance/interfaces/pthread_cond_timedwait/1-1.c + # temporarily unsupported(timed wait): conformance/interfaces/pthread_cond_timedwait/2-1.c + # temporarily unsupported(timed wait): conformance/interfaces/pthread_cond_timedwait/2-2.c + # temporarily unsupported(timed wait): conformance/interfaces/pthread_cond_timedwait/2-3.c + # temporarily unsupported(timed wait): conformance/interfaces/pthread_cond_timedwait/2-4.c + # temporarily unsupported(timed wait): conformance/interfaces/pthread_cond_timedwait/2-5.c + # temporarily unsupported(timed wait): conformance/interfaces/pthread_cond_timedwait/2-6.c + # temporarily unsupported(timed wait): conformance/interfaces/pthread_cond_timedwait/2-7.c + # temporarily unsupported(timed wait): conformance/interfaces/pthread_cond_timedwait/3-1.c + # temporarily unsupported(timed wait): conformance/interfaces/pthread_cond_timedwait/4-1.c + # temporarily unsupported(timed wait): conformance/interfaces/pthread_cond_timedwait/4-2.c + # temporarily unsupported(timed wait): conformance/interfaces/pthread_cond_timedwait/4-3.c + + add_posix_test(conformance/interfaces/pthread_cond_wait/1-1.c) + add_posix_test(conformance/interfaces/pthread_cond_wait/2-1.c) + add_posix_test(conformance/interfaces/pthread_cond_wait/2-2.c) + # unsupported(cancellation): conformance/interfaces/pthread_cond_wait/2-3.c + # test needs modified(alarm waking): conformance/interfaces/pthread_cond_wait/3-1.c + # test needs modified(alarm waking): conformance/interfaces/pthread_cond_wait/4-1.c + + add_posix_test(conformance/interfaces/pthread_condattr_destroy/1-1.c) + add_posix_test(conformance/interfaces/pthread_condattr_destroy/2-1.c) + add_posix_test(conformance/interfaces/pthread_condattr_destroy/3-1.c) + add_posix_test(conformance/interfaces/pthread_condattr_destroy/4-1.c) + + add_posix_test(conformance/interfaces/pthread_condattr_getclock/1-1.c) + add_posix_test(conformance/interfaces/pthread_condattr_getclock/1-2.c) + + # unsupported(process-shared): conformance/interfaces/pthread_condattr_getpshared/* + + add_posix_test(conformance/interfaces/pthread_condattr_init/1-1.c) + add_posix_test(conformance/interfaces/pthread_condattr_init/3-1.c) + + add_posix_test(conformance/interfaces/pthread_condattr_setclock/1-1.c) + add_posix_test(conformance/interfaces/pthread_condattr_setclock/1-2.c) + # unsupported(getcpuclockid): conformance/interfaces/pthread_condattr_setclock/1-3.c + add_posix_test(conformance/interfaces/pthread_condattr_setclock/2-1.c) + + # unsupported(process-shared): conformance/interfaces/pthread_condattr_setpshared/* + + add_posix_test(conformance/interfaces/pthread_create/1-1.c) + # unsupported(cancellation): conformance/interfaces/pthread_create/1-2.c + # unsupported(signals): conformance/interfaces/pthread_create/1-3.c + # unsupported(processes): conformance/interfaces/pthread_create/1-4.c + # unsupported(fork): add_posix_test(conformance/interfaces/pthread_create/1-5.c + # unsupported(scheduling): conformance/interfaces/pthread_create/1-6.c + # unsupported(signals): add_posix_test(conformance/interfaces/pthread_create/10-1.c) + # bad test(incorrect assumption):conformance/interfaces/pthread_create/11-1.c + add_posix_test(conformance/interfaces/pthread_create/12-1.c) + # unsupported(signals): conformance/interfaces/pthread_create/14-1.c + add_posix_test(conformance/interfaces/pthread_create/15-1.c) + add_posix_test(conformance/interfaces/pthread_create/2-1.c) + add_posix_test(conformance/interfaces/pthread_create/3-1.c) + # unsupported(fork): conformance/interfaces/pthread_create/3-2.c + add_posix_test(conformance/interfaces/pthread_create/4-1.c) + add_posix_test(conformance/interfaces/pthread_create/5-1.c) + add_posix_test(conformance/interfaces/pthread_create/5-2.c) + # unsupported(signals): conformance/interfaces/pthread_create/8-1.c + # unsupported(signals): conformance/interfaces/pthread_create/8-2.c + + add_posix_test(conformance/interfaces/pthread_detach/1-1.c) + add_posix_test(conformance/interfaces/pthread_detach/1-2.c) + add_posix_test(conformance/interfaces/pthread_detach/2-1.c) + add_posix_test(conformance/interfaces/pthread_detach/2-2.c) + add_posix_test(conformance/interfaces/pthread_detach/3-1.c) + add_posix_test(conformance/interfaces/pthread_detach/4-1.c) + # bad test(invalid assumption): conformance/interfaces/pthread_detach/4-2.c + # unsupported(signals): conformance/interfaces/pthread_detach/4-3.c + + add_posix_test(conformance/interfaces/pthread_equal/1-1.c) + add_posix_test(conformance/interfaces/pthread_equal/1-2.c) + # unsupported(signals): conformance/interfaces/pthread_equal/2-1.c + + # temporarily unsupported(pthread_exit) conformance/interfaces/pthread_exit/* + # unsupported(fork): conformance/interfaces/pthread_exit/6-1.c + + # unsupported(getcpuclockid): conformance/interfaces/pthread_getcpuclockid/* + # unsupported(scheduling): conformance/interfaces/pthread_getschedparam/* + + add_posix_test(conformance/interfaces/pthread_getspecific/1-1.c) + add_posix_test(conformance/interfaces/pthread_getspecific/3-1.c) + + add_posix_test(conformance/interfaces/pthread_join/1-1.c) + add_posix_test(conformance/interfaces/pthread_join/1-2.c) + add_posix_test(conformance/interfaces/pthread_join/2-1.c) + # unsupported(cancellation): conformance/interfaces/pthread_join/3-1.c + # unsupported(cancellation): conformance/interfaces/pthread_join/4-1.c + add_posix_test(conformance/interfaces/pthread_join/5-1.c) + # bad test(undefined behaviour): conformance/interfaces/pthread_join/6-2.c + # unsupported(signals): conformance/interfaces/pthread_join/6-3.c + add_posix_test(conformance/interfaces/pthread_join/speculative/6-1.c) + + add_posix_test(conformance/interfaces/pthread_key_create/1-1.c) + add_posix_test(conformance/interfaces/pthread_key_create/1-2.c) + add_posix_test(conformance/interfaces/pthread_key_create/2-1.c) + add_posix_test(conformance/interfaces/pthread_key_create/3-1.c) + add_posix_test(conformance/interfaces/pthread_key_create/speculative/5-1.c) + + add_posix_test(conformance/interfaces/pthread_key_delete/1-1.c) + add_posix_test(conformance/interfaces/pthread_key_delete/1-2.c) + add_posix_test(conformance/interfaces/pthread_key_delete/2-1.c) + + # unsupported(signals): conformance/interfaces/pthread_kill/* + + add_posix_test(conformance/interfaces/pthread_mutex_destroy/1-1.c) + add_posix_test(conformance/interfaces/pthread_mutex_destroy/2-1.c) + add_posix_test(conformance/interfaces/pthread_mutex_destroy/2-2.c) + add_posix_test(conformance/interfaces/pthread_mutex_destroy/3-1.c) + add_posix_test(conformance/interfaces/pthread_mutex_destroy/5-1.c) + add_posix_test(conformance/interfaces/pthread_mutex_destroy/5-2.c) + add_posix_test(conformance/interfaces/pthread_mutex_destroy/speculative/4-2.c) + + # unsupported(scheduling): conformance/interfaces/pthread_mutex_getprioceiling/* + + add_posix_test(conformance/interfaces/pthread_mutex_init/1-1.c) + # unsupported(cancellation): conformance/interfaces/pthread_mutex_init/1-2.c + add_posix_test(conformance/interfaces/pthread_mutex_init/2-1.c) + add_posix_test(conformance/interfaces/pthread_mutex_init/3-1.c) + # unsupported(cancellation): conformance/interfaces/pthread_mutex_init/3-2.c + add_posix_test(conformance/interfaces/pthread_mutex_init/4-1.c) + # unsupported(fork): add_posix_test(conformance/interfaces/pthread_mutex_init/5-1.c + # unsupported(signals): conformance/interfaces/pthread_mutex_init/5-3.c + # unsupported(privileges): conformance/interfaces/pthread_mutex_init/speculative/5-2.c + + add_posix_test(conformance/interfaces/pthread_mutex_lock/1-1.c) + add_posix_test(conformance/interfaces/pthread_mutex_lock/2-1.c) + # unsupported(signals): add_posix_test(conformance/interfaces/pthread_mutex_lock/3-1.c + add_posix_test(conformance/interfaces/pthread_mutex_lock/4-1.c) + # unsupported(signals): add_posix_test(conformance/interfaces/pthread_mutex_lock/5-1.c + + # temporarily unsupported(timed lock): conformance/interfaces/pthread_mutex_timedlock/1-1.c + # temporarily unsupported(timed lock): conformance/interfaces/pthread_mutex_timedlock/2-1.c + # temporarily unsupported(timed lock): conformance/interfaces/pthread_mutex_timedlock/4-1.c + # temporarily unsupported(timed lock): conformance/interfaces/pthread_mutex_timedlock/5-1.c + # temporarily unsupported(timed lock): conformance/interfaces/pthread_mutex_timedlock/5-2.c + # temporarily unsupported(timed lock): conformance/interfaces/pthread_mutex_timedlock/5-3.c + + add_posix_test(conformance/interfaces/pthread_mutex_trylock/1-1.c) + add_posix_test(conformance/interfaces/pthread_mutex_trylock/1-2.c) + add_posix_test(conformance/interfaces/pthread_mutex_trylock/2-1.c) + add_posix_test(conformance/interfaces/pthread_mutex_trylock/3-1.c) + add_posix_test(conformance/interfaces/pthread_mutex_trylock/4-1.c) + add_posix_test(conformance/interfaces/pthread_mutex_trylock/4-2.c) + # unsupported(signals): conformance/interfaces/pthread_mutex_trylock/4-3.c + + add_posix_test(conformance/interfaces/pthread_mutex_unlock/1-1.c) + add_posix_test(conformance/interfaces/pthread_mutex_unlock/2-1.c) + add_posix_test(conformance/interfaces/pthread_mutex_unlock/3-1.c) + add_posix_test(conformance/interfaces/pthread_mutex_unlock/5-1.c) + add_posix_test(conformance/interfaces/pthread_mutex_unlock/5-2.c) + + add_posix_test(conformance/interfaces/pthread_mutexattr_destroy/1-1.c) + add_posix_test(conformance/interfaces/pthread_mutexattr_destroy/2-1.c) + add_posix_test(conformance/interfaces/pthread_mutexattr_destroy/3-1.c) + add_posix_test(conformance/interfaces/pthread_mutexattr_destroy/4-1.c) + + # unsupported(scheduling): conformance/interfaces/pthread_mutexattr_getprioceiling/* + + add_posix_test(conformance/interfaces/pthread_mutexattr_getprotocol/1-1.c) + # unsupported(scheduling): conformance/interfaces/pthread_mutexattr_getprotocol/1-2.c + + # unsupported(process-shared): conformance/interfaces/pthread_mutexattr_getpshared/* + + add_posix_test(conformance/interfaces/pthread_mutexattr_gettype/1-1.c) + add_posix_test(conformance/interfaces/pthread_mutexattr_gettype/1-2.c) + add_posix_test(conformance/interfaces/pthread_mutexattr_gettype/1-3.c) + add_posix_test(conformance/interfaces/pthread_mutexattr_gettype/1-4.c) + add_posix_test(conformance/interfaces/pthread_mutexattr_gettype/1-5.c) + # bad test(assumes layout): conformance/interfaces/pthread_mutexattr_gettype/speculative/3-1.c + + add_posix_test(conformance/interfaces/pthread_mutexattr_init/1-1.c) + add_posix_test(conformance/interfaces/pthread_mutexattr_init/3-1.c) + + # unsupported(scheduling): conformance/interfaces/pthread_mutexattr_setprioceiling/* + + # unsupported(scheduling): conformance/interfaces/pthread_mutexattr_setprotocol/1-1.c + add_posix_test(conformance/interfaces/pthread_mutexattr_setprotocol/3-1.c) + add_posix_test(conformance/interfaces/pthread_mutexattr_setprotocol/3-2.c) + + # unsupported(process-shared): conformance/interfaces/pthread_mutexattr_setpshared/* + + add_posix_test(conformance/interfaces/pthread_mutexattr_settype/1-1.c) + # bad test(undefined behaviour): conformance/interfaces/pthread_mutexattr_settype/2-1.c + add_posix_test(conformance/interfaces/pthread_mutexattr_settype/3-1.c) + add_posix_test(conformance/interfaces/pthread_mutexattr_settype/3-2.c) + add_posix_test(conformance/interfaces/pthread_mutexattr_settype/3-3.c) + add_posix_test(conformance/interfaces/pthread_mutexattr_settype/3-4.c) + add_posix_test(conformance/interfaces/pthread_mutexattr_settype/7-1.c) + + add_posix_test(conformance/interfaces/pthread_once/1-1.c) + add_posix_test(conformance/interfaces/pthread_once/1-2.c) + add_posix_test(conformance/interfaces/pthread_once/1-3.c) + add_posix_test(conformance/interfaces/pthread_once/2-1.c) + # unsupported(cancellation): conformance/interfaces/pthread_once/3-1.c + add_posix_test(conformance/interfaces/pthread_once/4-1.c) + # unsupported(signals): conformance/interfaces/pthread_once/6-1.c + + add_posix_test(conformance/interfaces/pthread_rwlock_destroy/1-1.c) + add_posix_test(conformance/interfaces/pthread_rwlock_destroy/3-1.c) + + add_posix_test(conformance/interfaces/pthread_rwlock_init/1-1.c) + add_posix_test(conformance/interfaces/pthread_rwlock_init/2-1.c) + add_posix_test(conformance/interfaces/pthread_rwlock_init/3-1.c) + add_posix_test(conformance/interfaces/pthread_rwlock_init/6-1.c) + + add_posix_test(conformance/interfaces/pthread_rwlock_rdlock/1-1.c) + # unsupported(scheduling): conformance/interfaces/pthread_rwlock_rdlock/2-1.c + # unsupported(scheduling): conformance/interfaces/pthread_rwlock_rdlock/2-2.c + # unsupported(scheduling): conformance/interfaces/pthread_rwlock_rdlock/2-3.c + # unsupported(signals): conformance/interfaces/pthread_rwlock_rdlock/4-1.c + add_posix_test(conformance/interfaces/pthread_rwlock_rdlock/5-1.c) + + # temporarily unsupported(timed lock): conformance/interfaces/pthread_rwlock_timedrdlock/1-1.c + # temporarily unsupported(timed lock): conformance/interfaces/pthread_rwlock_timedrdlock/2-1.c + # temporarily unsupported(timed lock): conformance/interfaces/pthread_rwlock_timedrdlock/3-1.c + # temporarily unsupported(timed lock): conformance/interfaces/pthread_rwlock_timedrdlock/5-1.c + # temporarily unsupported(timed lock): conformance/interfaces/pthread_rwlock_timedrdlock/6-1.c + # temporarily unsupported(timed lock): conformance/interfaces/pthread_rwlock_timedrdlock/6-2.c + + # temporarily unsupported(timed lock): conformance/interfaces/pthread_rwlock_timedwrlock/1-1.c + # temporarily unsupported(timed lock): conformance/interfaces/pthread_rwlock_timedwrlock/2-1.c + # temporarily unsupported(timed lock): conformance/interfaces/pthread_rwlock_timedwrlock/3-1.c + # temporarily unsupported(timed lock): conformance/interfaces/pthread_rwlock_timedwrlock/5-1.c + # temporarily unsupported(timed lock): conformance/interfaces/pthread_rwlock_timedwrlock/6-1.c + # temporarily unsupported(timed lock): conformance/interfaces/pthread_rwlock_timedwrlock/6-2.c + + add_posix_test(conformance/interfaces/pthread_rwlock_tryrdlock/1-1.c) + + add_posix_test(conformance/interfaces/pthread_rwlock_trywrlock/1-1.c) + add_posix_test(conformance/interfaces/pthread_rwlock_trywrlock/speculative/3-1.c) + + add_posix_test(conformance/interfaces/pthread_rwlock_unlock/1-1.c) + add_posix_test(conformance/interfaces/pthread_rwlock_unlock/2-1.c) + # unsupported(scheduling): conformance/interfaces/pthread_rwlock_unlock/3-1.c + # bad test(undefined behaviour): conformance/interfaces/pthread_rwlock_unlock/4-1.c + # bad test(undefined behaviour): conformance/interfaces/pthread_rwlock_unlock/4-2.c + + add_posix_test(conformance/interfaces/pthread_rwlock_wrlock/1-1.c) + # unsupported(signals): conformance/interfaces/pthread_rwlock_wrlock/2-1.c + add_posix_test(conformance/interfaces/pthread_rwlock_wrlock/3-1.c) + + add_posix_test(conformance/interfaces/pthread_rwlockattr_destroy/1-1.c) + add_posix_test(conformance/interfaces/pthread_rwlockattr_destroy/2-1.c) + + # unsupported(process-shared): conformance/interfaces/pthread_rwlockattr_getpshared/* + + add_posix_test(conformance/interfaces/pthread_rwlockattr_init/1-1.c) + add_posix_test(conformance/interfaces/pthread_rwlockattr_init/2-1.c) + + # unsupported(process-shared): conformance/interfaces/pthread_rwlockattr_setpshared/* + + add_posix_test(conformance/interfaces/pthread_self/1-1.c) + + # unsupported(cancellation): conformance/interfaces/pthread_setcancelstate/* + + # unsupported(cancellation): conformance/interfaces/pthread_setcanceltype/* + + # unsupported(scheduling): conformance/interfaces/pthread_setschedparam/* + # unsupported(scheduling): conformance/interfaces/pthread_setschedprio/* + + add_posix_test(conformance/interfaces/pthread_setspecific/1-1.c) + add_posix_test(conformance/interfaces/pthread_setspecific/1-2.c) + + # unsupported(signals): conformance/interfaces/pthread_sigmask/* + + # unsupported(spinlocks): conformance/interfaces/pthread_spin_destroy/* + # unsupported(spinlocks): conformance/interfaces/pthread_spin_init/* + # unsupported(spinlocks): conformance/interfaces/pthread_spin_lock/* + # unsupported(spinlocks): conformance/interfaces/pthread_spin_trylock/* + # unsupported(spinlocks): conformance/interfaces/pthread_spin_unlock/* + + # unsupported(cancellation): conformance/interfaces/pthread_testcancel/* +endif() + # If enabled add a copy of Python which is built against `wasi-libc` and run # its tests. if (PYTHON_TESTS) diff --git a/test/socket-test.cmake b/test/socket-test.cmake index 91bd40a5f..37a76d62c 100644 --- a/test/socket-test.cmake +++ b/test/socket-test.cmake @@ -36,13 +36,13 @@ read port # read the first line of stdin from the previous process echo $port # forward this line to the next process, or out to cmake itself exec 2>&1 # close our stdout and replace it with stderr cat <&0 & # forward the rest of stdin to stderr so it shows up in cmake -exec ${ENGINE} -Wcomponent-model-async -Sp3,inherit-network ${CLIENT} \"$port\" +exec ${ENGINE} -Wcomponent-model-async -Wcomponent-model-threading -Sp3,inherit-network ${CLIENT} \"$port\" ") list(APPEND CLIENTS COMMAND bash -c ${client_script}) endforeach() execute_process( - COMMAND ${ENGINE} -Wcomponent-model-async -Sp3,inherit-network ${SERVER} + COMMAND ${ENGINE} -Wcomponent-model-async -Wcomponent-model-threading -Sp3,inherit-network ${SERVER} ${CLIENTS} TIMEOUT 5 COMMAND_ERROR_IS_FATAL ANY