Browse Source

Implement a stub pthreads library for `THREAD_MODEL=single` (#518)

~~This patch series first starts with a number of commits stubbing out
functions in the existing `THREAD_model=posix` code. According to "The
Open Group Base Specifications Issue 7, 2018 edition", there are a
number of mandatory functions which have not been provided. There are
also some optional functions that have been partially provided in a
not-useful way (e.g. get but no set function). For these, I have chosen
to clean them up and remove the get functions for consistency.~~ EDIT:
These have been split off into separate PRs and merged.

The remainder of the patches then build up a stub implementation of
pthreads for `THREAD_MODEL=single`. I have done my best to try to make
sure that all functions are as conforming as possible (under the
assumption that another thread cannot ever be launched). This means that
objects such as mutexes and rwlocks actually do update their state and
will correctly fail when locks cannot be acquired.

When an inevitable deadlock occurs, I have chosen to return EDEADLK when
it has been explicitly listed as a permissible return value, and to
invoke `__builtin_trap` otherwise.

I have tested this by rebuilding libc++ with threads enabled and then
smoke-testing Clang/LLVM-on-WASI to make sure that it can compile a
simple program. I have not run any more-extensive conformance testing.

Fixes #501
main
R 4 weeks ago
committed by GitHub
parent
commit
a05277a680
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 118
      Makefile
  2. 121
      expected/wasm32-wasip1/defined-symbols.txt
  3. 8
      expected/wasm32-wasip1/predefined-macros.txt
  4. 121
      expected/wasm32-wasip2/defined-symbols.txt
  5. 8
      expected/wasm32-wasip2/predefined-macros.txt
  6. 2
      libc-top-half/musl/include/limits.h
  7. 30
      libc-top-half/musl/include/pthread.h
  8. 2
      libc-top-half/musl/src/internal/pthread_impl.h
  9. 3
      libc-top-half/musl/src/thread/pthread_self.c
  10. 16
      stub-pthreads/README.md
  11. 23
      stub-pthreads/barrier.c
  12. 36
      stub-pthreads/condvar.c
  13. 67
      stub-pthreads/mutex.c
  14. 60
      stub-pthreads/rwlock.c
  15. 21
      stub-pthreads/spinlock.c
  16. 40
      stub-pthreads/stub-pthreads-emulated.c
  17. 43
      stub-pthreads/stub-pthreads-good.c

118
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

121
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

8
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

121
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

8
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

2
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

30
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 <assert.h>
#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

2
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)
{

3
libc-top-half/musl/src/thread/pthread_self.c

@ -1,8 +1,7 @@
#include "pthread_impl.h"
#include <threads.h>
#if !defined(__wasilibc_unmodified_upstream) && defined(__wasm__) && \
defined(_REENTRANT)
#if !defined(__wasilibc_unmodified_upstream) && defined(__wasm__)
_Thread_local struct pthread __wasilibc_pthread_self;
#endif

16
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.

23
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();
}

36
stub-pthreads/condvar.c

@ -0,0 +1,36 @@
#include "pthread_impl.h"
#include <time.h>
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);

67
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);

60
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);

21
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;
}

40
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;
}

43
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);
}
Loading…
Cancel
Save