From c97c85586da142df46fd2f8eef0dafc55def8568 Mon Sep 17 00:00:00 2001 From: Catherine Date: Sat, 26 Jul 2025 16:12:15 +0000 Subject: [PATCH] Refactor stub pthread library and enable unconditionally. This commit changes the layout of the stub pthread library to match that of libc-top-half: splits it into (mostly) one file per function, and ensures the same API is exposed from both libraries, adding stub functions as necessary. This commit also removes `-lwasi-emulated-pthread` as well as `-D_WASI_EMULATED_PTHREAD` and enables this functionality unconditionally, seeing as this is necessary for building libc++ with threading enabled. It adds a flag `-D_WASI_STRICT_PTHREAD` that causes thread creation functions (`pthread_create`, `pthread_detach`, `pthread_join`, `pthread_tryjoin_np`, `pthread_timedjoin_np`) to be defined as a macro causing a compile error, which can be used to locate the parts of a codebase requiring changes for the single thread model. Specifically, after this commit, the two targets `wasm32-wasip1` and `wasm32-wasip1-threads` differ as follows (cleaned up for clarity): --- expected/wasm32-wasip1/defined-symbols.txt +++ expected/wasm32-wasip1-threads/defined-symbols.txt +flockfile +ftrylockfile +funlockfile +sem_destroy +sem_getvalue +sem_init +sem_post +sem_timedwait +sem_trywait +sem_wait +wasi_thread_start --- expected/wasm32-wasip1/predefined-macros.txt +++ expected/wasm32-wasip1-threads/predefined-macros.txt +#define _REENTRANT 1 +#define SEM_NSEMS_MAX 256 +#define SEM_VALUE_MAX 0x7fffffff +#define __wasm_atomics__ 1 +#define __wasm_bulk_memory__ 1 --- Makefile | 60 ++++++++++-------- expected/wasm32-wasip1/defined-symbols.txt | 7 +- expected/wasm32-wasip1/predefined-macros.txt | 9 ++- expected/wasm32-wasip2/defined-symbols.txt | 7 +- expected/wasm32-wasip2/predefined-macros.txt | 9 ++- libc-top-half/musl/include/pthread.h | 25 ++------ libc-top-half/musl/include/unistd.h | 4 +- stub-pthreads/README.md | 16 ----- stub-pthreads/barrier.c | 23 ------- stub-pthreads/condvar.c | 36 ----------- stub-pthreads/mutex.c | 67 -------------------- stub-pthreads/rwlock.c | 60 ------------------ stub-pthreads/spinlock.c | 21 ------ stub-pthreads/stub-pthreads-emulated.c | 40 ------------ stub-pthreads/stub-pthreads-good.c | 43 ------------- thread-stub/README.md | 7 ++ thread-stub/pthread_barrier_destroy.c | 6 ++ thread-stub/pthread_barrier_init.c | 8 +++ thread-stub/pthread_barrier_wait.c | 7 ++ thread-stub/pthread_cond_broadcast.c | 6 ++ thread-stub/pthread_cond_destroy.c | 6 ++ thread-stub/pthread_cond_init.c | 6 ++ thread-stub/pthread_cond_signal.c | 6 ++ thread-stub/pthread_cond_timedwait.c | 13 ++++ thread-stub/pthread_cond_wait.c | 9 +++ thread-stub/pthread_create.c | 19 ++++++ thread-stub/pthread_detach.c | 13 ++++ thread-stub/pthread_getattr_np.c | 11 ++++ thread-stub/pthread_join.c | 32 ++++++++++ thread-stub/pthread_mutex_consistent.c | 8 +++ thread-stub/pthread_mutex_getprioceiling.c | 6 ++ thread-stub/pthread_mutex_lock.c | 21 ++++++ thread-stub/pthread_mutex_timedlock.c | 10 +++ thread-stub/pthread_mutex_trylock.c | 21 ++++++ thread-stub/pthread_mutex_unlock.c | 10 +++ thread-stub/pthread_once.c | 12 ++++ thread-stub/pthread_rwlock_rdlock.c | 11 ++++ thread-stub/pthread_rwlock_timedrdlock.c | 8 +++ thread-stub/pthread_rwlock_timedwrlock.c | 8 +++ thread-stub/pthread_rwlock_tryrdlock.c | 11 ++++ thread-stub/pthread_rwlock_trywrlock.c | 10 +++ thread-stub/pthread_rwlock_unlock.c | 12 ++++ thread-stub/pthread_rwlock_wrlock.c | 10 +++ thread-stub/pthread_spin_lock.c | 8 +++ thread-stub/pthread_spin_trylock.c | 8 +++ thread-stub/pthread_spin_unlock.c | 7 ++ 46 files changed, 388 insertions(+), 369 deletions(-) delete mode 100644 stub-pthreads/README.md delete mode 100644 stub-pthreads/barrier.c delete mode 100644 stub-pthreads/condvar.c delete mode 100644 stub-pthreads/mutex.c delete mode 100644 stub-pthreads/rwlock.c delete mode 100644 stub-pthreads/spinlock.c delete mode 100644 stub-pthreads/stub-pthreads-emulated.c delete mode 100644 stub-pthreads/stub-pthreads-good.c create mode 100644 thread-stub/README.md create mode 100644 thread-stub/pthread_barrier_destroy.c create mode 100644 thread-stub/pthread_barrier_init.c create mode 100644 thread-stub/pthread_barrier_wait.c create mode 100644 thread-stub/pthread_cond_broadcast.c create mode 100644 thread-stub/pthread_cond_destroy.c create mode 100644 thread-stub/pthread_cond_init.c create mode 100644 thread-stub/pthread_cond_signal.c create mode 100644 thread-stub/pthread_cond_timedwait.c create mode 100644 thread-stub/pthread_cond_wait.c create mode 100644 thread-stub/pthread_create.c create mode 100644 thread-stub/pthread_detach.c create mode 100644 thread-stub/pthread_getattr_np.c create mode 100644 thread-stub/pthread_join.c create mode 100644 thread-stub/pthread_mutex_consistent.c create mode 100644 thread-stub/pthread_mutex_getprioceiling.c create mode 100644 thread-stub/pthread_mutex_lock.c create mode 100644 thread-stub/pthread_mutex_timedlock.c create mode 100644 thread-stub/pthread_mutex_trylock.c create mode 100644 thread-stub/pthread_mutex_unlock.c create mode 100644 thread-stub/pthread_once.c create mode 100644 thread-stub/pthread_rwlock_rdlock.c create mode 100644 thread-stub/pthread_rwlock_timedrdlock.c create mode 100644 thread-stub/pthread_rwlock_timedwrlock.c create mode 100644 thread-stub/pthread_rwlock_tryrdlock.c create mode 100644 thread-stub/pthread_rwlock_trywrlock.c create mode 100644 thread-stub/pthread_rwlock_unlock.c create mode 100644 thread-stub/pthread_rwlock_wrlock.c create mode 100644 thread-stub/pthread_spin_lock.c create mode 100644 thread-stub/pthread_spin_trylock.c create mode 100644 thread-stub/pthread_spin_unlock.c diff --git a/Makefile b/Makefile index 1d44df029..26bdcfa9b 100644 --- a/Makefile +++ b/Makefile @@ -71,7 +71,7 @@ DLMALLOC_SOURCES = $(DLMALLOC_SRC_DIR)/dlmalloc.c DLMALLOC_INC = $(DLMALLOC_DIR)/include EMMALLOC_DIR = emmalloc EMMALLOC_SOURCES = $(EMMALLOC_DIR)/emmalloc.c -STUB_PTHREADS_DIR = stub-pthreads +THREAD_STUB_DIR = thread-stub LIBC_BOTTOM_HALF_DIR = libc-bottom-half LIBC_BOTTOM_HALF_CLOUDLIBC_SRC = $(LIBC_BOTTOM_HALF_DIR)/cloudlibc/src LIBC_BOTTOM_HALF_CLOUDLIBC_SRC_INC = $(LIBC_BOTTOM_HALF_CLOUDLIBC_SRC)/include @@ -138,8 +138,6 @@ LIBWASI_EMULATED_SIGNAL_SOURCES = \ LIBWASI_EMULATED_SIGNAL_MUSL_SOURCES = \ $(LIBC_TOP_HALF_MUSL_SRC_DIR)/signal/psignal.c \ $(LIBC_TOP_HALF_MUSL_SRC_DIR)/string/strsignal.c -LIBWASI_EMULATED_PTHREAD_SOURCES = \ - $(STUB_PTHREADS_DIR)/stub-pthreads-emulated.c LIBDL_SOURCES = $(LIBC_TOP_HALF_MUSL_SRC_DIR)/misc/dl.c LIBSETJMP_SOURCES = $(LIBC_TOP_HALF_MUSL_SRC_DIR)/setjmp/wasm32/rt.c LIBC_BOTTOM_HALF_CRT_SOURCES = $(wildcard $(LIBC_BOTTOM_HALF_DIR)/crt/*.c) @@ -373,12 +371,38 @@ endif ifeq ($(THREAD_MODEL), single) # pthreads stubs for single-threaded environment LIBC_TOP_HALF_MUSL_SOURCES += \ - $(STUB_PTHREADS_DIR)/barrier.c \ - $(STUB_PTHREADS_DIR)/condvar.c \ - $(STUB_PTHREADS_DIR)/mutex.c \ - $(STUB_PTHREADS_DIR)/rwlock.c \ - $(STUB_PTHREADS_DIR)/spinlock.c \ - $(STUB_PTHREADS_DIR)/stub-pthreads-good.c + $(addprefix $(THREAD_STUB_DIR)/, \ + pthread_barrier_destroy.c \ + pthread_barrier_init.c \ + pthread_barrier_wait.c \ + pthread_cond_broadcast.c \ + pthread_cond_destroy.c \ + pthread_cond_init.c \ + pthread_cond_signal.c \ + pthread_cond_timedwait.c \ + pthread_cond_wait.c \ + pthread_create.c \ + pthread_detach.c \ + pthread_getattr_np.c \ + pthread_join.c \ + pthread_mutex_consistent.c \ + pthread_mutex_getprioceiling.c \ + pthread_mutex_lock.c \ + pthread_mutex_timedlock.c \ + pthread_mutex_trylock.c \ + pthread_mutex_unlock.c \ + pthread_once.c \ + pthread_rwlock_rdlock.c \ + pthread_rwlock_timedrdlock.c \ + pthread_rwlock_timedwrlock.c \ + pthread_rwlock_tryrdlock.c \ + pthread_rwlock_trywrlock.c \ + pthread_rwlock_unlock.c \ + pthread_rwlock_wrlock.c \ + pthread_spin_lock.c \ + pthread_spin_trylock.c \ + pthread_spin_unlock.c \ + ) endif MUSL_PRINTSCAN_SOURCES = \ @@ -505,7 +529,6 @@ LIBWASI_EMULATED_PROCESS_CLOCKS_OBJS = $(call objs,$(LIBWASI_EMULATED_PROCESS_CL LIBWASI_EMULATED_GETPID_OBJS = $(call objs,$(LIBWASI_EMULATED_GETPID_SOURCES)) LIBWASI_EMULATED_SIGNAL_OBJS = $(call objs,$(LIBWASI_EMULATED_SIGNAL_SOURCES)) LIBWASI_EMULATED_SIGNAL_MUSL_OBJS = $(call objs,$(LIBWASI_EMULATED_SIGNAL_MUSL_SOURCES)) -LIBWASI_EMULATED_PTHREAD_OBJS = $(call objs,$(LIBWASI_EMULATED_PTHREAD_SOURCES)) LIBDL_OBJS = $(call objs,$(LIBDL_SOURCES)) LIBSETJMP_OBJS = $(call objs,$(LIBSETJMP_SOURCES)) LIBC_BOTTOM_HALF_CRT_OBJS = $(call objs,$(LIBC_BOTTOM_HALF_CRT_SOURCES)) @@ -529,7 +552,6 @@ LIBWASI_EMULATED_PROCESS_CLOCKS_SO_OBJS = $(patsubst %.o,%.pic.o,$(LIBWASI_EMULA LIBWASI_EMULATED_GETPID_SO_OBJS = $(patsubst %.o,%.pic.o,$(LIBWASI_EMULATED_GETPID_OBJS)) LIBWASI_EMULATED_SIGNAL_SO_OBJS = $(patsubst %.o,%.pic.o,$(LIBWASI_EMULATED_SIGNAL_OBJS)) LIBWASI_EMULATED_SIGNAL_MUSL_SO_OBJS = $(patsubst %.o,%.pic.o,$(LIBWASI_EMULATED_SIGNAL_MUSL_OBJS)) -LIBWASI_EMULATED_PTHREAD_SO_OBJS = $(patsubst %.o,%.pic.o,$(LIBWASI_EMULATED_PTHREAD_OBJS)) LIBDL_SO_OBJS = $(patsubst %.o,%.pic.o,$(LIBDL_OBJS)) LIBSETJMP_SO_OBJS = $(patsubst %.o,%.pic.o,$(LIBSETJMP_OBJS)) BULK_MEMORY_SO_OBJS = $(patsubst %.o,%.pic.o,$(BULK_MEMORY_OBJS)) @@ -546,7 +568,6 @@ PIC_OBJS = \ $(LIBWASI_EMULATED_GETPID_SO_OBJS) \ $(LIBWASI_EMULATED_SIGNAL_SO_OBJS) \ $(LIBWASI_EMULATED_SIGNAL_MUSL_SO_OBJS) \ - $(LIBWASI_EMULATED_PTHREAD_SO_OBJS) \ $(LIBDL_SO_OBJS) \ $(LIBSETJMP_SO_OBJS) \ $(BULK_MEMORY_SO_OBJS) \ @@ -646,8 +667,6 @@ $(OBJDIR)/libwasi-emulated-getpid.so.a: $(LIBWASI_EMULATED_GETPID_SO_OBJS) $(OBJDIR)/libwasi-emulated-signal.so.a: $(LIBWASI_EMULATED_SIGNAL_SO_OBJS) $(LIBWASI_EMULATED_SIGNAL_MUSL_SO_OBJS) -$(OBJDIR)/libwasi-emulated-pthread.so.a: $(LIBWASI_EMULATED_PTHREAD_SO_OBJS) - $(OBJDIR)/libdl.so.a: $(LIBDL_SO_OBJS) $(OBJDIR)/libsetjmp.so.a: $(LIBSETJMP_SO_OBJS) @@ -666,8 +685,6 @@ $(SYSROOT_LIB)/libwasi-emulated-getpid.a: $(LIBWASI_EMULATED_GETPID_OBJS) $(SYSROOT_LIB)/libwasi-emulated-signal.a: $(LIBWASI_EMULATED_SIGNAL_OBJS) $(LIBWASI_EMULATED_SIGNAL_MUSL_OBJS) -$(SYSROOT_LIB)/libwasi-emulated-pthread.a: $(LIBWASI_EMULATED_PTHREAD_OBJS) - $(SYSROOT_LIB)/libdl.a: $(LIBDL_OBJS) $(SYSROOT_LIB)/libsetjmp.a: $(LIBSETJMP_OBJS) @@ -770,12 +787,6 @@ $(FTS_OBJS) $(FTS_SO_OBJS): CFLAGS += \ $(LIBWASI_EMULATED_PROCESS_CLOCKS_OBJS) $(LIBWASI_EMULATED_PROCESS_CLOCKS_SO_OBJS): CFLAGS += \ -I$(LIBC_BOTTOM_HALF_CLOUDLIBC_SRC) -$(LIBWASI_EMULATED_PTHREAD_OBJS) $(LIBWASI_EMULATED_PTHREAD_SO_OBJS): CFLAGS += \ - -I$(LIBC_TOP_HALF_MUSL_SRC_DIR)/include \ - -I$(LIBC_TOP_HALF_MUSL_SRC_DIR)/internal \ - -I$(LIBC_TOP_HALF_MUSL_DIR)/arch/wasm32 \ - -D_WASI_EMULATED_PTHREAD - # emmalloc uses a lot of pointer type-punning, which is UB under strict aliasing, # and this was found to have real miscompilations in wasi-libc#421. $(EMMALLOC_OBJS): CFLAGS += \ @@ -816,7 +827,6 @@ LIBC_SO = \ $(SYSROOT_LIB)/libwasi-emulated-process-clocks.so \ $(SYSROOT_LIB)/libwasi-emulated-getpid.so \ $(SYSROOT_LIB)/libwasi-emulated-signal.so \ - $(SYSROOT_LIB)/libwasi-emulated-pthread.so \ $(SYSROOT_LIB)/libdl.so ifeq ($(BUILD_LIBSETJMP),yes) LIBC_SO += \ @@ -835,10 +845,6 @@ STATIC_LIBS = \ $(SYSROOT_LIB)/libwasi-emulated-getpid.a \ $(SYSROOT_LIB)/libwasi-emulated-signal.a \ $(SYSROOT_LIB)/libdl.a -ifneq ($(THREAD_MODEL), posix) - STATIC_LIBS += \ - $(SYSROOT_LIB)/libwasi-emulated-pthread.a -endif ifeq ($(BUILD_LIBSETJMP),yes) STATIC_LIBS += \ $(SYSROOT_LIB)/libsetjmp.a diff --git a/expected/wasm32-wasip1/defined-symbols.txt b/expected/wasm32-wasip1/defined-symbols.txt index 6e06d64ff..5ec699a95 100644 --- a/expected/wasm32-wasip1/defined-symbols.txt +++ b/expected/wasm32-wasip1/defined-symbols.txt @@ -182,15 +182,14 @@ __progname __progname_full __pthread_cond_timedwait __pthread_create -__pthread_detach __pthread_join __pthread_key_create __pthread_key_delete -__pthread_mutex_consistent __pthread_mutex_lock __pthread_mutex_timedlock __pthread_mutex_trylock __pthread_mutex_unlock +__pthread_once __pthread_rwlock_rdlock __pthread_rwlock_timedrdlock __pthread_rwlock_timedwrlock @@ -991,13 +990,14 @@ pthread_condattr_setpshared pthread_create pthread_detach pthread_equal -pthread_exit +pthread_getattr_np pthread_getspecific pthread_join pthread_key_create pthread_key_delete pthread_mutex_consistent pthread_mutex_destroy +pthread_mutex_getprioceiling pthread_mutex_init pthread_mutex_lock pthread_mutex_timedlock @@ -1221,6 +1221,7 @@ tgamma tgammaf tgammal thrd_current +thrd_detach thrd_equal thrd_sleep time diff --git a/expected/wasm32-wasip1/predefined-macros.txt b/expected/wasm32-wasip1/predefined-macros.txt index d1aca887a..66f4c4c88 100644 --- a/expected/wasm32-wasip1/predefined-macros.txt +++ b/expected/wasm32-wasip1/predefined-macros.txt @@ -2252,8 +2252,12 @@ #define _POSIX_STREAM_MAX 8 #define _POSIX_SYMLINK_MAX 255 #define _POSIX_SYMLOOP_MAX 8 +#define _POSIX_THREADS _POSIX_VERSION +#define _POSIX_THREAD_ATTR_STACKSIZE _POSIX_VERSION #define _POSIX_THREAD_DESTRUCTOR_ITERATIONS 4 #define _POSIX_THREAD_KEYS_MAX 128 +#define _POSIX_THREAD_PROCESS_SHARED _POSIX_VERSION +#define _POSIX_THREAD_SAFE_FUNCTIONS _POSIX_VERSION #define _POSIX_THREAD_THREADS_MAX 64 #define _POSIX_TIMEOUTS _POSIX_VERSION #define _POSIX_TIMERS _POSIX_VERSION @@ -3430,12 +3434,7 @@ #define preadv64 preadv #define pthread_cleanup_pop(r) _pthread_cleanup_pop(&__cb, (r)); } while(0) #define pthread_cleanup_push(f,x) do { struct __ptcb __cb; _pthread_cleanup_push(&__cb, f, x); -#define pthread_create(...) ({ _Static_assert(0, "This mode of WASI does not have threads enabled; to enable stub functions which always fail, compile with -D_WASI_EMULATED_PTHREAD and link with -lwasi-emulated-pthread"); 0;}) -#define pthread_detach(...) ({ _Static_assert(0, "This mode of WASI does not have threads enabled; to enable stub functions which always fail, compile with -D_WASI_EMULATED_PTHREAD and link with -lwasi-emulated-pthread"); 0;}) #define pthread_equal(x,y) ((x)==(y)) -#define pthread_join(...) ({ _Static_assert(0, "This mode of WASI does not have threads enabled; to enable stub functions which always fail, compile with -D_WASI_EMULATED_PTHREAD and link with -lwasi-emulated-pthread"); 0;}) -#define pthread_timedjoin_np(...) ({ _Static_assert(0, "This mode of WASI does not have threads enabled; to enable stub functions which always fail, compile with -D_WASI_EMULATED_PTHREAD and link with -lwasi-emulated-pthread"); 0;}) -#define pthread_tryjoin_np(...) ({ _Static_assert(0, "This mode of WASI does not have threads enabled; to enable stub functions which always fail, compile with -D_WASI_EMULATED_PTHREAD and link with -lwasi-emulated-pthread"); 0;}) #define pwrite64 pwrite #define pwritev64 pwritev #define readdir64 readdir diff --git a/expected/wasm32-wasip2/defined-symbols.txt b/expected/wasm32-wasip2/defined-symbols.txt index 9d2e803fd..d5fc8f116 100644 --- a/expected/wasm32-wasip2/defined-symbols.txt +++ b/expected/wasm32-wasip2/defined-symbols.txt @@ -185,15 +185,14 @@ __progname __progname_full __pthread_cond_timedwait __pthread_create -__pthread_detach __pthread_join __pthread_key_create __pthread_key_delete -__pthread_mutex_consistent __pthread_mutex_lock __pthread_mutex_timedlock __pthread_mutex_trylock __pthread_mutex_unlock +__pthread_once __pthread_rwlock_rdlock __pthread_rwlock_timedrdlock __pthread_rwlock_timedwrlock @@ -1127,13 +1126,14 @@ pthread_condattr_setpshared pthread_create pthread_detach pthread_equal -pthread_exit +pthread_getattr_np pthread_getspecific pthread_join pthread_key_create pthread_key_delete pthread_mutex_consistent pthread_mutex_destroy +pthread_mutex_getprioceiling pthread_mutex_init pthread_mutex_lock pthread_mutex_timedlock @@ -1457,6 +1457,7 @@ tgamma tgammaf tgammal thrd_current +thrd_detach thrd_equal thrd_sleep time diff --git a/expected/wasm32-wasip2/predefined-macros.txt b/expected/wasm32-wasip2/predefined-macros.txt index 959dd6ec8..1ae2e1fd2 100644 --- a/expected/wasm32-wasip2/predefined-macros.txt +++ b/expected/wasm32-wasip2/predefined-macros.txt @@ -2403,8 +2403,12 @@ #define _POSIX_STREAM_MAX 8 #define _POSIX_SYMLINK_MAX 255 #define _POSIX_SYMLOOP_MAX 8 +#define _POSIX_THREADS _POSIX_VERSION +#define _POSIX_THREAD_ATTR_STACKSIZE _POSIX_VERSION #define _POSIX_THREAD_DESTRUCTOR_ITERATIONS 4 #define _POSIX_THREAD_KEYS_MAX 128 +#define _POSIX_THREAD_PROCESS_SHARED _POSIX_VERSION +#define _POSIX_THREAD_SAFE_FUNCTIONS _POSIX_VERSION #define _POSIX_THREAD_THREADS_MAX 64 #define _POSIX_TIMEOUTS _POSIX_VERSION #define _POSIX_TIMERS _POSIX_VERSION @@ -3585,12 +3589,7 @@ #define preadv64 preadv #define pthread_cleanup_pop(r) _pthread_cleanup_pop(&__cb, (r)); } while(0) #define pthread_cleanup_push(f,x) do { struct __ptcb __cb; _pthread_cleanup_push(&__cb, f, x); -#define pthread_create(...) ({ _Static_assert(0, "This mode of WASI does not have threads enabled; to enable stub functions which always fail, compile with -D_WASI_EMULATED_PTHREAD and link with -lwasi-emulated-pthread"); 0;}) -#define pthread_detach(...) ({ _Static_assert(0, "This mode of WASI does not have threads enabled; to enable stub functions which always fail, compile with -D_WASI_EMULATED_PTHREAD and link with -lwasi-emulated-pthread"); 0;}) #define pthread_equal(x,y) ((x)==(y)) -#define pthread_join(...) ({ _Static_assert(0, "This mode of WASI does not have threads enabled; to enable stub functions which always fail, compile with -D_WASI_EMULATED_PTHREAD and link with -lwasi-emulated-pthread"); 0;}) -#define pthread_timedjoin_np(...) ({ _Static_assert(0, "This mode of WASI does not have threads enabled; to enable stub functions which always fail, compile with -D_WASI_EMULATED_PTHREAD and link with -lwasi-emulated-pthread"); 0;}) -#define pthread_tryjoin_np(...) ({ _Static_assert(0, "This mode of WASI does not have threads enabled; to enable stub functions which always fail, compile with -D_WASI_EMULATED_PTHREAD and link with -lwasi-emulated-pthread"); 0;}) #define pwrite64 pwrite #define pwritev64 pwritev #define readdir64 readdir diff --git a/libc-top-half/musl/include/pthread.h b/libc-top-half/musl/include/pthread.h index fa9f66ae8..111c1fbf8 100644 --- a/libc-top-half/musl/include/pthread.h +++ b/libc-top-half/musl/include/pthread.h @@ -83,21 +83,14 @@ int pthread_detach(pthread_t); _Noreturn void pthread_exit(void *); int pthread_join(pthread_t, void **); #else -#if defined(_WASI_EMULATED_PTHREAD) || defined(_REENTRANT) +#if defined(_REENTRANT) || !defined(_WASI_STRICT_PTHREAD) int pthread_create(pthread_t *__restrict, const pthread_attr_t *__restrict, void *(*)(void *), void *__restrict); int pthread_detach(pthread_t); int pthread_join(pthread_t, void **); #else -#include -#define pthread_create(...) ({ _Static_assert(0, "This mode of WASI does not have threads enabled; \ -to enable stub functions which always fail, \ -compile with -D_WASI_EMULATED_PTHREAD and link with -lwasi-emulated-pthread"); 0;}) -#define pthread_detach(...) ({ _Static_assert(0, "This mode of WASI does not have threads enabled; \ -to enable stub functions which always fail, \ -compile with -D_WASI_EMULATED_PTHREAD and link with -lwasi-emulated-pthread"); 0;}) -#define pthread_join(...) ({ _Static_assert(0, "This mode of WASI does not have threads enabled; \ -to enable stub functions which always fail, \ -compile with -D_WASI_EMULATED_PTHREAD and link with -lwasi-emulated-pthread"); 0;}) +#define pthread_create(...) ({ _Static_assert(0, "This function is not available on a single-threaded target; switch to a multi-threaded target with -pthread or enable a stub implementation by undefining _WASI_STRICT_PTHREAD"); 0;}) +#define pthread_detach(...) ({ _Static_assert(0, "This function is not available on a single-threaded target; switch to a multi-threaded target with -pthread or enable a stub implementation by undefining _WASI_STRICT_PTHREAD"); 0;}) +#define pthread_join(...) ({ _Static_assert(0, "This function is not available on a single-threaded target; switch to a multi-threaded target with -pthread or enable a stub implementation by undefining _WASI_STRICT_PTHREAD"); 0;}) #endif #endif @@ -249,16 +242,12 @@ int pthread_setname_np(pthread_t, const char *); int pthread_getname_np(pthread_t, char *, size_t); int pthread_getattr_default_np(pthread_attr_t *); int pthread_setattr_default_np(const pthread_attr_t *); -#if defined(__wasilibc_unmodified_upstream) || defined(_WASI_EMULATED_PTHREAD) || defined(_REENTRANT) +#if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) || !defined(_WASI_STRICT_PTHREAD) int pthread_tryjoin_np(pthread_t, void **); int pthread_timedjoin_np(pthread_t, void **, const struct timespec *); #else -#define pthread_tryjoin_np(...) ({ _Static_assert(0, "This mode of WASI does not have threads enabled; \ -to enable stub functions which always fail, \ -compile with -D_WASI_EMULATED_PTHREAD and link with -lwasi-emulated-pthread"); 0;}) -#define pthread_timedjoin_np(...) ({ _Static_assert(0, "This mode of WASI does not have threads enabled; \ -to enable stub functions which always fail, \ -compile with -D_WASI_EMULATED_PTHREAD and link with -lwasi-emulated-pthread"); 0;}) +#define pthread_tryjoin_np(...) ({ _Static_assert(0, "This function is not available on a single-threaded target; switch to a multi-threaded target with -pthread or enable a stub implementation by undefining _WASI_STRICT_PTHREAD"); 0;}) +#define pthread_timedjoin_np(...) ({ _Static_assert(0, "This function is not available on a single-threaded target; switch to a multi-threaded target with -pthread or enable a stub implementation by undefining _WASI_STRICT_PTHREAD"); 0;}) #endif #endif diff --git a/libc-top-half/musl/include/unistd.h b/libc-top-half/musl/include/unistd.h index 1c1d62d22..c3158e6e6 100644 --- a/libc-top-half/musl/include/unistd.h +++ b/libc-top-half/musl/include/unistd.h @@ -336,7 +336,7 @@ pid_t gettid(void); #endif #define _POSIX_VDISABLE 0 -#if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) +#if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) || !defined(_WASI_STRICT_PTHREAD) #define _POSIX_THREADS _POSIX_VERSION #define _POSIX_THREAD_PROCESS_SHARED _POSIX_VERSION #define _POSIX_THREAD_SAFE_FUNCTIONS _POSIX_VERSION @@ -344,7 +344,7 @@ pid_t gettid(void); #if defined(__wasilibc_unmodified_upstream) /* wasi-libc doesn't provide pthread_attr_{get,set}stackaddr */ #define _POSIX_THREAD_ATTR_STACKADDR _POSIX_VERSION #endif -#if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) +#if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) || !defined(_WASI_STRICT_PTHREAD) #define _POSIX_THREAD_ATTR_STACKSIZE _POSIX_VERSION #endif #if defined(__wasilibc_unmodified_upstream) /* WASI has no scheduling control, and wasi-libc doesn't provide pthread_getcpuclockid */ diff --git a/stub-pthreads/README.md b/stub-pthreads/README.md deleted file mode 100644 index fe2161000..000000000 --- a/stub-pthreads/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Single-threaded pthreads stubs - -The goal of these files is to provide stub implementations of pthreads -functions for `THREAD_MODEL=single`. This implementation should _always_ -follow the strict letter of the POSIX specifications. This means that -"doing nothing", "always succeeding", etc. are not proper implementations --- these stubs aim for higher conformance than that. - -The code that is "more" aligned with the spirit of the POSIX specifications - ends up compiled into libc itself. This primarily consists of synchronization - primitives (which are implemented to actually track state). - -The code that is "less" aligned with the spirit of the specifications -(e.g. by "rules-lawyering" and always failing) are built into a library -`wasi-emulated-pthread.a`. The distinction is ultimately made by vibes and a -judgement call, not formal criteria. diff --git a/stub-pthreads/barrier.c b/stub-pthreads/barrier.c deleted file mode 100644 index e0ceed06a..000000000 --- a/stub-pthreads/barrier.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "pthread_impl.h" -/* - Note on PTHREAD_PROCESS_SHARED: - Because WASM doesn't have virtual memory nor subprocesses, - there isn't any way for the same synchronization object to have multiple mappings. - Thus we can completely ignore it here. -*/ - -int pthread_barrier_init(pthread_barrier_t *restrict b, const pthread_barrierattr_t *restrict a, unsigned count) -{ - if (count-1 > INT_MAX-1) return EINVAL; - *b = (pthread_barrier_t){ ._b_limit = count-1 }; - return 0; -} -int pthread_barrier_destroy(pthread_barrier_t *b) -{ - return 0; -} -int pthread_barrier_wait(pthread_barrier_t *b) -{ - if (!b->_b_limit) return PTHREAD_BARRIER_SERIAL_THREAD; - __builtin_trap(); -} diff --git a/stub-pthreads/condvar.c b/stub-pthreads/condvar.c deleted file mode 100644 index de65bac23..000000000 --- a/stub-pthreads/condvar.c +++ /dev/null @@ -1,36 +0,0 @@ -#include "pthread_impl.h" -#include - -int pthread_cond_init(pthread_cond_t *restrict c, const pthread_condattr_t *restrict a) -{ - return 0; -} -int pthread_cond_destroy(pthread_cond_t *c) -{ - return 0; -} -int pthread_cond_broadcast(pthread_cond_t *c) -{ - return 0; -} -int pthread_cond_signal(pthread_cond_t *c) -{ - return 0; -} -int pthread_cond_wait(pthread_cond_t *restrict c, pthread_mutex_t *restrict m) -{ - /* Because there is no other thread that can signal us, this is a deadlock immediately. - The other possible choice is to return immediately (spurious wakeup), but that is likely to - just result in the program spinning forever on a condition that cannot become true. */ - __builtin_trap(); -} -int __pthread_cond_timedwait(pthread_cond_t *restrict c, pthread_mutex_t *restrict m, const struct timespec *restrict ts) -{ - /* Error check mutexes must detect if they're not locked (UB for others) */ - if (!m->_m_count) return EPERM; - int ret = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, ts, 0); - if (ret == 0) return ETIMEDOUT; - if (ret != EINTR) return ret; - return 0; -} -weak_alias(__pthread_cond_timedwait, pthread_cond_timedwait); diff --git a/stub-pthreads/mutex.c b/stub-pthreads/mutex.c deleted file mode 100644 index 475bdcf8c..000000000 --- a/stub-pthreads/mutex.c +++ /dev/null @@ -1,67 +0,0 @@ -#include "pthread_impl.h" -/* - Musl mutex (FYI) - - _m_type: - b[7] - process shared - b[3] - priority inherit - b[2] - robust - b[1:0] - type - 0 - normal - 1 - recursive - 2 - errorcheck - - _m_lock: - b[30] - owner dead, if robust - b[29:0] - tid, if not normal - b[4] - locked?, if normal -*/ - -int __pthread_mutex_consistent(pthread_mutex_t *m) -{ - /* cannot be a robust mutex, as they're entirely unsupported in WASI */ - return EINVAL; -} -weak_alias(__pthread_mutex_consistent, pthread_mutex_consistent); - -int __pthread_mutex_lock(pthread_mutex_t *m) -{ - if (m->_m_type&3 != PTHREAD_MUTEX_RECURSIVE) { - if (m->_m_count) return EDEADLK; - m->_m_count = 1; - } else { - if ((unsigned)m->_m_count >= INT_MAX) return EAGAIN; - m->_m_count++; - } - return 0; -} -weak_alias(__pthread_mutex_lock, pthread_mutex_lock); - -int __pthread_mutex_trylock(pthread_mutex_t *m) -{ - if (m->_m_type&3 != PTHREAD_MUTEX_RECURSIVE) { - if (m->_m_count) return EBUSY; - m->_m_count = 1; - } else { - if ((unsigned)m->_m_count >= INT_MAX) return EAGAIN; - m->_m_count++; - } - return 0; -} -weak_alias(__pthread_mutex_trylock, pthread_mutex_trylock); - -int __pthread_mutex_unlock(pthread_mutex_t *m) -{ - if (!m->_m_count) return EPERM; - m->_m_count--; - return 0; -} -weak_alias(__pthread_mutex_unlock, pthread_mutex_unlock); - -int __pthread_mutex_timedlock(pthread_mutex_t *restrict m, const struct timespec *restrict at) -{ - /* "The pthread_mutex_timedlock() function may fail if: A deadlock condition was detected." */ - /* This means that we don't have to wait and then return timeout, we can just detect deadlock. */ - return pthread_mutex_lock(m); -} -weak_alias(__pthread_mutex_timedlock, pthread_mutex_timedlock); diff --git a/stub-pthreads/rwlock.c b/stub-pthreads/rwlock.c deleted file mode 100644 index ce0fc7a29..000000000 --- a/stub-pthreads/rwlock.c +++ /dev/null @@ -1,60 +0,0 @@ -#include "pthread_impl.h" -/* Musl uses bit31 to mark "has waiters", bit[30:0] all 1s to indicate writer */ - -/* These functions have the __ prefix to help stub out thread-specific data */ - -int __pthread_rwlock_rdlock(pthread_rwlock_t *rw) -{ - if (rw->_rw_lock == 0x7fffffff) return EDEADLK; - if (rw->_rw_lock == 0x7ffffffe) return EAGAIN; - rw->_rw_lock++; - return 0; -} -weak_alias(__pthread_rwlock_rdlock, pthread_rwlock_rdlock); - -int __pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rw, const struct timespec *restrict at) -{ - return pthread_rwlock_rdlock(rw); -} -weak_alias(__pthread_rwlock_timedrdlock, pthread_rwlock_timedrdlock); - -int __pthread_rwlock_tryrdlock(pthread_rwlock_t *rw) -{ - if (rw->_rw_lock == 0x7fffffff) return EBUSY; - if (rw->_rw_lock == 0x7ffffffe) return EAGAIN; - rw->_rw_lock++; - return 0; -} -weak_alias(__pthread_rwlock_tryrdlock, pthread_rwlock_tryrdlock); - -int __pthread_rwlock_wrlock(pthread_rwlock_t *rw) -{ - if (rw->_rw_lock) return EDEADLK; - rw->_rw_lock = 0x7fffffff; - return 0; -} -weak_alias(__pthread_rwlock_wrlock, pthread_rwlock_wrlock); - -int __pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rw, const struct timespec *restrict at) -{ - return pthread_rwlock_wrlock(rw); -} -weak_alias(__pthread_rwlock_timedwrlock, pthread_rwlock_timedwrlock); - -int __pthread_rwlock_trywrlock(pthread_rwlock_t *rw) -{ - if (rw->_rw_lock) return EBUSY; - rw->_rw_lock = 0x7fffffff; - return 0; -} -weak_alias(__pthread_rwlock_trywrlock, pthread_rwlock_trywrlock); - -int __pthread_rwlock_unlock(pthread_rwlock_t *rw) -{ - if (rw->_rw_lock == 0x7fffffff) - rw->_rw_lock = 0; - else - rw->_rw_lock--; - return 0; -} -weak_alias(__pthread_rwlock_unlock, pthread_rwlock_unlock); diff --git a/stub-pthreads/spinlock.c b/stub-pthreads/spinlock.c deleted file mode 100644 index 1099f7e07..000000000 --- a/stub-pthreads/spinlock.c +++ /dev/null @@ -1,21 +0,0 @@ - -#include "pthread_impl.h" -/* The only reason why we have to do anything is trylock */ - -int pthread_spin_lock(pthread_spinlock_t *s) -{ - if (*s) return EDEADLK; - *s = 1; - return 0; -} -int pthread_spin_trylock(pthread_spinlock_t *s) -{ - if (*s) return EBUSY; - *s = 1; - return 0; -} -int pthread_spin_unlock(pthread_spinlock_t *s) -{ - *s = 0; - return 0; -} diff --git a/stub-pthreads/stub-pthreads-emulated.c b/stub-pthreads/stub-pthreads-emulated.c deleted file mode 100644 index d7e04774f..000000000 --- a/stub-pthreads/stub-pthreads-emulated.c +++ /dev/null @@ -1,40 +0,0 @@ -// This file is linked into wasi-emulated-pthread - -#include "pthread_impl.h" - -int __pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attrp, void *(*entry)(void *), void *restrict arg) -{ - /* - "The system lacked the necessary resources to create another thread, - or the system-imposed limit on the total number of threads in a process - {PTHREAD_THREADS_MAX} would be exceeded." - */ - return EAGAIN; -} -weak_alias(__pthread_create, pthread_create); -int __pthread_detach(pthread_t t) -{ - /* - If we are the only thread, when we exit the whole process exits. - So the storage will be reclaimed no matter what. - */ - return 0; -} -weak_alias(__pthread_detach, pthread_detach); -int __pthread_join(pthread_t t, void **res) -{ - /* - "The behavior is undefined if the value specified by the thread argument - to pthread_join() refers to the calling thread." - */ - return 0; -} -weak_alias(__pthread_join, pthread_join); -int pthread_tryjoin_np(pthread_t t, void **res) -{ - return 0; -} -int pthread_timedjoin_np(pthread_t t, void **res, const struct timespec *at) -{ - return 0; -} diff --git a/stub-pthreads/stub-pthreads-good.c b/stub-pthreads/stub-pthreads-good.c deleted file mode 100644 index 0f6328404..000000000 --- a/stub-pthreads/stub-pthreads-good.c +++ /dev/null @@ -1,43 +0,0 @@ -// This file is linked into libc - -#include "pthread_impl.h" - -static void dummy_0() -{ -} -weak_alias(dummy_0, __acquire_ptc); -weak_alias(dummy_0, __release_ptc); -weak_alias(dummy_0, __pthread_tsd_run_dtors); - -int pthread_once(pthread_once_t *control, void (*init)(void)) -{ - if (!*control) { - init(); - *control = 1; - } - return 0; -} - -_Noreturn void pthread_exit(void *result) -{ - /* - We are the only thread, so when we exit the whole process exits. - But we still have to run cancellation handlers... - */ - 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(); - - exit(0); -} diff --git a/thread-stub/README.md b/thread-stub/README.md new file mode 100644 index 000000000..0a78fc2fa --- /dev/null +++ b/thread-stub/README.md @@ -0,0 +1,7 @@ +# Stub pthreads implementation + +This library provides the foundation for a POSIX conformant implementation of +pthreads API for `THREAD_MODEL=single`. When combined with the appropriate +parts of musl's libc-top-half, it forms a complete pthreads implementation +that is never able to spawn new threads, but otherwise follows the letter of +the specification in every way. diff --git a/thread-stub/pthread_barrier_destroy.c b/thread-stub/pthread_barrier_destroy.c new file mode 100644 index 000000000..2898c41a0 --- /dev/null +++ b/thread-stub/pthread_barrier_destroy.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_barrier_destroy(pthread_barrier_t *b) +{ + return 0; +} diff --git a/thread-stub/pthread_barrier_init.c b/thread-stub/pthread_barrier_init.c new file mode 100644 index 000000000..42eb6451b --- /dev/null +++ b/thread-stub/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-1 > INT_MAX-1) return EINVAL; + *b = (pthread_barrier_t){ ._b_limit = count-1 }; + return 0; +} diff --git a/thread-stub/pthread_barrier_wait.c b/thread-stub/pthread_barrier_wait.c new file mode 100644 index 000000000..c5a4afb73 --- /dev/null +++ b/thread-stub/pthread_barrier_wait.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +int pthread_barrier_wait(pthread_barrier_t *b) +{ + if (!b->_b_limit) return PTHREAD_BARRIER_SERIAL_THREAD; + __builtin_trap(); +} diff --git a/thread-stub/pthread_cond_broadcast.c b/thread-stub/pthread_cond_broadcast.c new file mode 100644 index 000000000..ce3180996 --- /dev/null +++ b/thread-stub/pthread_cond_broadcast.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_cond_broadcast(pthread_cond_t *c) +{ + return 0; +} diff --git a/thread-stub/pthread_cond_destroy.c b/thread-stub/pthread_cond_destroy.c new file mode 100644 index 000000000..1d21a5a8b --- /dev/null +++ b/thread-stub/pthread_cond_destroy.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_cond_destroy(pthread_cond_t *c) +{ + return 0; +} diff --git a/thread-stub/pthread_cond_init.c b/thread-stub/pthread_cond_init.c new file mode 100644 index 000000000..c32903b6c --- /dev/null +++ b/thread-stub/pthread_cond_init.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_cond_init(pthread_cond_t *restrict c, const pthread_condattr_t *restrict a) +{ + return 0; +} diff --git a/thread-stub/pthread_cond_signal.c b/thread-stub/pthread_cond_signal.c new file mode 100644 index 000000000..4eb2774ae --- /dev/null +++ b/thread-stub/pthread_cond_signal.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_cond_signal(pthread_cond_t *c) +{ + return 0; +} diff --git a/thread-stub/pthread_cond_timedwait.c b/thread-stub/pthread_cond_timedwait.c new file mode 100644 index 000000000..0e7f9f835 --- /dev/null +++ b/thread-stub/pthread_cond_timedwait.c @@ -0,0 +1,13 @@ +#include "pthread_impl.h" + +int __pthread_cond_timedwait(pthread_cond_t *restrict c, pthread_mutex_t *restrict m, const struct timespec *restrict ts) +{ + /* Error check mutexes must detect if they're not locked (UB for others) */ + if (!m->_m_count) return EPERM; + int ret = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, ts, 0); + if (ret == 0) return ETIMEDOUT; + if (ret != EINTR) return ret; + return 0; +} + +weak_alias(__pthread_cond_timedwait, pthread_cond_timedwait); diff --git a/thread-stub/pthread_cond_wait.c b/thread-stub/pthread_cond_wait.c new file mode 100644 index 000000000..84a44b7bc --- /dev/null +++ b/thread-stub/pthread_cond_wait.c @@ -0,0 +1,9 @@ +#include "pthread_impl.h" + +int pthread_cond_wait(pthread_cond_t *restrict c, pthread_mutex_t *restrict m) +{ + /* Because there is no other thread that can signal us, this is a deadlock immediately. + The other possible choice is to return immediately (spurious wakeup), but that is likely to + just result in the program spinning forever on a condition that cannot become true. */ + __builtin_trap(); +} diff --git a/thread-stub/pthread_create.c b/thread-stub/pthread_create.c new file mode 100644 index 000000000..717564cd1 --- /dev/null +++ b/thread-stub/pthread_create.c @@ -0,0 +1,19 @@ +#include "pthread_impl.h" + +static void dummy_0() +{ +} +weak_alias(dummy_0, __acquire_ptc); +weak_alias(dummy_0, __release_ptc); + +int __pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attrp, void *(*entry)(void *), void *restrict arg) +{ + /* + "The system lacked the necessary resources to create another thread, + or the system-imposed limit on the total number of threads in a process + {PTHREAD_THREADS_MAX} would be exceeded." + */ + return EAGAIN; +} + +weak_alias(__pthread_create, pthread_create); diff --git a/thread-stub/pthread_detach.c b/thread-stub/pthread_detach.c new file mode 100644 index 000000000..55953e159 --- /dev/null +++ b/thread-stub/pthread_detach.c @@ -0,0 +1,13 @@ +#include "pthread_impl.h" + +static int __pthread_detach(pthread_t t) +{ + /* + If we are the only thread, when we exit the whole process exits. + So the storage will be reclaimed no matter what. + */ + return 0; +} + +weak_alias(__pthread_detach, pthread_detach); +weak_alias(__pthread_detach, thrd_detach); diff --git a/thread-stub/pthread_getattr_np.c b/thread-stub/pthread_getattr_np.c new file mode 100644 index 000000000..d463de143 --- /dev/null +++ b/thread-stub/pthread_getattr_np.c @@ -0,0 +1,11 @@ +#include "pthread_impl.h" + +int pthread_getattr_np(pthread_t t, pthread_attr_t *a) +{ + *a = (pthread_attr_t){0}; + /* Can't join main thread. */ + a->_a_detach = PTHREAD_CREATE_DETACHED; + /* WASI doesn't have memory protection required for stack guards. */ + a->_a_guardsize = 0; + return 0; +} diff --git a/thread-stub/pthread_join.c b/thread-stub/pthread_join.c new file mode 100644 index 000000000..cbdcedd73 --- /dev/null +++ b/thread-stub/pthread_join.c @@ -0,0 +1,32 @@ +#include "pthread_impl.h" + +static int __pthread_tryjoin_np(pthread_t t, void **res) +{ + /* + "The behavior is undefined if the value specified by the thread argument + to pthread_join() refers to the calling thread." + */ + return 0; +} + +static int __pthread_timedjoin_np(pthread_t t, void **res, const struct timespec *at) +{ + /* + "The behavior is undefined if the value specified by the thread argument + to pthread_join() refers to the calling thread." + */ + return 0; +} + +int __pthread_join(pthread_t t, void **res) +{ + /* + "The behavior is undefined if the value specified by the thread argument + to pthread_join() refers to the calling thread." + */ + return 0; +} + +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/thread-stub/pthread_mutex_consistent.c b/thread-stub/pthread_mutex_consistent.c new file mode 100644 index 000000000..639635361 --- /dev/null +++ b/thread-stub/pthread_mutex_consistent.c @@ -0,0 +1,8 @@ +#include "pthread_impl.h" + +int pthread_mutex_consistent(pthread_mutex_t *m) +{ + /* cannot be a robust mutex, as they're entirely unsupported in WASI */ + return EINVAL; + +} diff --git a/thread-stub/pthread_mutex_getprioceiling.c b/thread-stub/pthread_mutex_getprioceiling.c new file mode 100644 index 000000000..8c75a6612 --- /dev/null +++ b/thread-stub/pthread_mutex_getprioceiling.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_mutex_getprioceiling(const pthread_mutex_t *restrict m, int *restrict ceiling) +{ + return EINVAL; +} diff --git a/thread-stub/pthread_mutex_lock.c b/thread-stub/pthread_mutex_lock.c new file mode 100644 index 000000000..9b380c50e --- /dev/null +++ b/thread-stub/pthread_mutex_lock.c @@ -0,0 +1,21 @@ +#include "pthread_impl.h" + +int __pthread_mutex_lock(pthread_mutex_t *m) +{ + /* + _m_type[1:0] - type + 0 - normal + 1 - recursive + 2 - errorcheck + */ + if (m->_m_type&3 != PTHREAD_MUTEX_RECURSIVE) { + if (m->_m_count) return EDEADLK; + m->_m_count = 1; + } else { + if ((unsigned)m->_m_count >= INT_MAX) return EAGAIN; + m->_m_count++; + } + return 0; +} + +weak_alias(__pthread_mutex_lock, pthread_mutex_lock); diff --git a/thread-stub/pthread_mutex_timedlock.c b/thread-stub/pthread_mutex_timedlock.c new file mode 100644 index 000000000..51f260640 --- /dev/null +++ b/thread-stub/pthread_mutex_timedlock.c @@ -0,0 +1,10 @@ +#include "pthread_impl.h" + +int __pthread_mutex_timedlock(pthread_mutex_t *restrict m, const struct timespec *restrict at) +{ + /* "The pthread_mutex_timedlock() function may fail if: A deadlock condition was detected." */ + /* This means that we don't have to wait and then return timeout, we can just detect deadlock. */ + return pthread_mutex_lock(m); +} + +weak_alias(__pthread_mutex_timedlock, pthread_mutex_timedlock); diff --git a/thread-stub/pthread_mutex_trylock.c b/thread-stub/pthread_mutex_trylock.c new file mode 100644 index 000000000..9f00f893f --- /dev/null +++ b/thread-stub/pthread_mutex_trylock.c @@ -0,0 +1,21 @@ +#include "pthread_impl.h" + +int __pthread_mutex_trylock(pthread_mutex_t *m) +{ + /* + _m_type[1:0] - type + 0 - normal + 1 - recursive + 2 - errorcheck + */ + if (m->_m_type&3 != PTHREAD_MUTEX_RECURSIVE) { + if (m->_m_count) return EBUSY; + m->_m_count = 1; + } else { + if ((unsigned)m->_m_count >= INT_MAX) return EAGAIN; + m->_m_count++; + } + return 0; +} + +weak_alias(__pthread_mutex_trylock, pthread_mutex_trylock); diff --git a/thread-stub/pthread_mutex_unlock.c b/thread-stub/pthread_mutex_unlock.c new file mode 100644 index 000000000..33757864a --- /dev/null +++ b/thread-stub/pthread_mutex_unlock.c @@ -0,0 +1,10 @@ +#include "pthread_impl.h" + +int __pthread_mutex_unlock(pthread_mutex_t *m) +{ + if (!m->_m_count) return EPERM; + m->_m_count--; + return 0; +} + +weak_alias(__pthread_mutex_unlock, pthread_mutex_unlock); diff --git a/thread-stub/pthread_once.c b/thread-stub/pthread_once.c new file mode 100644 index 000000000..350342a1e --- /dev/null +++ b/thread-stub/pthread_once.c @@ -0,0 +1,12 @@ +#include "pthread_impl.h" + +int __pthread_once(pthread_once_t *control, void (*init)(void)) +{ + if (!*control) { + init(); + *control = 1; + } + return 0; +} + +weak_alias(__pthread_once, pthread_once); diff --git a/thread-stub/pthread_rwlock_rdlock.c b/thread-stub/pthread_rwlock_rdlock.c new file mode 100644 index 000000000..1b935da4e --- /dev/null +++ b/thread-stub/pthread_rwlock_rdlock.c @@ -0,0 +1,11 @@ +#include "pthread_impl.h" + +int __pthread_rwlock_rdlock(pthread_rwlock_t *rw) +{ + if (rw->_rw_lock == 0x7fffffff) return EDEADLK; + if (rw->_rw_lock == 0x7ffffffe) return EAGAIN; + rw->_rw_lock++; + return 0; +} + +weak_alias(__pthread_rwlock_rdlock, pthread_rwlock_rdlock); diff --git a/thread-stub/pthread_rwlock_timedrdlock.c b/thread-stub/pthread_rwlock_timedrdlock.c new file mode 100644 index 000000000..448256434 --- /dev/null +++ b/thread-stub/pthread_rwlock_timedrdlock.c @@ -0,0 +1,8 @@ +#include "pthread_impl.h" + +int __pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rw, const struct timespec *restrict at) +{ + return pthread_rwlock_rdlock(rw); +} + +weak_alias(__pthread_rwlock_timedrdlock, pthread_rwlock_timedrdlock); diff --git a/thread-stub/pthread_rwlock_timedwrlock.c b/thread-stub/pthread_rwlock_timedwrlock.c new file mode 100644 index 000000000..1ee24500b --- /dev/null +++ b/thread-stub/pthread_rwlock_timedwrlock.c @@ -0,0 +1,8 @@ +#include "pthread_impl.h" + +int __pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rw, const struct timespec *restrict at) +{ + return pthread_rwlock_wrlock(rw); +} + +weak_alias(__pthread_rwlock_timedwrlock, pthread_rwlock_timedwrlock); diff --git a/thread-stub/pthread_rwlock_tryrdlock.c b/thread-stub/pthread_rwlock_tryrdlock.c new file mode 100644 index 000000000..4753a60e0 --- /dev/null +++ b/thread-stub/pthread_rwlock_tryrdlock.c @@ -0,0 +1,11 @@ +#include "pthread_impl.h" + +int __pthread_rwlock_tryrdlock(pthread_rwlock_t *rw) +{ + if (rw->_rw_lock == 0x7fffffff) return EBUSY; + if (rw->_rw_lock == 0x7ffffffe) return EAGAIN; + rw->_rw_lock++; + return 0; +} + +weak_alias(__pthread_rwlock_tryrdlock, pthread_rwlock_tryrdlock); diff --git a/thread-stub/pthread_rwlock_trywrlock.c b/thread-stub/pthread_rwlock_trywrlock.c new file mode 100644 index 000000000..3077fdc31 --- /dev/null +++ b/thread-stub/pthread_rwlock_trywrlock.c @@ -0,0 +1,10 @@ +#include "pthread_impl.h" + +int __pthread_rwlock_trywrlock(pthread_rwlock_t *rw) +{ + if (rw->_rw_lock) return EBUSY; + rw->_rw_lock = 0x7fffffff; + return 0; +} + +weak_alias(__pthread_rwlock_trywrlock, pthread_rwlock_trywrlock); diff --git a/thread-stub/pthread_rwlock_unlock.c b/thread-stub/pthread_rwlock_unlock.c new file mode 100644 index 000000000..40f047ea9 --- /dev/null +++ b/thread-stub/pthread_rwlock_unlock.c @@ -0,0 +1,12 @@ +#include "pthread_impl.h" + +int __pthread_rwlock_unlock(pthread_rwlock_t *rw) +{ + if (rw->_rw_lock == 0x7fffffff) + rw->_rw_lock = 0; + else + rw->_rw_lock--; + return 0; +} + +weak_alias(__pthread_rwlock_unlock, pthread_rwlock_unlock); diff --git a/thread-stub/pthread_rwlock_wrlock.c b/thread-stub/pthread_rwlock_wrlock.c new file mode 100644 index 000000000..ce9d2099b --- /dev/null +++ b/thread-stub/pthread_rwlock_wrlock.c @@ -0,0 +1,10 @@ +#include "pthread_impl.h" + +int __pthread_rwlock_wrlock(pthread_rwlock_t *rw) +{ + if (rw->_rw_lock) return EDEADLK; + rw->_rw_lock = 0x7fffffff; + return 0; +} + +weak_alias(__pthread_rwlock_wrlock, pthread_rwlock_wrlock); diff --git a/thread-stub/pthread_spin_lock.c b/thread-stub/pthread_spin_lock.c new file mode 100644 index 000000000..4a682a6b6 --- /dev/null +++ b/thread-stub/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 = 1; + return 0; +} diff --git a/thread-stub/pthread_spin_trylock.c b/thread-stub/pthread_spin_trylock.c new file mode 100644 index 000000000..5ef1b2a02 --- /dev/null +++ b/thread-stub/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 = 1; + return 0; +} diff --git a/thread-stub/pthread_spin_unlock.c b/thread-stub/pthread_spin_unlock.c new file mode 100644 index 000000000..f9d8a23dc --- /dev/null +++ b/thread-stub/pthread_spin_unlock.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +int pthread_spin_unlock(pthread_spinlock_t *s) +{ + *s = 0; + return 0; +}