diff --git a/Makefile b/Makefile index 4eb619295..935b012b1 100644 --- a/Makefile +++ b/Makefile @@ -76,6 +76,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 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 @@ -142,6 +143,8 @@ 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) @@ -274,88 +277,93 @@ LIBC_TOP_HALF_MUSL_SOURCES += \ ) endif -ifeq ($(THREAD_MODEL), posix) +# pthreads functions (possibly stub) for either thread model LIBC_TOP_HALF_MUSL_SOURCES += \ $(addprefix $(LIBC_TOP_HALF_MUSL_SRC_DIR)/, \ - env/__init_tls.c \ - stdio/__lockfile.c \ - stdio/flockfile.c \ - stdio/ftrylockfile.c \ - stdio/funlockfile.c \ - thread/__lock.c \ - thread/__wait.c \ - thread/__timedwait.c \ thread/default_attr.c \ thread/pthread_attr_destroy.c \ thread/pthread_attr_get.c \ thread/pthread_attr_init.c \ thread/pthread_attr_setdetachstate.c \ thread/pthread_attr_setguardsize.c \ + thread/pthread_attr_setschedparam.c \ thread/pthread_attr_setstack.c \ thread/pthread_attr_setstacksize.c \ - thread/pthread_attr_setschedparam.c \ - thread/pthread_barrier_destroy.c \ - thread/pthread_barrier_init.c \ - thread/pthread_barrier_wait.c \ thread/pthread_barrierattr_destroy.c \ thread/pthread_barrierattr_init.c \ thread/pthread_barrierattr_setpshared.c \ - thread/pthread_cleanup_push.c \ thread/pthread_cancel.c \ - thread/pthread_cond_broadcast.c \ - thread/pthread_cond_destroy.c \ - thread/pthread_cond_init.c \ - thread/pthread_cond_signal.c \ - thread/pthread_cond_timedwait.c \ - thread/pthread_cond_wait.c \ + thread/pthread_cleanup_push.c \ thread/pthread_condattr_destroy.c \ thread/pthread_condattr_init.c \ thread/pthread_condattr_setclock.c \ thread/pthread_condattr_setpshared.c \ - thread/pthread_create.c \ - thread/pthread_detach.c \ thread/pthread_equal.c \ - thread/pthread_getattr_np.c \ thread/pthread_getspecific.c \ - thread/pthread_join.c \ thread/pthread_key_create.c \ - thread/pthread_mutex_consistent.c \ thread/pthread_mutex_destroy.c \ thread/pthread_mutex_init.c \ - thread/pthread_mutex_getprioceiling.c \ - thread/pthread_mutex_lock.c \ - thread/pthread_mutex_timedlock.c \ - thread/pthread_mutex_trylock.c \ - thread/pthread_mutex_unlock.c \ thread/pthread_mutexattr_destroy.c \ thread/pthread_mutexattr_init.c \ thread/pthread_mutexattr_setprotocol.c \ thread/pthread_mutexattr_setpshared.c \ thread/pthread_mutexattr_setrobust.c \ thread/pthread_mutexattr_settype.c \ - thread/pthread_once.c \ thread/pthread_rwlock_destroy.c \ thread/pthread_rwlock_init.c \ - thread/pthread_rwlock_rdlock.c \ - thread/pthread_rwlock_timedrdlock.c \ - thread/pthread_rwlock_timedwrlock.c \ - thread/pthread_rwlock_tryrdlock.c \ - thread/pthread_rwlock_trywrlock.c \ - thread/pthread_rwlock_unlock.c \ - thread/pthread_rwlock_wrlock.c \ thread/pthread_rwlockattr_destroy.c \ thread/pthread_rwlockattr_init.c \ thread/pthread_rwlockattr_setpshared.c \ + thread/pthread_self.c \ thread/pthread_setcancelstate.c \ thread/pthread_setcanceltype.c \ thread/pthread_setspecific.c \ - thread/pthread_self.c \ thread/pthread_spin_destroy.c \ thread/pthread_spin_init.c \ + thread/pthread_testcancel.c \ + ) +ifeq ($(THREAD_MODEL), posix) +# pthreads functions needed for actual thread support +LIBC_TOP_HALF_MUSL_SOURCES += \ + $(addprefix $(LIBC_TOP_HALF_MUSL_SRC_DIR)/, \ + env/__init_tls.c \ + stdio/__lockfile.c \ + stdio/flockfile.c \ + stdio/ftrylockfile.c \ + stdio/funlockfile.c \ + thread/__lock.c \ + thread/__wait.c \ + thread/__timedwait.c \ + thread/pthread_barrier_destroy.c \ + thread/pthread_barrier_init.c \ + thread/pthread_barrier_wait.c \ + thread/pthread_cond_broadcast.c \ + thread/pthread_cond_destroy.c \ + thread/pthread_cond_init.c \ + thread/pthread_cond_signal.c \ + thread/pthread_cond_timedwait.c \ + thread/pthread_cond_wait.c \ + thread/pthread_create.c \ + thread/pthread_detach.c \ + thread/pthread_getattr_np.c \ + thread/pthread_join.c \ + thread/pthread_mutex_consistent.c \ + thread/pthread_mutex_getprioceiling.c \ + thread/pthread_mutex_lock.c \ + thread/pthread_mutex_timedlock.c \ + thread/pthread_mutex_trylock.c \ + thread/pthread_mutex_unlock.c \ + thread/pthread_once.c \ + thread/pthread_rwlock_rdlock.c \ + thread/pthread_rwlock_timedrdlock.c \ + thread/pthread_rwlock_timedwrlock.c \ + thread/pthread_rwlock_tryrdlock.c \ + thread/pthread_rwlock_trywrlock.c \ + thread/pthread_rwlock_unlock.c \ + thread/pthread_rwlock_wrlock.c \ thread/pthread_spin_lock.c \ thread/pthread_spin_trylock.c \ thread/pthread_spin_unlock.c \ - thread/pthread_testcancel.c \ thread/sem_destroy.c \ thread/sem_getvalue.c \ thread/sem_init.c \ @@ -366,6 +374,16 @@ LIBC_TOP_HALF_MUSL_SOURCES += \ thread/wasm32/wasi_thread_start.s \ ) 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 +endif MUSL_PRINTSCAN_SOURCES = \ $(LIBC_TOP_HALF_MUSL_SRC_DIR)/internal/floatscan.c \ @@ -418,10 +436,10 @@ ifeq ($(THREAD_MODEL), posix) # https://reviews.llvm.org/D130053). CFLAGS += -mthread-model posix -pthread -ftls-model=local-exec ASMFLAGS += -matomics +endif # Include cloudlib's directory to access the structure definition of clockid_t CFLAGS += -I$(LIBC_BOTTOM_HALF_CLOUDLIBC_SRC) -endif ifneq ($(LTO),no) ifeq ($(LTO),full) @@ -490,6 +508,7 @@ 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)) @@ -593,6 +612,7 @@ 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)) @@ -608,6 +628,7 @@ 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) \ @@ -649,6 +670,8 @@ $(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) @@ -667,6 +690,8 @@ $(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) @@ -769,6 +794,12 @@ $(FTS_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 += \ @@ -820,6 +851,7 @@ 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 += \ @@ -838,6 +870,10 @@ 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 a94920b90..6e06d64ff 100644 --- a/expected/wasm32-wasip1/defined-symbols.txt +++ b/expected/wasm32-wasip1/defined-symbols.txt @@ -11,6 +11,7 @@ __EINVAL __ENOMEM __SIG_ERR __SIG_IGN +__acquire_ptc __asctime_r __assert_fail __c_dot_utf8 @@ -34,7 +35,11 @@ __ctype_tolower_loc __ctype_toupper_loc __cxa_atexit __cxa_finalize +__default_guardsize +__default_stacksize __des_setkey +__do_cleanup_pop +__do_cleanup_push __do_des __duplocale __env_rm_add @@ -175,10 +180,34 @@ __pow_log_data __powf_log2_data __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_rwlock_rdlock +__pthread_rwlock_timedrdlock +__pthread_rwlock_timedwrlock +__pthread_rwlock_tryrdlock +__pthread_rwlock_trywrlock +__pthread_rwlock_unlock +__pthread_rwlock_wrlock +__pthread_setcancelstate +__pthread_testcancel +__pthread_tsd_main +__pthread_tsd_run_dtors +__pthread_tsd_size __putenv __qsort_r __rand48_step __reallocarray +__release_ptc __rem_pio2 __rem_pio2_large __rem_pio2f @@ -234,6 +263,9 @@ __sysv_signal __tan __tandf __tanl +__testcancel +__tl_lock +__tl_unlock __tm_to_secs __tm_to_tzname __tolower_l @@ -330,6 +362,7 @@ __wasilibc_nocwd_symlinkat __wasilibc_nocwd_utimensat __wasilibc_open_nomode __wasilibc_populate_preopens +__wasilibc_pthread_self __wasilibc_register_preopened_fd __wasilibc_rename_newat __wasilibc_rename_oldat @@ -352,6 +385,8 @@ _environ _exit _flushlbf _initialize +_pthread_cleanup_pop +_pthread_cleanup_push _start a64l abort @@ -921,6 +956,89 @@ program_invocation_name program_invocation_short_name pselect psignal +pthread_attr_destroy +pthread_attr_getdetachstate +pthread_attr_getguardsize +pthread_attr_getschedparam +pthread_attr_getstack +pthread_attr_getstacksize +pthread_attr_init +pthread_attr_setdetachstate +pthread_attr_setguardsize +pthread_attr_setschedparam +pthread_attr_setstack +pthread_attr_setstacksize +pthread_barrier_destroy +pthread_barrier_init +pthread_barrier_wait +pthread_barrierattr_destroy +pthread_barrierattr_getpshared +pthread_barrierattr_init +pthread_barrierattr_setpshared +pthread_cancel +pthread_cond_broadcast +pthread_cond_destroy +pthread_cond_init +pthread_cond_signal +pthread_cond_timedwait +pthread_cond_wait +pthread_condattr_destroy +pthread_condattr_getclock +pthread_condattr_getpshared +pthread_condattr_init +pthread_condattr_setclock +pthread_condattr_setpshared +pthread_create +pthread_detach +pthread_equal +pthread_exit +pthread_getspecific +pthread_join +pthread_key_create +pthread_key_delete +pthread_mutex_consistent +pthread_mutex_destroy +pthread_mutex_init +pthread_mutex_lock +pthread_mutex_timedlock +pthread_mutex_trylock +pthread_mutex_unlock +pthread_mutexattr_destroy +pthread_mutexattr_getprotocol +pthread_mutexattr_getpshared +pthread_mutexattr_getrobust +pthread_mutexattr_gettype +pthread_mutexattr_init +pthread_mutexattr_setprotocol +pthread_mutexattr_setpshared +pthread_mutexattr_setrobust +pthread_mutexattr_settype +pthread_once +pthread_rwlock_destroy +pthread_rwlock_init +pthread_rwlock_rdlock +pthread_rwlock_timedrdlock +pthread_rwlock_timedwrlock +pthread_rwlock_tryrdlock +pthread_rwlock_trywrlock +pthread_rwlock_unlock +pthread_rwlock_wrlock +pthread_rwlockattr_destroy +pthread_rwlockattr_getpshared +pthread_rwlockattr_init +pthread_rwlockattr_setpshared +pthread_self +pthread_setcancelstate +pthread_setcanceltype +pthread_setspecific +pthread_spin_destroy +pthread_spin_init +pthread_spin_lock +pthread_spin_trylock +pthread_spin_unlock +pthread_testcancel +pthread_timedjoin_np +pthread_tryjoin_np putc putc_unlocked putchar @@ -1102,6 +1220,8 @@ tfind tgamma tgammaf tgammal +thrd_current +thrd_equal thrd_sleep time timegm @@ -1123,6 +1243,7 @@ truncate truncf truncl tsearch +tss_get twalk uname ungetc diff --git a/expected/wasm32-wasip1/predefined-macros.txt b/expected/wasm32-wasip1/predefined-macros.txt index 2ddc59bc7..a85cfb340 100644 --- a/expected/wasm32-wasip1/predefined-macros.txt +++ b/expected/wasm32-wasip1/predefined-macros.txt @@ -1463,8 +1463,10 @@ #define PTHREAD_COND_INITIALIZER {{{0}}} #define PTHREAD_CREATE_DETACHED 1 #define PTHREAD_CREATE_JOINABLE 0 +#define PTHREAD_DESTRUCTOR_ITERATIONS 4 #define PTHREAD_EXPLICIT_SCHED 1 #define PTHREAD_INHERIT_SCHED 0 +#define PTHREAD_KEYS_MAX 128 #define PTHREAD_MUTEX_DEFAULT 0 #define PTHREAD_MUTEX_ERRORCHECK 2 #define PTHREAD_MUTEX_INITIALIZER {{{0}}} @@ -1482,6 +1484,7 @@ #define PTHREAD_RWLOCK_INITIALIZER {{{0}}} #define PTHREAD_SCOPE_PROCESS 1 #define PTHREAD_SCOPE_SYSTEM 0 +#define PTHREAD_STACK_MIN 2048 #define PTRBITS (sizeof(char *) * 8) #define PTRDIFF_MAX INT32_MAX #define PTRDIFF_MIN INT32_MIN @@ -3386,7 +3389,12 @@ #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 d850aa3cf..35e5c3913 100644 --- a/expected/wasm32-wasip2/defined-symbols.txt +++ b/expected/wasm32-wasip2/defined-symbols.txt @@ -12,6 +12,7 @@ __EINVAL __ENOMEM __SIG_ERR __SIG_IGN +__acquire_ptc __asctime_r __assert_fail __c_dot_utf8 @@ -37,7 +38,11 @@ __ctype_tolower_loc __ctype_toupper_loc __cxa_atexit __cxa_finalize +__default_guardsize +__default_stacksize __des_setkey +__do_cleanup_pop +__do_cleanup_push __do_des __duplocale __env_rm_add @@ -178,10 +183,34 @@ __pow_log_data __powf_log2_data __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_rwlock_rdlock +__pthread_rwlock_timedrdlock +__pthread_rwlock_timedwrlock +__pthread_rwlock_tryrdlock +__pthread_rwlock_trywrlock +__pthread_rwlock_unlock +__pthread_rwlock_wrlock +__pthread_setcancelstate +__pthread_testcancel +__pthread_tsd_main +__pthread_tsd_run_dtors +__pthread_tsd_size __putenv __qsort_r __rand48_step __reallocarray +__release_ptc __rem_pio2 __rem_pio2_large __rem_pio2f @@ -237,6 +266,9 @@ __sysv_signal __tan __tandf __tanl +__testcancel +__tl_lock +__tl_unlock __tm_to_secs __tm_to_tzname __tolower_l @@ -346,6 +378,7 @@ __wasilibc_nocwd_symlinkat __wasilibc_nocwd_utimensat __wasilibc_open_nomode __wasilibc_populate_preopens +__wasilibc_pthread_self __wasilibc_register_preopened_fd __wasilibc_rename_newat __wasilibc_rename_oldat @@ -368,6 +401,8 @@ _environ _exit _flushlbf _initialize +_pthread_cleanup_pop +_pthread_cleanup_push _start a64l abort @@ -1054,6 +1089,89 @@ program_invocation_name program_invocation_short_name pselect psignal +pthread_attr_destroy +pthread_attr_getdetachstate +pthread_attr_getguardsize +pthread_attr_getschedparam +pthread_attr_getstack +pthread_attr_getstacksize +pthread_attr_init +pthread_attr_setdetachstate +pthread_attr_setguardsize +pthread_attr_setschedparam +pthread_attr_setstack +pthread_attr_setstacksize +pthread_barrier_destroy +pthread_barrier_init +pthread_barrier_wait +pthread_barrierattr_destroy +pthread_barrierattr_getpshared +pthread_barrierattr_init +pthread_barrierattr_setpshared +pthread_cancel +pthread_cond_broadcast +pthread_cond_destroy +pthread_cond_init +pthread_cond_signal +pthread_cond_timedwait +pthread_cond_wait +pthread_condattr_destroy +pthread_condattr_getclock +pthread_condattr_getpshared +pthread_condattr_init +pthread_condattr_setclock +pthread_condattr_setpshared +pthread_create +pthread_detach +pthread_equal +pthread_exit +pthread_getspecific +pthread_join +pthread_key_create +pthread_key_delete +pthread_mutex_consistent +pthread_mutex_destroy +pthread_mutex_init +pthread_mutex_lock +pthread_mutex_timedlock +pthread_mutex_trylock +pthread_mutex_unlock +pthread_mutexattr_destroy +pthread_mutexattr_getprotocol +pthread_mutexattr_getpshared +pthread_mutexattr_getrobust +pthread_mutexattr_gettype +pthread_mutexattr_init +pthread_mutexattr_setprotocol +pthread_mutexattr_setpshared +pthread_mutexattr_setrobust +pthread_mutexattr_settype +pthread_once +pthread_rwlock_destroy +pthread_rwlock_init +pthread_rwlock_rdlock +pthread_rwlock_timedrdlock +pthread_rwlock_timedwrlock +pthread_rwlock_tryrdlock +pthread_rwlock_trywrlock +pthread_rwlock_unlock +pthread_rwlock_wrlock +pthread_rwlockattr_destroy +pthread_rwlockattr_getpshared +pthread_rwlockattr_init +pthread_rwlockattr_setpshared +pthread_self +pthread_setcancelstate +pthread_setcanceltype +pthread_setspecific +pthread_spin_destroy +pthread_spin_init +pthread_spin_lock +pthread_spin_trylock +pthread_spin_unlock +pthread_testcancel +pthread_timedjoin_np +pthread_tryjoin_np putc putc_unlocked putchar @@ -1335,6 +1453,8 @@ tfind tgamma tgammaf tgammal +thrd_current +thrd_equal thrd_sleep time timegm @@ -1356,6 +1476,7 @@ truncate truncf truncl tsearch +tss_get twalk udp_accept udp_bind diff --git a/expected/wasm32-wasip2/predefined-macros.txt b/expected/wasm32-wasip2/predefined-macros.txt index 68b05cbf1..6ebd9803d 100644 --- a/expected/wasm32-wasip2/predefined-macros.txt +++ b/expected/wasm32-wasip2/predefined-macros.txt @@ -1594,8 +1594,10 @@ #define PTHREAD_COND_INITIALIZER {{{0}}} #define PTHREAD_CREATE_DETACHED 1 #define PTHREAD_CREATE_JOINABLE 0 +#define PTHREAD_DESTRUCTOR_ITERATIONS 4 #define PTHREAD_EXPLICIT_SCHED 1 #define PTHREAD_INHERIT_SCHED 0 +#define PTHREAD_KEYS_MAX 128 #define PTHREAD_MUTEX_DEFAULT 0 #define PTHREAD_MUTEX_ERRORCHECK 2 #define PTHREAD_MUTEX_INITIALIZER {{{0}}} @@ -1613,6 +1615,7 @@ #define PTHREAD_RWLOCK_INITIALIZER {{{0}}} #define PTHREAD_SCOPE_PROCESS 1 #define PTHREAD_SCOPE_SYSTEM 0 +#define PTHREAD_STACK_MIN 2048 #define PTRBITS (sizeof(char *) * 8) #define PTRDIFF_MAX INT32_MAX #define PTRDIFF_MIN INT32_MIN @@ -3541,7 +3544,12 @@ #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/limits.h b/libc-top-half/musl/include/limits.h index 2fc0d2a38..c160568cd 100644 --- a/libc-top-half/musl/include/limits.h +++ b/libc-top-half/musl/include/limits.h @@ -65,11 +65,9 @@ /* Implementation choices... */ -#if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) #define PTHREAD_KEYS_MAX 128 #define PTHREAD_STACK_MIN 2048 #define PTHREAD_DESTRUCTOR_ITERATIONS 4 -#endif #if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) #define SEM_VALUE_MAX 0x7fffffff #define SEM_NSEMS_MAX 256 diff --git a/libc-top-half/musl/include/pthread.h b/libc-top-half/musl/include/pthread.h index 2c35d0b51..fa9f66ae8 100644 --- a/libc-top-half/musl/include/pthread.h +++ b/libc-top-half/musl/include/pthread.h @@ -77,12 +77,29 @@ extern "C" { #define PTHREAD_NULL ((pthread_t)0) +#ifdef __wasilibc_unmodified_upstream int pthread_create(pthread_t *__restrict, const pthread_attr_t *__restrict, void *(*)(void *), void *__restrict); int pthread_detach(pthread_t); -#ifdef __wasilibc_unmodified_upstream _Noreturn void pthread_exit(void *); -#endif int pthread_join(pthread_t, void **); +#else +#if defined(_WASI_EMULATED_PTHREAD) || defined(_REENTRANT) +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;}) +#endif +#endif #ifdef __GNUC__ __attribute__((const)) @@ -232,8 +249,17 @@ 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) 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;}) +#endif #endif #if _REDIR_TIME64 diff --git a/libc-top-half/musl/src/internal/pthread_impl.h b/libc-top-half/musl/src/internal/pthread_impl.h index 1e7b9741d..0106ac385 100644 --- a/libc-top-half/musl/src/internal/pthread_impl.h +++ b/libc-top-half/musl/src/internal/pthread_impl.h @@ -186,8 +186,10 @@ static inline void __wake(volatile void *addr, int cnt, int priv) __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); #endif +#endif } static inline void __futexwait(volatile void *addr, int val, int priv) { diff --git a/libc-top-half/musl/src/thread/pthread_self.c b/libc-top-half/musl/src/thread/pthread_self.c index 1f3eee1d1..1ea2856d6 100644 --- a/libc-top-half/musl/src/thread/pthread_self.c +++ b/libc-top-half/musl/src/thread/pthread_self.c @@ -1,8 +1,7 @@ #include "pthread_impl.h" #include -#if !defined(__wasilibc_unmodified_upstream) && defined(__wasm__) && \ - defined(_REENTRANT) +#if !defined(__wasilibc_unmodified_upstream) && defined(__wasm__) _Thread_local struct pthread __wasilibc_pthread_self; #endif diff --git a/stub-pthreads/README.md b/stub-pthreads/README.md new file mode 100644 index 000000000..fe2161000 --- /dev/null +++ b/stub-pthreads/README.md @@ -0,0 +1,16 @@ +# 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 new file mode 100644 index 000000000..e0ceed06a --- /dev/null +++ b/stub-pthreads/barrier.c @@ -0,0 +1,23 @@ +#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 new file mode 100644 index 000000000..de65bac23 --- /dev/null +++ b/stub-pthreads/condvar.c @@ -0,0 +1,36 @@ +#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 new file mode 100644 index 000000000..475bdcf8c --- /dev/null +++ b/stub-pthreads/mutex.c @@ -0,0 +1,67 @@ +#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 new file mode 100644 index 000000000..ce0fc7a29 --- /dev/null +++ b/stub-pthreads/rwlock.c @@ -0,0 +1,60 @@ +#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 new file mode 100644 index 000000000..1099f7e07 --- /dev/null +++ b/stub-pthreads/spinlock.c @@ -0,0 +1,21 @@ + +#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 new file mode 100644 index 000000000..d7e04774f --- /dev/null +++ b/stub-pthreads/stub-pthreads-emulated.c @@ -0,0 +1,40 @@ +// 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 new file mode 100644 index 000000000..0f6328404 --- /dev/null +++ b/stub-pthreads/stub-pthreads-good.c @@ -0,0 +1,43 @@ +// 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); +}