Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
421005c
Restructure thread impls
TartanLlama Feb 11, 2026
fd62b0b
Cmake fixes
TartanLlama Feb 11, 2026
974fc8a
Common files
TartanLlama Feb 11, 2026
2789d8f
Rename folder
TartanLlama Feb 11, 2026
8bf9a00
First pass
TartanLlama Feb 20, 2026
9f4cb70
Merge branch 'main' into sy/coop-threading
TartanLlama Feb 20, 2026
ed7f5d2
Merge in WASIP3
TartanLlama Feb 20, 2026
bd108e2
Linking fixes
TartanLlama Mar 2, 2026
5f6f5ae
Various clocks and build fixes
TartanLlama Mar 2, 2026
6bc21e1
Lock fixes
TartanLlama Mar 2, 2026
2f613cc
Sysconf changes
TartanLlama Mar 2, 2026
8f2a99c
sem_destroy fixes
TartanLlama Mar 2, 2026
16d406a
Link and test updates
TartanLlama Mar 4, 2026
5b95b09
Merge branch 'main' into sy/coop-threading
TartanLlama Mar 4, 2026
72ac6f8
WASIp3 bump
TartanLlama Mar 4, 2026
36d2c17
crt1 bump
TartanLlama Mar 4, 2026
aba8568
Fix bad lock initializations
TartanLlama Mar 12, 2026
3647168
Ifdef lock
TartanLlama Mar 12, 2026
e055edf
typedef locks with no threading
TartanLlama Mar 12, 2026
c10da06
Unused variable warnings
TartanLlama Mar 12, 2026
7b431f1
More lock nonsense
TartanLlama Mar 12, 2026
787c289
Redo lock changes
TartanLlama Mar 12, 2026
1000351
Move lock_ptc
TartanLlama Mar 12, 2026
1fc2a40
OFL lock fix
TartanLlama Mar 12, 2026
c37f771
Add wasi-threads sem_destroy
TartanLlama Mar 12, 2026
0ef525a
wasi-threads fixes
TartanLlama Mar 12, 2026
cfec5f1
wasi-threads fixes
TartanLlama Mar 12, 2026
c78d35c
wasi-threads fixes
TartanLlama Mar 12, 2026
73dff1a
wasi-threads fixes
TartanLlama Mar 12, 2026
df6a4da
Pointer shenanigans
TartanLlama Mar 12, 2026
35b1ed9
Pointer shenanigans
TartanLlama Mar 12, 2026
d8fb9a6
Pointer shenanigans
TartanLlama Mar 12, 2026
cb5f6a4
More lock changes
TartanLlama Mar 12, 2026
cce6bc0
Defined symbol changes
TartanLlama Mar 12, 2026
53be6e5
Bump test suite
TartanLlama Mar 12, 2026
6e3518a
Call compiler-rt
TartanLlama Mar 23, 2026
ffdf6b0
Merge branch 'main' into sy/coop-threading
TartanLlama Mar 23, 2026
fd5840e
Correct mtx cmake
TartanLlama Mar 23, 2026
ddd75e1
Update
TartanLlama Jun 1, 2026
e02f07d
Merge branch 'main' into sy/coop-threading
TartanLlama Jun 12, 2026
4d2e463
Rename coop threads macro
TartanLlama Jun 12, 2026
d7961f8
Lock name fixes
TartanLlama Jun 12, 2026
8fe99d7
Fix macro name
TartanLlama Jun 12, 2026
84383bb
Fix lock issue
TartanLlama Jun 22, 2026
5d1f328
Remove stale files
TartanLlama Jun 22, 2026
d249f32
Merge branch 'main' into sy/coop-threading
TartanLlama Jun 22, 2026
3c017ff
Move thread index retrieval
TartanLlama Jun 22, 2026
112e662
Move TLS allocation to C
TartanLlama Jun 22, 2026
7c413fa
Add configuration option
TartanLlama Jun 24, 2026
f753690
Update expected symbols on wasip1-threads
alexcrichton Jun 29, 2026
686e708
Adjust wasip3 coop threads build
alexcrichton Jun 29, 2026
8a922b1
Run clang-format
alexcrichton Jun 29, 2026
9b7bedc
Add comment for new version.h define
alexcrichton Jun 29, 2026
413ed50
Avoid `FetchContent`
alexcrichton Jun 29, 2026
4df0e1d
Add missing #define
alexcrichton Jun 29, 2026
f4c4149
Include `__libcall_thread_context.s` on all clang 23+ builds
alexcrichton Jun 29, 2026
ab97506
Don't test a link
alexcrichton Jun 29, 2026
f47e879
Adjust libcall_thread_context.S
alexcrichton Jun 29, 2026
f198075
More adjustments for wasip3
alexcrichton Jun 29, 2026
285ad52
Don't initialize TLS on each task
alexcrichton Jun 29, 2026
3103216
Add pthread dep since clang seems to want it
alexcrichton Jun 30, 2026
80bf71d
Move configuring V8 tests into CMake
alexcrichton Jun 30, 2026
a56a46d
Use a published version of wasmtime
alexcrichton Jun 30, 2026
8db2fce
Merge remote-tracking branch 'origin/main' into wat
alexcrichton Jun 30, 2026
9016dd8
Update wasm-component-ld to support `--cooperative-threading`
alexcrichton Jun 30, 2026
b8360a0
Remove a debug printf
alexcrichton Jun 30, 2026
8dd056e
Add a missing header for the wasi defines
alexcrichton Jun 30, 2026
4ef1350
Prevent a double-free when detaching a joined thread
alexcrichton Jun 30, 2026
0248c96
Run clang-format over coop-threads
alexcrichton Jun 30, 2026
7a4f451
Remove THREADS from CMake
alexcrichton Jun 30, 2026
dad2471
Trim flags for coop threads
alexcrichton Jun 30, 2026
1a3cc4d
Remove stray file
alexcrichton Jun 30, 2026
7147fb2
Enable adding unit tests for coop threads
alexcrichton Jun 30, 2026
e22446e
Fix sem_wait wakeup with multiple waiters
alexcrichton Jun 30, 2026
29ac9cb
Fix losing wakeups in pthread_cond_signal
alexcrichton Jun 30, 2026
c8d7402
Fix dropping locked files on thread exit
alexcrichton Jun 30, 2026
b86cddd
Fix a deadlock issue with the cwd lock
alexcrichton Jun 30, 2026
0af5aca
Fix an issue in `__waitlist_wake_all`
alexcrichton Jul 1, 2026
f5c7666
Fix a UAF in `pthread_detach`
alexcrichton Jul 1, 2026
6a6d4b6
Handle allocation failure in `__allocate_tls`
alexcrichton Jul 1, 2026
0132240
Adjust waiters in pthread_once
alexcrichton Jul 1, 2026
5f65090
Don't set map_base to NULL, it's ub
alexcrichton Jul 1, 2026
8770137
Don't yield to joiners when exiting a thread
alexcrichton Jul 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 35 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ if (CMAKE_HOST_WIN32)
set(CMAKE_INCLUDE_SYSTEM_FLAG_C "-isystem ")
endif()

set(TARGET_TRIPLE "wasm32-wasip1" CACHE STRING "WASI target to test")
set(CMAKE_C_COMPILER_TARGET ${TARGET_TRIPLE})

# We're building libc, and the compiler being used may not have a wasm sysroot
# for libc, so skip this check. Otherwise the `project` call below attempts a
# link to see if the compiler is working and if that fails it doesn't mean it
# doesn't work. Let the build keep going to see what happens.
set(CMAKE_C_COMPILER_WORKS 1)

project(wasi-libc LANGUAGES C ASM)

if(NOT CMAKE_C_COMPILER_ID MATCHES Clang)
Expand All @@ -36,7 +45,6 @@ message(STATUS "Found executable for `nm`: ${CMAKE_NM}")
message(STATUS "Found executable for `ar`: ${CMAKE_AR}")
message(STATUS "Found executable for `ranlib`: ${CMAKE_RANLIB}")

set(TARGET_TRIPLE "wasm32-wasip1" CACHE STRING "WASI target to test")
# Note: thin LTO here is just for experimentation. It has known issues:
# - https://github.com/llvm/llvm-project/issues/91700
# - https://github.com/llvm/llvm-project/issues/91711
Expand All @@ -51,6 +59,7 @@ option(BUILD_SHARED "Whether or not to build shared libraries" ON)
option(CHECK_SYMBOLS "Whether or not to check the exported symbols of libc.a" OFF)
set(WASI_SDK_VERSION "" CACHE STRING "Version information for wasi-sdk to embed in headers")
option(ENABLE_WERROR "Whether to compile with `-Werror`" OFF)
option(ENABLE_COOP_THREADS "Whether to compile with cooperative threads support" OFF)

if(TARGET_TRIPLE MATCHES "-threads$")
set(WASI p1)
Expand Down Expand Up @@ -86,10 +95,10 @@ else()
endif()

if(TARGET_TRIPLE MATCHES "-threads$")
set(THREADS ON)
add_compile_options(-mthread-model posix -pthread -ftls-model=local-exec -matomics)
elseif(ENABLE_COOP_THREADS)
add_compile_options(-mthread-model posix -pthread -ftls-model=local-exec)
else()
set(THREADS OFF)
add_compile_options(-mthread-model single)
endif()

Expand Down Expand Up @@ -209,6 +218,28 @@ if (ENABLE_WERROR)
add_compile_options(-Werror)
endif()

# =============================================================================
# Detection of a libcall thread context for wasip3

include(CheckSourceCompiles)
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
check_source_compiles(C [[
#ifndef __wasm_libcall_thread_context__
#error "missing define"
#endif
]] HAVE_WASM_LIBCALL_THREAD_CONTEXT)

if(ENABLE_COOP_THREADS)
if(NOT WASI STREQUAL "p3")
message(FATAL_ERROR "Cooperative threads are only supported on WASIp3")
endif()
if (NOT HAVE_WASM_LIBCALL_THREAD_CONTEXT)
message(FATAL_ERROR "Cooperative threads require clang 23 or later")
endif()
set(__wasi_cooperative_threads__ ON)
else()
endif()

# =============================================================================
# Helper functions for adding libraries.

Expand Down Expand Up @@ -259,7 +290,7 @@ function(add_internal_shared_library name)
add_library(${name} SHARED EXCLUDE_FROM_ALL ${ARGN})
set_pic(${name})
target_link_libraries(${name} PUBLIC musl-top-half-interface)
add_dependencies(${name} sysroot-c builtins)
add_dependencies(${name} sysroot-c builtins sysroot-empty-pthread)
target_compile_options(${name} PRIVATE -fvisibility=default)
set_target_properties(${name} PROPERTIES NO_SONAME 1)
clang_format_target(${name})
Expand Down
10 changes: 6 additions & 4 deletions cmake/check-symbols.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ add_custom_command(
${CMAKE_CURRENT_LIST_DIR}/scripts/run-check-symbols.cmake
)

if(THREADS)
set(expected "${CMAKE_SOURCE_DIR}/expected/wasm32-wasip1-threads")
elseif(WASI STREQUAL "p1")
set(expected "${CMAKE_SOURCE_DIR}/expected/wasm32-wasip1")
if(WASI STREQUAL "p1")
if(TARGET_TRIPLE MATCHES "threads")
set(expected "${CMAKE_SOURCE_DIR}/expected/wasm32-wasip1-threads")
else()
set(expected "${CMAKE_SOURCE_DIR}/expected/wasm32-wasip1")
endif()
elseif(WASI STREQUAL "p2")
set(expected "${CMAKE_SOURCE_DIR}/expected/wasm32-wasip2")
elseif(WASI STREQUAL "p3")
Expand Down
5 changes: 3 additions & 2 deletions cmake/clang-format.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ set(formatted_sources)

function(clang_format_file file)
if (file MATCHES "__generated" OR
file MATCHES "wasip.\.c$") # Skip auto-generated files
file MATCHES "wasip.\.c$" OR # Skip auto-generated files
file MATCHES "\.(s|S)$") # Skip assembly files
return()
endif()
cmake_path(ABSOLUTE_PATH file BASE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE src)
Expand All @@ -30,7 +31,7 @@ function(clang_format_file file)
# excludes upstream projects like cloudlibc/musl, malloc implementations, etc.
if (NOT EXISTS ${src}
OR ${src} MATCHES "cloudlibc"
OR ${src} MATCHES "libc-top-half"
OR (${src} MATCHES "libc-top-half" AND NOT ${src} MATCHES "libc-top-half/musl/src/thread/coop-threads")
OR ${src} MATCHES "fts/musl-fts"
OR ${src} MATCHES "dlmalloc"
OR ${src} MATCHES "emmalloc"
Expand Down
2 changes: 1 addition & 1 deletion cmake/wasm-component-ld.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ if (NOT WASM_COMPONENT_LD_EXECUTABLE)
ba_download(
wasm-component-ld
"https://github.com/bytecodealliance/wasm-component-ld"
"v0.5.21"
"v0.5.25"
)
ExternalProject_Get_Property(wasm-component-ld SOURCE_DIR)
set(WASM_COMPONENT_LD_EXECUTABLE "${SOURCE_DIR}/wasm-component-ld")
Expand Down
13 changes: 7 additions & 6 deletions expected/wasm32-wasip1-threads/defined-symbols.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@ __SIG_ERR
__SIG_IGN
__acquire_ptc
__aio_close
__allocate_tls
__asctime_r
__assert_fail
__at_quick_exit_lockptr
__atexit_lockptr
__c_dot_utf8
__c_dot_utf8_locale
__c_locale
Expand Down Expand Up @@ -81,6 +80,7 @@ __ftello
__ftello_unlocked
__funcs_on_exit
__funcs_on_quick_exit
__futexwait
__futimesat
__fwritable
__fwritex
Expand All @@ -92,6 +92,7 @@ __getopt_msg
__gmtime_r
__hwcap
__inet_aton
__inhibit_ptc
__init_ssp
__init_tp
__intscan
Expand Down Expand Up @@ -148,7 +149,6 @@ __libc_free
__libc_malloc
__loc_is_allocated
__locale_lock
__locale_lockptr
__localtime_r
__lock
__lockfile
Expand Down Expand Up @@ -219,7 +219,6 @@ __pthread_tsd_size
__putenv
__qsort_r
__rand48_step
__random_lockptr
__reallocarray
__register_locked_file
__release_ptc
Expand Down Expand Up @@ -248,7 +247,6 @@ __stdin_used
__stdio_close
__stdio_exit
__stdio_exit_needed
__stdio_ofl_lockptr
__stdio_read
__stdio_seek
__stdio_write
Expand Down Expand Up @@ -308,6 +306,7 @@ __unlockfile
__uselocale
__utc
__wait
__wake
__wasi_args_get
__wasi_args_sizes_get
__wasi_clock_res_get
Expand Down Expand Up @@ -359,7 +358,6 @@ __wasi_thread_start_C
__wasilibc_access
__wasilibc_cwd
__wasilibc_cwd_lock
__wasilibc_cwd_unlock
__wasilibc_deinitialize_environ
__wasilibc_dttoif
__wasilibc_enable_futex_busywait_on_current_thread
Expand Down Expand Up @@ -946,6 +944,9 @@ modff
modfl
mprotect
mrand48
mtx_destroy
mtx_init
mtx_timedlock
munmap
nan
nanf
Expand Down
7 changes: 7 additions & 0 deletions libc-bottom-half/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,13 @@ if (WASI STREQUAL "p3")
)
endif()

if (HAVE_WASM_LIBCALL_THREAD_CONTEXT)
list(APPEND bottom_half_sources
sources/__libcall_thread_context.S
sources/__wasm_init_task.S
)
endif()

# Don't export symbols of generated code in shared libraries, so specifically
# pass `-fvisibility=hidden`.
set_source_files_properties(sources/wasip2.c PROPERTIES
Expand Down
10 changes: 9 additions & 1 deletion libc-bottom-half/cloudlibc/src/libc/sched/sched_yield.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,15 @@
#include <sched.h>

int sched_yield(void) {
#ifdef __wasip1__
#ifdef __wasi_cooperative_threads__
#ifdef __wasip3__
wasip3_thread_yield();
return 0;
#else
#error "Unknown WASI version"
#endif

#elif defined(__wasip1__)
__wasi_errno_t error = __wasi_sched_yield();
if (error != 0) {
errno = error;
Expand Down
2 changes: 1 addition & 1 deletion libc-bottom-half/cloudlibc/src/libc/unistd/pwrite.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ ssize_t pwrite(int fildes, const void *buf, size_t nbyte, off_t offset) {
&closed);
filesystem_stream_u8_drop_writable(writer);

// Wait for the subtask to resolve now that the writer half is closed and if
// Wait for the future to resolve now that the writer half is closed and if
// we failed to write bytes (0 bytes written) and the result is an error we
// can return -1.
filesystem_result_void_error_code_t result;
Expand Down
8 changes: 8 additions & 0 deletions libc-bottom-half/crt/crt1-command.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ extern void __wasm_call_ctors(void);
extern int __main_void(void);
extern void __wasm_call_dtors(void);

#ifdef __wasm_libcall_thread_context__
// Force __wasm_init_task and __wasm_init_async_task to be linked in for wasip3
extern void __wasm_init_task(void);
extern void __wasm_init_async_task(void);
__attribute__((used)) void *__wasm_init_task_ref = __wasm_init_task;
__attribute__((used)) void *__wasm_init_async_task_ref = __wasm_init_async_task;
#endif

#if defined(__wasip1__)
__attribute__((export_name("_start"))) void _start(void)
#elif defined(__wasip2__)
Expand Down
9 changes: 9 additions & 0 deletions libc-bottom-half/crt/crt1-reactor.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@
extern void __wasi_init_tp(void);
extern void __wasm_call_ctors(void);

#ifdef __wasm_libcall_thread_context__
// Force __wasm_init_task and __wasm_init_async_task to be linked in for wasip3
extern void __wasm_init_task(void);
extern void __wasm_init_async_task(void);
__attribute__((used)) static void *__wasm_init_task_ref = __wasm_init_task;
__attribute__((used)) static void *__wasm_init_async_task_ref =
__wasm_init_async_task;
#endif

__attribute__((export_name("_initialize"))) void _initialize(void) {
#if defined(_REENTRANT)
static volatile atomic_int initialized = 0;
Expand Down
7 changes: 7 additions & 0 deletions libc-bottom-half/headers/public/wasi/version.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@
#cmakedefine __wasip2__
#cmakedefine __wasip3__

// The `__wasi_cooperative_threads__` define is used to test whether cooperative
// threads are enabled for this build of wasi-libc. This is detected at
// build-time and only works on supported versions of clang.
#ifdef __wasm_libcall_thread_context__
#cmakedefine __wasi_cooperative_threads__
#endif

#cmakedefine __wasi_sdk_major__ @__wasi_sdk_major__@
#cmakedefine __wasi_sdk_version__ "@__wasi_sdk_version__@"

Expand Down
73 changes: 73 additions & 0 deletions libc-bottom-half/sources/__libcall_thread_context.S

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This I think is ok for an initial implementation, but I think we're going to want to eventually ensure that calls to __wasm_get_stack_pointer go directly to the builtin intrinsic without needing a wrapper in the middle. Otherwise there's basically no way to create a module that directly performs the call because even with LTO these won't get inlined.

Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# LLVM emits calls to the following functions when the libcall-thread-context feature is enabled (default on WASIp3)

#include <wasi/version.h>

.globl __wasm_get_stack_pointer
.type __wasm_get_stack_pointer,@function
.globl __wasm_set_stack_pointer
.type __wasm_set_stack_pointer,@function

.functype __wasm_component_model_builtin_context_get_0 () -> (i32)
.import_module __wasm_component_model_builtin_context_get_0, "$root"
.import_name __wasm_component_model_builtin_context_get_0, "[context-get-0]"
.functype __wasm_component_model_builtin_context_set_0 (i32) -> ()
.import_module __wasm_component_model_builtin_context_set_0, "$root"
.import_name __wasm_component_model_builtin_context_set_0, "[context-set-0]"

__wasm_get_stack_pointer:
.functype __wasm_get_stack_pointer () -> (i32)
call __wasm_component_model_builtin_context_get_0
end_function

__wasm_set_stack_pointer:
.functype __wasm_set_stack_pointer (i32) -> ()
local.get 0
call __wasm_component_model_builtin_context_set_0
end_function

.globl __wasm_get_tls_base
.type __wasm_get_tls_base,@function
.globl __wasm_set_tls_base
.type __wasm_set_tls_base,@function

#ifdef __wasi_cooperative_threads__
# When coop threads are enabled the tls base lives in this async tasks's
# context slot index as 1. Note that this slot isn't valid when the
# component-model-threading feature is disabled, however, so it can't be
# used.
.functype __wasm_component_model_builtin_context_get_1 () -> (i32)
.import_module __wasm_component_model_builtin_context_get_1, "$root"
.import_name __wasm_component_model_builtin_context_get_1, "[context-get-1]"
.functype __wasm_component_model_builtin_context_set_1 (i32) -> ()
.import_module __wasm_component_model_builtin_context_set_1, "$root"
.import_name __wasm_component_model_builtin_context_set_1, "[context-set-1]"

__wasm_get_tls_base:
.functype __wasm_get_tls_base () -> (i32)
call __wasm_component_model_builtin_context_get_1
end_function

__wasm_set_tls_base:
.functype __wasm_set_tls_base (i32) -> ()
local.get 0
call __wasm_component_model_builtin_context_set_1
end_function
#else
# When coop threads are disabled do something similar to the basic output
# which is to have a global that stores the TLS base.
.globaltype __wasilibc_tls_base, i32

__wasm_get_tls_base:
.functype __wasm_get_tls_base () -> (i32)
global.get __wasilibc_tls_base
end_function

__wasm_set_tls_base:
.functype __wasm_set_tls_base (i32) -> ()
local.get 0
global.set __wasilibc_tls_base
end_function

.section .wasm.__wasilibc_tls_base,"",@
__wasilibc_tls_base:
#endif
Loading