From 50d7d2d02edd45dc9fe2ae7802b815f447631788 Mon Sep 17 00:00:00 2001
From: R <rqou@berkeley.edu>
Date: Wed, 25 Sep 2024 17:57:58 +0100
Subject: [PATCH 01/13] Add dummy implementation of pthread_attr for
 single-threaded model

---
 Makefile                                     | 31 +++++++++++++-------
 expected/wasm32-wasip1/defined-symbols.txt   | 24 +++++++++++++++
 expected/wasm32-wasip1/predefined-macros.txt |  3 ++
 expected/wasm32-wasip2/defined-symbols.txt   | 24 +++++++++++++++
 expected/wasm32-wasip2/predefined-macros.txt |  3 ++
 libc-top-half/musl/include/limits.h          |  2 --
 stub-pthreads/README.md                      | 16 ++++++++++
 stub-pthreads/stub-pthreads-good.c           |  9 ++++++
 8 files changed, 100 insertions(+), 12 deletions(-)
 create mode 100644 stub-pthreads/README.md
 create mode 100644 stub-pthreads/stub-pthreads-good.c

diff --git a/Makefile b/Makefile
index 4eb619295..a6bdcaf5a 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
@@ -274,7 +275,21 @@ LIBC_TOP_HALF_MUSL_SOURCES += \
     )
 endif
 
+# pthreads functions (possibly stub) for either thread model
+LIBC_TOP_HALF_MUSL_SOURCES += \
+    $(addprefix $(LIBC_TOP_HALF_MUSL_SRC_DIR)/, \
+        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 \
+    )
 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 \
@@ -285,15 +300,6 @@ LIBC_TOP_HALF_MUSL_SOURCES += \
         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_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 \
@@ -366,6 +372,11 @@ 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)/stub-pthreads-good.c
+endif
 
 MUSL_PRINTSCAN_SOURCES = \
     $(LIBC_TOP_HALF_MUSL_SRC_DIR)/internal/floatscan.c \
@@ -418,10 +429,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)
diff --git a/expected/wasm32-wasip1/defined-symbols.txt b/expected/wasm32-wasip1/defined-symbols.txt
index a94920b90..f5e9061cf 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,6 +35,8 @@ __ctype_tolower_loc
 __ctype_toupper_loc
 __cxa_atexit
 __cxa_finalize
+__default_guardsize
+__default_stacksize
 __des_setkey
 __do_des
 __duplocale
@@ -179,6 +182,7 @@ __putenv
 __qsort_r
 __rand48_step
 __reallocarray
+__release_ptc
 __rem_pio2
 __rem_pio2_large
 __rem_pio2f
@@ -921,6 +925,26 @@ 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_barrierattr_getpshared
+pthread_condattr_getclock
+pthread_condattr_getpshared
+pthread_mutexattr_getprotocol
+pthread_mutexattr_getpshared
+pthread_mutexattr_getrobust
+pthread_mutexattr_gettype
+pthread_rwlockattr_getpshared
 putc
 putc_unlocked
 putchar
diff --git a/expected/wasm32-wasip1/predefined-macros.txt b/expected/wasm32-wasip1/predefined-macros.txt
index 2ddc59bc7..cb18bf06f 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
diff --git a/expected/wasm32-wasip2/defined-symbols.txt b/expected/wasm32-wasip2/defined-symbols.txt
index d850aa3cf..6306f32ad 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,6 +38,8 @@ __ctype_tolower_loc
 __ctype_toupper_loc
 __cxa_atexit
 __cxa_finalize
+__default_guardsize
+__default_stacksize
 __des_setkey
 __do_des
 __duplocale
@@ -182,6 +185,7 @@ __putenv
 __qsort_r
 __rand48_step
 __reallocarray
+__release_ptc
 __rem_pio2
 __rem_pio2_large
 __rem_pio2f
@@ -1054,6 +1058,26 @@ 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_barrierattr_getpshared
+pthread_condattr_getclock
+pthread_condattr_getpshared
+pthread_mutexattr_getprotocol
+pthread_mutexattr_getpshared
+pthread_mutexattr_getrobust
+pthread_mutexattr_gettype
+pthread_rwlockattr_getpshared
 putc
 putc_unlocked
 putchar
diff --git a/expected/wasm32-wasip2/predefined-macros.txt b/expected/wasm32-wasip2/predefined-macros.txt
index 68b05cbf1..267371225 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
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/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/stub-pthreads-good.c b/stub-pthreads/stub-pthreads-good.c
new file mode 100644
index 000000000..df7e3b04b
--- /dev/null
+++ b/stub-pthreads/stub-pthreads-good.c
@@ -0,0 +1,9 @@
+// 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);

From 7e41cef8ebef3045ffe9c358bd08c4bba0fefea3 Mon Sep 17 00:00:00 2001
From: R <rqou@berkeley.edu>
Date: Tue, 16 Jul 2024 04:52:02 +0100
Subject: [PATCH 02/13] Add a stub implementation of barriers

---
 Makefile                                   |  7 ++++---
 expected/wasm32-wasip1/defined-symbols.txt |  6 ++++++
 expected/wasm32-wasip2/defined-symbols.txt |  6 ++++++
 stub-pthreads/barrier.c                    | 23 ++++++++++++++++++++++
 4 files changed, 39 insertions(+), 3 deletions(-)
 create mode 100644 stub-pthreads/barrier.c

diff --git a/Makefile b/Makefile
index a6bdcaf5a..8e560c53f 100644
--- a/Makefile
+++ b/Makefile
@@ -287,6 +287,9 @@ LIBC_TOP_HALF_MUSL_SOURCES += \
         thread/pthread_attr_setschedparam.c \
         thread/pthread_attr_setstack.c \
         thread/pthread_attr_setstacksize.c \
+        thread/pthread_barrierattr_destroy.c \
+        thread/pthread_barrierattr_init.c \
+        thread/pthread_barrierattr_setpshared.c \
     )
 ifeq ($(THREAD_MODEL), posix)
 # pthreads functions needed for actual thread support
@@ -303,9 +306,6 @@ LIBC_TOP_HALF_MUSL_SOURCES += \
         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 \
@@ -375,6 +375,7 @@ endif
 ifeq ($(THREAD_MODEL), single)
 # pthreads stubs for single-threaded environment
 LIBC_TOP_HALF_MUSL_SOURCES += \
+    $(STUB_PTHREADS_DIR)/barrier.c \
     $(STUB_PTHREADS_DIR)/stub-pthreads-good.c
 endif
 
diff --git a/expected/wasm32-wasip1/defined-symbols.txt b/expected/wasm32-wasip1/defined-symbols.txt
index f5e9061cf..7a531b2e1 100644
--- a/expected/wasm32-wasip1/defined-symbols.txt
+++ b/expected/wasm32-wasip1/defined-symbols.txt
@@ -937,7 +937,13 @@ 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_condattr_getclock
 pthread_condattr_getpshared
 pthread_mutexattr_getprotocol
diff --git a/expected/wasm32-wasip2/defined-symbols.txt b/expected/wasm32-wasip2/defined-symbols.txt
index 6306f32ad..ad8725f78 100644
--- a/expected/wasm32-wasip2/defined-symbols.txt
+++ b/expected/wasm32-wasip2/defined-symbols.txt
@@ -1070,7 +1070,13 @@ 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_condattr_getclock
 pthread_condattr_getpshared
 pthread_mutexattr_getprotocol
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();
+}

From be912c4bfe18286349814442a4262d80bcf7b5c1 Mon Sep 17 00:00:00 2001
From: R <rqou@berkeley.edu>
Date: Tue, 16 Jul 2024 05:42:08 +0100
Subject: [PATCH 03/13] Add a stub implementation of mutexes

---
 Makefile                                   | 17 +++---
 expected/wasm32-wasip1/defined-symbols.txt | 18 ++++++
 expected/wasm32-wasip2/defined-symbols.txt | 18 ++++++
 stub-pthreads/mutex.c                      | 67 ++++++++++++++++++++++
 4 files changed, 112 insertions(+), 8 deletions(-)
 create mode 100644 stub-pthreads/mutex.c

diff --git a/Makefile b/Makefile
index 8e560c53f..1677f7921 100644
--- a/Makefile
+++ b/Makefile
@@ -290,6 +290,14 @@ LIBC_TOP_HALF_MUSL_SOURCES += \
         thread/pthread_barrierattr_destroy.c \
         thread/pthread_barrierattr_init.c \
         thread/pthread_barrierattr_setpshared.c \
+        thread/pthread_mutex_destroy.c \
+        thread/pthread_mutex_init.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 \
     )
 ifeq ($(THREAD_MODEL), posix)
 # pthreads functions needed for actual thread support
@@ -326,19 +334,11 @@ LIBC_TOP_HALF_MUSL_SOURCES += \
         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 \
@@ -376,6 +376,7 @@ ifeq ($(THREAD_MODEL), single)
 # pthreads stubs for single-threaded environment
 LIBC_TOP_HALF_MUSL_SOURCES += \
     $(STUB_PTHREADS_DIR)/barrier.c \
+    $(STUB_PTHREADS_DIR)/mutex.c \
     $(STUB_PTHREADS_DIR)/stub-pthreads-good.c
 endif
 
diff --git a/expected/wasm32-wasip1/defined-symbols.txt b/expected/wasm32-wasip1/defined-symbols.txt
index 7a531b2e1..a31553c33 100644
--- a/expected/wasm32-wasip1/defined-symbols.txt
+++ b/expected/wasm32-wasip1/defined-symbols.txt
@@ -178,6 +178,11 @@ __pow_log_data
 __powf_log2_data
 __progname
 __progname_full
+__pthread_mutex_consistent
+__pthread_mutex_lock
+__pthread_mutex_timedlock
+__pthread_mutex_trylock
+__pthread_mutex_unlock
 __putenv
 __qsort_r
 __rand48_step
@@ -946,10 +951,23 @@ pthread_barrierattr_init
 pthread_barrierattr_setpshared
 pthread_condattr_getclock
 pthread_condattr_getpshared
+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_rwlockattr_getpshared
 putc
 putc_unlocked
diff --git a/expected/wasm32-wasip2/defined-symbols.txt b/expected/wasm32-wasip2/defined-symbols.txt
index ad8725f78..0ee46035d 100644
--- a/expected/wasm32-wasip2/defined-symbols.txt
+++ b/expected/wasm32-wasip2/defined-symbols.txt
@@ -181,6 +181,11 @@ __pow_log_data
 __powf_log2_data
 __progname
 __progname_full
+__pthread_mutex_consistent
+__pthread_mutex_lock
+__pthread_mutex_timedlock
+__pthread_mutex_trylock
+__pthread_mutex_unlock
 __putenv
 __qsort_r
 __rand48_step
@@ -1079,10 +1084,23 @@ pthread_barrierattr_init
 pthread_barrierattr_setpshared
 pthread_condattr_getclock
 pthread_condattr_getpshared
+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_rwlockattr_getpshared
 putc
 putc_unlocked
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);

From 7d6fc8f43c7b0107a863e2bc7509eeafce3a30c0 Mon Sep 17 00:00:00 2001
From: R <rqou@berkeley.edu>
Date: Thu, 18 Jul 2024 03:06:07 +0100
Subject: [PATCH 04/13] Add a stub implementation of condvars

---
 Makefile                                   |  9 +++---
 expected/wasm32-wasip1/defined-symbols.txt | 11 +++++++
 expected/wasm32-wasip2/defined-symbols.txt | 11 +++++++
 stub-pthreads/condvar.c                    | 36 ++++++++++++++++++++++
 4 files changed, 63 insertions(+), 4 deletions(-)
 create mode 100644 stub-pthreads/condvar.c

diff --git a/Makefile b/Makefile
index 1677f7921..c50e977ec 100644
--- a/Makefile
+++ b/Makefile
@@ -290,6 +290,10 @@ LIBC_TOP_HALF_MUSL_SOURCES += \
         thread/pthread_barrierattr_destroy.c \
         thread/pthread_barrierattr_init.c \
         thread/pthread_barrierattr_setpshared.c \
+        thread/pthread_condattr_destroy.c \
+        thread/pthread_condattr_init.c \
+        thread/pthread_condattr_setclock.c \
+        thread/pthread_condattr_setpshared.c \
         thread/pthread_mutex_destroy.c \
         thread/pthread_mutex_init.c \
         thread/pthread_mutexattr_destroy.c \
@@ -322,10 +326,6 @@ LIBC_TOP_HALF_MUSL_SOURCES += \
         thread/pthread_cond_signal.c \
         thread/pthread_cond_timedwait.c \
         thread/pthread_cond_wait.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 \
@@ -376,6 +376,7 @@ 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)/stub-pthreads-good.c
 endif
diff --git a/expected/wasm32-wasip1/defined-symbols.txt b/expected/wasm32-wasip1/defined-symbols.txt
index a31553c33..53cbe78d2 100644
--- a/expected/wasm32-wasip1/defined-symbols.txt
+++ b/expected/wasm32-wasip1/defined-symbols.txt
@@ -178,6 +178,7 @@ __pow_log_data
 __powf_log2_data
 __progname
 __progname_full
+__pthread_cond_timedwait
 __pthread_mutex_consistent
 __pthread_mutex_lock
 __pthread_mutex_timedlock
@@ -949,8 +950,18 @@ pthread_barrierattr_destroy
 pthread_barrierattr_getpshared
 pthread_barrierattr_init
 pthread_barrierattr_setpshared
+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_mutex_consistent
 pthread_mutex_destroy
 pthread_mutex_init
diff --git a/expected/wasm32-wasip2/defined-symbols.txt b/expected/wasm32-wasip2/defined-symbols.txt
index 0ee46035d..feaf1d9a2 100644
--- a/expected/wasm32-wasip2/defined-symbols.txt
+++ b/expected/wasm32-wasip2/defined-symbols.txt
@@ -181,6 +181,7 @@ __pow_log_data
 __powf_log2_data
 __progname
 __progname_full
+__pthread_cond_timedwait
 __pthread_mutex_consistent
 __pthread_mutex_lock
 __pthread_mutex_timedlock
@@ -1082,8 +1083,18 @@ pthread_barrierattr_destroy
 pthread_barrierattr_getpshared
 pthread_barrierattr_init
 pthread_barrierattr_setpshared
+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_mutex_consistent
 pthread_mutex_destroy
 pthread_mutex_init
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 <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);

From ce2d57de007a7f2674b649843df05a13d8dd15c2 Mon Sep 17 00:00:00 2001
From: R <rqou@berkeley.edu>
Date: Thu, 18 Jul 2024 03:13:05 +0100
Subject: [PATCH 05/13] Add a stub implementation of rwlocks

---
 Makefile                                   | 11 ++--
 expected/wasm32-wasip1/defined-symbols.txt | 19 +++++++
 expected/wasm32-wasip2/defined-symbols.txt | 19 +++++++
 stub-pthreads/rwlock.c                     | 60 ++++++++++++++++++++++
 4 files changed, 104 insertions(+), 5 deletions(-)
 create mode 100644 stub-pthreads/rwlock.c

diff --git a/Makefile b/Makefile
index c50e977ec..409b819e0 100644
--- a/Makefile
+++ b/Makefile
@@ -302,6 +302,11 @@ LIBC_TOP_HALF_MUSL_SOURCES += \
         thread/pthread_mutexattr_setpshared.c \
         thread/pthread_mutexattr_setrobust.c \
         thread/pthread_mutexattr_settype.c \
+        thread/pthread_rwlock_destroy.c \
+        thread/pthread_rwlock_init.c \
+        thread/pthread_rwlockattr_destroy.c \
+        thread/pthread_rwlockattr_init.c \
+        thread/pthread_rwlockattr_setpshared.c \
     )
 ifeq ($(THREAD_MODEL), posix)
 # pthreads functions needed for actual thread support
@@ -340,8 +345,6 @@ LIBC_TOP_HALF_MUSL_SOURCES += \
         thread/pthread_mutex_trylock.c \
         thread/pthread_mutex_unlock.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 \
@@ -349,9 +352,6 @@ LIBC_TOP_HALF_MUSL_SOURCES += \
         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_setcancelstate.c \
         thread/pthread_setcanceltype.c \
         thread/pthread_setspecific.c \
@@ -378,6 +378,7 @@ 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)/stub-pthreads-good.c
 endif
 
diff --git a/expected/wasm32-wasip1/defined-symbols.txt b/expected/wasm32-wasip1/defined-symbols.txt
index 53cbe78d2..44ecf1b2b 100644
--- a/expected/wasm32-wasip1/defined-symbols.txt
+++ b/expected/wasm32-wasip1/defined-symbols.txt
@@ -184,6 +184,13 @@ __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
 __putenv
 __qsort_r
 __rand48_step
@@ -979,7 +986,19 @@ pthread_mutexattr_setprotocol
 pthread_mutexattr_setpshared
 pthread_mutexattr_setrobust
 pthread_mutexattr_settype
+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
 putc
 putc_unlocked
 putchar
diff --git a/expected/wasm32-wasip2/defined-symbols.txt b/expected/wasm32-wasip2/defined-symbols.txt
index feaf1d9a2..5b7450837 100644
--- a/expected/wasm32-wasip2/defined-symbols.txt
+++ b/expected/wasm32-wasip2/defined-symbols.txt
@@ -187,6 +187,13 @@ __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
 __putenv
 __qsort_r
 __rand48_step
@@ -1112,7 +1119,19 @@ pthread_mutexattr_setprotocol
 pthread_mutexattr_setpshared
 pthread_mutexattr_setrobust
 pthread_mutexattr_settype
+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
 putc
 putc_unlocked
 putchar
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);

From 406a6f37bc4b0a60aaa866d58f8f9a4eea8f9212 Mon Sep 17 00:00:00 2001
From: R <rqou@berkeley.edu>
Date: Thu, 18 Jul 2024 03:17:03 +0100
Subject: [PATCH 06/13] Add a stub implementation of spinlocks

---
 Makefile                                   |  5 +++--
 expected/wasm32-wasip1/defined-symbols.txt |  5 +++++
 expected/wasm32-wasip2/defined-symbols.txt |  5 +++++
 stub-pthreads/spinlock.c                   | 21 +++++++++++++++++++++
 4 files changed, 34 insertions(+), 2 deletions(-)
 create mode 100644 stub-pthreads/spinlock.c

diff --git a/Makefile b/Makefile
index 409b819e0..63ee108bb 100644
--- a/Makefile
+++ b/Makefile
@@ -307,6 +307,8 @@ LIBC_TOP_HALF_MUSL_SOURCES += \
         thread/pthread_rwlockattr_destroy.c \
         thread/pthread_rwlockattr_init.c \
         thread/pthread_rwlockattr_setpshared.c \
+        thread/pthread_spin_destroy.c \
+        thread/pthread_spin_init.c \
     )
 ifeq ($(THREAD_MODEL), posix)
 # pthreads functions needed for actual thread support
@@ -356,8 +358,6 @@ LIBC_TOP_HALF_MUSL_SOURCES += \
         thread/pthread_setcanceltype.c \
         thread/pthread_setspecific.c \
         thread/pthread_self.c \
-        thread/pthread_spin_destroy.c \
-        thread/pthread_spin_init.c \
         thread/pthread_spin_lock.c \
         thread/pthread_spin_trylock.c \
         thread/pthread_spin_unlock.c \
@@ -379,6 +379,7 @@ LIBC_TOP_HALF_MUSL_SOURCES += \
     $(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
 
diff --git a/expected/wasm32-wasip1/defined-symbols.txt b/expected/wasm32-wasip1/defined-symbols.txt
index 44ecf1b2b..2abefe83e 100644
--- a/expected/wasm32-wasip1/defined-symbols.txt
+++ b/expected/wasm32-wasip1/defined-symbols.txt
@@ -999,6 +999,11 @@ pthread_rwlockattr_destroy
 pthread_rwlockattr_getpshared
 pthread_rwlockattr_init
 pthread_rwlockattr_setpshared
+pthread_spin_destroy
+pthread_spin_init
+pthread_spin_lock
+pthread_spin_trylock
+pthread_spin_unlock
 putc
 putc_unlocked
 putchar
diff --git a/expected/wasm32-wasip2/defined-symbols.txt b/expected/wasm32-wasip2/defined-symbols.txt
index 5b7450837..d947a6de7 100644
--- a/expected/wasm32-wasip2/defined-symbols.txt
+++ b/expected/wasm32-wasip2/defined-symbols.txt
@@ -1132,6 +1132,11 @@ pthread_rwlockattr_destroy
 pthread_rwlockattr_getpshared
 pthread_rwlockattr_init
 pthread_rwlockattr_setpshared
+pthread_spin_destroy
+pthread_spin_init
+pthread_spin_lock
+pthread_spin_trylock
+pthread_spin_unlock
 putc
 putc_unlocked
 putchar
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;
+}

From d0feebaf6df543684495270269c58d779785bd8d Mon Sep 17 00:00:00 2001
From: R <rqou@berkeley.edu>
Date: Wed, 17 Jul 2024 19:42:13 +0100
Subject: [PATCH 07/13] Enable pthread_self

This is needed in order to *correctly* implement things such as
cancellation handlers being invoked on pthread_exit
---
 Makefile                                     | 2 +-
 expected/wasm32-wasip1/defined-symbols.txt   | 3 +++
 expected/wasm32-wasip2/defined-symbols.txt   | 3 +++
 libc-top-half/musl/src/thread/pthread_self.c | 3 +--
 4 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/Makefile b/Makefile
index 63ee108bb..dc80c90c2 100644
--- a/Makefile
+++ b/Makefile
@@ -307,6 +307,7 @@ LIBC_TOP_HALF_MUSL_SOURCES += \
         thread/pthread_rwlockattr_destroy.c \
         thread/pthread_rwlockattr_init.c \
         thread/pthread_rwlockattr_setpshared.c \
+        thread/pthread_self.c \
         thread/pthread_spin_destroy.c \
         thread/pthread_spin_init.c \
     )
@@ -357,7 +358,6 @@ LIBC_TOP_HALF_MUSL_SOURCES += \
         thread/pthread_setcancelstate.c \
         thread/pthread_setcanceltype.c \
         thread/pthread_setspecific.c \
-        thread/pthread_self.c \
         thread/pthread_spin_lock.c \
         thread/pthread_spin_trylock.c \
         thread/pthread_spin_unlock.c \
diff --git a/expected/wasm32-wasip1/defined-symbols.txt b/expected/wasm32-wasip1/defined-symbols.txt
index 2abefe83e..b595ebed7 100644
--- a/expected/wasm32-wasip1/defined-symbols.txt
+++ b/expected/wasm32-wasip1/defined-symbols.txt
@@ -347,6 +347,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
@@ -999,6 +1000,7 @@ pthread_rwlockattr_destroy
 pthread_rwlockattr_getpshared
 pthread_rwlockattr_init
 pthread_rwlockattr_setpshared
+pthread_self
 pthread_spin_destroy
 pthread_spin_init
 pthread_spin_lock
@@ -1185,6 +1187,7 @@ tfind
 tgamma
 tgammaf
 tgammal
+thrd_current
 thrd_sleep
 time
 timegm
diff --git a/expected/wasm32-wasip2/defined-symbols.txt b/expected/wasm32-wasip2/defined-symbols.txt
index d947a6de7..d687b9972 100644
--- a/expected/wasm32-wasip2/defined-symbols.txt
+++ b/expected/wasm32-wasip2/defined-symbols.txt
@@ -363,6 +363,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
@@ -1132,6 +1133,7 @@ pthread_rwlockattr_destroy
 pthread_rwlockattr_getpshared
 pthread_rwlockattr_init
 pthread_rwlockattr_setpshared
+pthread_self
 pthread_spin_destroy
 pthread_spin_init
 pthread_spin_lock
@@ -1418,6 +1420,7 @@ tfind
 tgamma
 tgammaf
 tgammal
+thrd_current
 thrd_sleep
 time
 timegm
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 <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
 

From cc0fbfe32cdfa4cb9ef7e8d115e339d72d42694d Mon Sep 17 00:00:00 2001
From: R <rqou@berkeley.edu>
Date: Wed, 17 Jul 2024 19:58:42 +0100
Subject: [PATCH 08/13] Enable a number of functions that now work now that we
 have pthread_self

---
 Makefile                                   | 16 ++++++++--------
 expected/wasm32-wasip1/defined-symbols.txt | 21 +++++++++++++++++++++
 expected/wasm32-wasip2/defined-symbols.txt | 21 +++++++++++++++++++++
 3 files changed, 50 insertions(+), 8 deletions(-)

diff --git a/Makefile b/Makefile
index dc80c90c2..e70709b91 100644
--- a/Makefile
+++ b/Makefile
@@ -290,10 +290,14 @@ LIBC_TOP_HALF_MUSL_SOURCES += \
         thread/pthread_barrierattr_destroy.c \
         thread/pthread_barrierattr_init.c \
         thread/pthread_barrierattr_setpshared.c \
+        thread/pthread_cancel.c \
         thread/pthread_condattr_destroy.c \
         thread/pthread_condattr_init.c \
         thread/pthread_condattr_setclock.c \
         thread/pthread_condattr_setpshared.c \
+        thread/pthread_equal.c \
+        thread/pthread_getspecific.c \
+        thread/pthread_key_create.c \
         thread/pthread_mutex_destroy.c \
         thread/pthread_mutex_init.c \
         thread/pthread_mutexattr_destroy.c \
@@ -308,8 +312,12 @@ LIBC_TOP_HALF_MUSL_SOURCES += \
         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_spin_destroy.c \
         thread/pthread_spin_init.c \
+        thread/pthread_testcancel.c \
     )
 ifeq ($(THREAD_MODEL), posix)
 # pthreads functions needed for actual thread support
@@ -327,7 +335,6 @@ LIBC_TOP_HALF_MUSL_SOURCES += \
         thread/pthread_barrier_init.c \
         thread/pthread_barrier_wait.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 \
@@ -336,11 +343,8 @@ LIBC_TOP_HALF_MUSL_SOURCES += \
         thread/pthread_cond_wait.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_getprioceiling.c \
         thread/pthread_mutex_lock.c \
@@ -355,13 +359,9 @@ LIBC_TOP_HALF_MUSL_SOURCES += \
         thread/pthread_rwlock_trywrlock.c \
         thread/pthread_rwlock_unlock.c \
         thread/pthread_rwlock_wrlock.c \
-        thread/pthread_setcancelstate.c \
-        thread/pthread_setcanceltype.c \
-        thread/pthread_setspecific.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 \
diff --git a/expected/wasm32-wasip1/defined-symbols.txt b/expected/wasm32-wasip1/defined-symbols.txt
index b595ebed7..5862aadf9 100644
--- a/expected/wasm32-wasip1/defined-symbols.txt
+++ b/expected/wasm32-wasip1/defined-symbols.txt
@@ -179,6 +179,8 @@ __powf_log2_data
 __progname
 __progname_full
 __pthread_cond_timedwait
+__pthread_key_create
+__pthread_key_delete
 __pthread_mutex_consistent
 __pthread_mutex_lock
 __pthread_mutex_timedlock
@@ -191,6 +193,11 @@ __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
@@ -251,6 +258,9 @@ __sysv_signal
 __tan
 __tandf
 __tanl
+__testcancel
+__tl_lock
+__tl_unlock
 __tm_to_secs
 __tm_to_tzname
 __tolower_l
@@ -958,6 +968,7 @@ pthread_barrierattr_destroy
 pthread_barrierattr_getpshared
 pthread_barrierattr_init
 pthread_barrierattr_setpshared
+pthread_cancel
 pthread_cond_broadcast
 pthread_cond_destroy
 pthread_cond_init
@@ -970,6 +981,10 @@ pthread_condattr_getpshared
 pthread_condattr_init
 pthread_condattr_setclock
 pthread_condattr_setpshared
+pthread_equal
+pthread_getspecific
+pthread_key_create
+pthread_key_delete
 pthread_mutex_consistent
 pthread_mutex_destroy
 pthread_mutex_init
@@ -1001,11 +1016,15 @@ 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
 putc
 putc_unlocked
 putchar
@@ -1188,6 +1207,7 @@ tgamma
 tgammaf
 tgammal
 thrd_current
+thrd_equal
 thrd_sleep
 time
 timegm
@@ -1209,6 +1229,7 @@ truncate
 truncf
 truncl
 tsearch
+tss_get
 twalk
 uname
 ungetc
diff --git a/expected/wasm32-wasip2/defined-symbols.txt b/expected/wasm32-wasip2/defined-symbols.txt
index d687b9972..db80ee882 100644
--- a/expected/wasm32-wasip2/defined-symbols.txt
+++ b/expected/wasm32-wasip2/defined-symbols.txt
@@ -182,6 +182,8 @@ __powf_log2_data
 __progname
 __progname_full
 __pthread_cond_timedwait
+__pthread_key_create
+__pthread_key_delete
 __pthread_mutex_consistent
 __pthread_mutex_lock
 __pthread_mutex_timedlock
@@ -194,6 +196,11 @@ __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
@@ -254,6 +261,9 @@ __sysv_signal
 __tan
 __tandf
 __tanl
+__testcancel
+__tl_lock
+__tl_unlock
 __tm_to_secs
 __tm_to_tzname
 __tolower_l
@@ -1091,6 +1101,7 @@ pthread_barrierattr_destroy
 pthread_barrierattr_getpshared
 pthread_barrierattr_init
 pthread_barrierattr_setpshared
+pthread_cancel
 pthread_cond_broadcast
 pthread_cond_destroy
 pthread_cond_init
@@ -1103,6 +1114,10 @@ pthread_condattr_getpshared
 pthread_condattr_init
 pthread_condattr_setclock
 pthread_condattr_setpshared
+pthread_equal
+pthread_getspecific
+pthread_key_create
+pthread_key_delete
 pthread_mutex_consistent
 pthread_mutex_destroy
 pthread_mutex_init
@@ -1134,11 +1149,15 @@ 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
 putc
 putc_unlocked
 putchar
@@ -1421,6 +1440,7 @@ tgamma
 tgammaf
 tgammal
 thrd_current
+thrd_equal
 thrd_sleep
 time
 timegm
@@ -1442,6 +1462,7 @@ truncate
 truncf
 truncl
 tsearch
+tss_get
 twalk
 udp_accept
 udp_bind

From 45adc9fa215701fc23405af52a992c5bd0571502 Mon Sep 17 00:00:00 2001
From: R <rqou@berkeley.edu>
Date: Tue, 6 Aug 2024 21:13:39 +0100
Subject: [PATCH 09/13] Don't invoke notify builtin in single-threaded mode

This fixes compatibility with old versions of clang
---
 libc-top-half/musl/src/internal/pthread_impl.h | 2 ++
 1 file changed, 2 insertions(+)

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

From 7ea3949cbf23a33077de718e0191cfaafac78f35 Mon Sep 17 00:00:00 2001
From: R <rqou@berkeley.edu>
Date: Wed, 25 Sep 2024 18:16:25 +0100
Subject: [PATCH 10/13] Add a stub for pthread_once

---
 expected/wasm32-wasip1/defined-symbols.txt | 1 +
 expected/wasm32-wasip2/defined-symbols.txt | 1 +
 stub-pthreads/stub-pthreads-good.c         | 9 +++++++++
 3 files changed, 11 insertions(+)

diff --git a/expected/wasm32-wasip1/defined-symbols.txt b/expected/wasm32-wasip1/defined-symbols.txt
index 5862aadf9..ed482ba95 100644
--- a/expected/wasm32-wasip1/defined-symbols.txt
+++ b/expected/wasm32-wasip1/defined-symbols.txt
@@ -1002,6 +1002,7 @@ pthread_mutexattr_setprotocol
 pthread_mutexattr_setpshared
 pthread_mutexattr_setrobust
 pthread_mutexattr_settype
+pthread_once
 pthread_rwlock_destroy
 pthread_rwlock_init
 pthread_rwlock_rdlock
diff --git a/expected/wasm32-wasip2/defined-symbols.txt b/expected/wasm32-wasip2/defined-symbols.txt
index db80ee882..28aba8869 100644
--- a/expected/wasm32-wasip2/defined-symbols.txt
+++ b/expected/wasm32-wasip2/defined-symbols.txt
@@ -1135,6 +1135,7 @@ pthread_mutexattr_setprotocol
 pthread_mutexattr_setpshared
 pthread_mutexattr_setrobust
 pthread_mutexattr_settype
+pthread_once
 pthread_rwlock_destroy
 pthread_rwlock_init
 pthread_rwlock_rdlock
diff --git a/stub-pthreads/stub-pthreads-good.c b/stub-pthreads/stub-pthreads-good.c
index df7e3b04b..3c059b324 100644
--- a/stub-pthreads/stub-pthreads-good.c
+++ b/stub-pthreads/stub-pthreads-good.c
@@ -7,3 +7,12 @@ static void dummy_0()
 }
 weak_alias(dummy_0, __acquire_ptc);
 weak_alias(dummy_0, __release_ptc);
+
+int pthread_once(pthread_once_t *control, void (*init)(void))
+{
+	if (!*control) {
+		init();
+		*control = 1;
+	}
+	return 0;
+}

From 93fd9f5e3262b13dc6aa70a0aa2e64096e9ffd89 Mon Sep 17 00:00:00 2001
From: R <rqou@berkeley.edu>
Date: Wed, 17 Jul 2024 23:34:06 +0100
Subject: [PATCH 11/13] Implement single_threaded pthread_exit

This is "good" as it actually does perform
all the cancellation actions and then calls exit()
---
 Makefile                                   |  2 +-
 expected/wasm32-wasip1/defined-symbols.txt |  5 +++++
 expected/wasm32-wasip2/defined-symbols.txt |  5 +++++
 stub-pthreads/stub-pthreads-good.c         | 25 ++++++++++++++++++++++
 4 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/Makefile b/Makefile
index e70709b91..51da30f22 100644
--- a/Makefile
+++ b/Makefile
@@ -291,6 +291,7 @@ LIBC_TOP_HALF_MUSL_SOURCES += \
         thread/pthread_barrierattr_init.c \
         thread/pthread_barrierattr_setpshared.c \
         thread/pthread_cancel.c \
+        thread/pthread_cleanup_push.c \
         thread/pthread_condattr_destroy.c \
         thread/pthread_condattr_init.c \
         thread/pthread_condattr_setclock.c \
@@ -334,7 +335,6 @@ LIBC_TOP_HALF_MUSL_SOURCES += \
         thread/pthread_barrier_destroy.c \
         thread/pthread_barrier_init.c \
         thread/pthread_barrier_wait.c \
-        thread/pthread_cleanup_push.c \
         thread/pthread_cond_broadcast.c \
         thread/pthread_cond_destroy.c \
         thread/pthread_cond_init.c \
diff --git a/expected/wasm32-wasip1/defined-symbols.txt b/expected/wasm32-wasip1/defined-symbols.txt
index ed482ba95..15c306b2e 100644
--- a/expected/wasm32-wasip1/defined-symbols.txt
+++ b/expected/wasm32-wasip1/defined-symbols.txt
@@ -38,6 +38,8 @@ __cxa_finalize
 __default_guardsize
 __default_stacksize
 __des_setkey
+__do_cleanup_pop
+__do_cleanup_push
 __do_des
 __duplocale
 __env_rm_add
@@ -380,6 +382,8 @@ _environ
 _exit
 _flushlbf
 _initialize
+_pthread_cleanup_pop
+_pthread_cleanup_push
 _start
 a64l
 abort
@@ -982,6 +986,7 @@ pthread_condattr_init
 pthread_condattr_setclock
 pthread_condattr_setpshared
 pthread_equal
+pthread_exit
 pthread_getspecific
 pthread_key_create
 pthread_key_delete
diff --git a/expected/wasm32-wasip2/defined-symbols.txt b/expected/wasm32-wasip2/defined-symbols.txt
index 28aba8869..2c52ecd7c 100644
--- a/expected/wasm32-wasip2/defined-symbols.txt
+++ b/expected/wasm32-wasip2/defined-symbols.txt
@@ -41,6 +41,8 @@ __cxa_finalize
 __default_guardsize
 __default_stacksize
 __des_setkey
+__do_cleanup_pop
+__do_cleanup_push
 __do_des
 __duplocale
 __env_rm_add
@@ -396,6 +398,8 @@ _environ
 _exit
 _flushlbf
 _initialize
+_pthread_cleanup_pop
+_pthread_cleanup_push
 _start
 a64l
 abort
@@ -1115,6 +1119,7 @@ pthread_condattr_init
 pthread_condattr_setclock
 pthread_condattr_setpshared
 pthread_equal
+pthread_exit
 pthread_getspecific
 pthread_key_create
 pthread_key_delete
diff --git a/stub-pthreads/stub-pthreads-good.c b/stub-pthreads/stub-pthreads-good.c
index 3c059b324..0f6328404 100644
--- a/stub-pthreads/stub-pthreads-good.c
+++ b/stub-pthreads/stub-pthreads-good.c
@@ -7,6 +7,7 @@ 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))
 {
@@ -16,3 +17,27 @@ int pthread_once(pthread_once_t *control, void (*init)(void))
 	}
 	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);
+}

From 1b0706dc96a743a99289ff78c594fbe60e92c812 Mon Sep 17 00:00:00 2001
From: R <rqou@berkeley.edu>
Date: Wed, 25 Sep 2024 18:38:20 +0100
Subject: [PATCH 12/13] Add emulated thread-creation-related functions

These functions always fail
---
 Makefile                                   | 19 ++++++++++
 expected/wasm32-wasip1/defined-symbols.txt |  8 +++++
 expected/wasm32-wasip2/defined-symbols.txt |  8 +++++
 stub-pthreads/stub-pthreads-emulated.c     | 40 ++++++++++++++++++++++
 4 files changed, 75 insertions(+)
 create mode 100644 stub-pthreads/stub-pthreads-emulated.c

diff --git a/Makefile b/Makefile
index 51da30f22..c4796c409 100644
--- a/Makefile
+++ b/Makefile
@@ -143,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)
@@ -506,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))
@@ -609,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))
@@ -624,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) \
@@ -665,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)
@@ -683,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)
@@ -785,6 +794,11 @@ $(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
+
 # 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 += \
@@ -836,6 +850,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 += \
@@ -854,6 +869,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 15c306b2e..6e06d64ff 100644
--- a/expected/wasm32-wasip1/defined-symbols.txt
+++ b/expected/wasm32-wasip1/defined-symbols.txt
@@ -181,6 +181,9 @@ __powf_log2_data
 __progname
 __progname_full
 __pthread_cond_timedwait
+__pthread_create
+__pthread_detach
+__pthread_join
 __pthread_key_create
 __pthread_key_delete
 __pthread_mutex_consistent
@@ -985,9 +988,12 @@ 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
@@ -1031,6 +1037,8 @@ pthread_spin_lock
 pthread_spin_trylock
 pthread_spin_unlock
 pthread_testcancel
+pthread_timedjoin_np
+pthread_tryjoin_np
 putc
 putc_unlocked
 putchar
diff --git a/expected/wasm32-wasip2/defined-symbols.txt b/expected/wasm32-wasip2/defined-symbols.txt
index 2c52ecd7c..35e5c3913 100644
--- a/expected/wasm32-wasip2/defined-symbols.txt
+++ b/expected/wasm32-wasip2/defined-symbols.txt
@@ -184,6 +184,9 @@ __powf_log2_data
 __progname
 __progname_full
 __pthread_cond_timedwait
+__pthread_create
+__pthread_detach
+__pthread_join
 __pthread_key_create
 __pthread_key_delete
 __pthread_mutex_consistent
@@ -1118,9 +1121,12 @@ 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
@@ -1164,6 +1170,8 @@ pthread_spin_lock
 pthread_spin_trylock
 pthread_spin_unlock
 pthread_testcancel
+pthread_timedjoin_np
+pthread_tryjoin_np
 putc
 putc_unlocked
 putchar
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;
+}

From 9b80a8ea43899cc5aa14a9049e6d38a0649439a4 Mon Sep 17 00:00:00 2001
From: R <rqou@berkeley.edu>
Date: Wed, 25 Sep 2024 20:57:04 +0100
Subject: [PATCH 13/13] Make always-fail functions error if
 _WASI_EMULATED_PTHREAD isn't defined

---
 Makefile                                     |  3 +-
 expected/wasm32-wasip1/predefined-macros.txt |  5 ++++
 expected/wasm32-wasip2/predefined-macros.txt |  5 ++++
 libc-top-half/musl/include/pthread.h         | 30 ++++++++++++++++++--
 4 files changed, 40 insertions(+), 3 deletions(-)

diff --git a/Makefile b/Makefile
index c4796c409..935b012b1 100644
--- a/Makefile
+++ b/Makefile
@@ -797,7 +797,8 @@ $(LIBWASI_EMULATED_PROCESS_CLOCKS_OBJS) $(LIBWASI_EMULATED_PROCESS_CLOCKS_SO_OBJ
 $(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
+    -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.
diff --git a/expected/wasm32-wasip1/predefined-macros.txt b/expected/wasm32-wasip1/predefined-macros.txt
index cb18bf06f..a85cfb340 100644
--- a/expected/wasm32-wasip1/predefined-macros.txt
+++ b/expected/wasm32-wasip1/predefined-macros.txt
@@ -3389,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/predefined-macros.txt b/expected/wasm32-wasip2/predefined-macros.txt
index 267371225..6ebd9803d 100644
--- a/expected/wasm32-wasip2/predefined-macros.txt
+++ b/expected/wasm32-wasip2/predefined-macros.txt
@@ -3544,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/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 <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