added: WASI support for threads at the platform layer
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 12 May 2023 16:39:56 +0000 (11:39 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 12 May 2023 16:39:56 +0000 (11:39 -0500)
CHANGELOG
compiler/src/wasm_emit.c
core/builtin.onyx
core/io/stdio.onyx
core/runtime/platform/wasi/platform.onyx
core/runtime/platform/wasi/wasi_defs.onyx
interpreter/src/wasm/module_parsing.h

index a7ecc1eace07974e207ffa4c0fd43a4b9a92a094..c4a2405677ac3f54df56b021b7cfd53cab9da487 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -4,6 +4,8 @@ Release v0.1.1
 Additions:
 * Ability to have tags on `#foreign` block procedures.
     - This will enable the creation different binding generators, such jsbindgen.
+* WASI support for threads at the platform layer.
+    - Requires passing '--multi-threaded' CLI flag.
 * `#distinct` types can now be made over any type.
     - Used to be only primitives.
 * New `logo-new-256.ico` for favicon on website.
index 14102044a87f895617b65738ba829e0fb3821795..25e4656b84f018a3ea5921a7d205700933d8fb24 100644 (file)
@@ -4957,7 +4957,7 @@ void onyx_wasm_module_link(OnyxWasmModule *module, OnyxWasmLinkOptions *options)
             .kind   = WASM_FOREIGN_MEMORY,
             .min    = options->memory_min_size,
             .max    = options->memory_max_size, // NOTE: Why not use all 4 Gigs of memory?
-            .shared = context.options->runtime == Runtime_Js,
+            .shared = context.options->use_multi_threading && context.options->runtime != Runtime_Onyx,
 
             .mod    = options->import_memory_module_name,
             .name   = options->import_memory_import_name,
index 05dec3c5532da1a0cd82b050ccfdd48aea1cb606..6e9ae87d71cc6741d20411bc2d9e8f7dcbdade18 100644 (file)
@@ -540,8 +540,15 @@ Link_Options :: struct {
         IMPORT_MEMORY_DEFAULT :: true;
         IMPORT_MEMORY_MODULE_NAME_DEFAULT :: "onyx";
         IMPORT_MEMORY_IMPORT_NAME_DEFAULT :: "memory";
+    }
+
+    #if runtime.runtime == .Wasi && runtime.Multi_Threading_Enabled {
+        IMPORT_MEMORY_DEFAULT :: true;
+        IMPORT_MEMORY_MODULE_NAME_DEFAULT :: "env";
+        IMPORT_MEMORY_IMPORT_NAME_DEFAULT :: "memory";
+    }
 
-    } else {
+    #if !#defined(IMPORT_MEMORY_DEFAULT) {
         IMPORT_MEMORY_DEFAULT :: false;
         IMPORT_MEMORY_MODULE_NAME_DEFAULT :: "";
         IMPORT_MEMORY_IMPORT_NAME_DEFAULT :: "";
index 7e61518bdf6c66c0deec355515c9b0492763016d..14317c9b036e0d91fcd9647246defa8db99b5c41 100644 (file)
@@ -66,6 +66,8 @@ __flush_stdio :: () {
 // that are sent directly to io.write.
 print :: #match #locked {
     (x: str) {
+        if !x do return;
+
         io.write(&stdio.print_writer, x);
         if x[x.count - 1] == #char "\n" && stdio.auto_flush do __flush_stdio();
     },
index 0394fb3a75763f345b117b82e1718c626f1e6b3a..9082bcf4a0fafde018baace95cd9d78f148688f9 100644 (file)
@@ -33,7 +33,7 @@ Supports_Processes :: false
 Supports_Time :: true
 Supports_Networking :: false
 Supports_Type_Info :: true
-Supports_Threads :: false
+Supports_Threads :: true
 Supports_Env_Vars :: true
 
 
@@ -129,3 +129,35 @@ __start :: () {
     __flush_stdio();
 }
 
+
+#if runtime.Multi_Threading_Enabled {
+    #local ThreadData :: struct {
+        id: i32;
+        tls_base, stack_base: rawptr;
+        func: (data: rawptr) -> void;
+        data: rawptr;
+    }
+
+    __spawn_thread :: (
+        id: i32,
+        tls_base, stack_base: rawptr,
+        func: (data: rawptr) -> void, data: rawptr) -> void
+    {
+        thread_data := core.alloc.on_heap(ThreadData.{
+            id, tls_base, stack_base, func, data
+        });
+
+        wasi.thread_spawn(thread_data);
+    }
+
+    // Currently, WASI does not provide a way to kill a thread preemptively.
+    __kill_thread :: (id: i32) => 0
+
+    #export "wasi_thread_start"
+    (wasi_tid: u32, use td: &ThreadData) {
+        runtime._thread_start(id, tls_base, stack_base, func, data);
+        runtime._thread_exit(id);
+    }
+}
+
+
index a825dbdb8daaeddac77720cbdd3e1ff86623ab35..abd1517a98a79c61620d83ed681d26f45669fdd4 100644 (file)
@@ -1,5 +1,9 @@
 package wasi
 
+#if !#defined(WASI_VERSION) {
+    WASI_VERSION :: "wasi_snapshot_preview1"
+}
+
 Size      :: #type u32;
 Filesize  :: #type u64;
 Timestamp :: #type u64;
@@ -365,42 +369,42 @@ PrestatTagged :: struct {
 
 
 // FUNCTIONS
-args_get       :: (argv: & &u8, argv_buf: &u8) -> Errno #foreign "wasi_snapshot_preview1" "args_get"---
-args_sizes_get :: (argc: &Size, argv_buf_size: &Size) -> Errno #foreign "wasi_snapshot_preview1" "args_sizes_get" ---
-
-environ_get       :: (environ: & &u8, environ_buf: &u8) -> Errno #foreign "wasi_snapshot_preview1" "environ_get" ---
-environ_sizes_get :: (environc: &Size, environ_buf_size: &Size) -> Errno #foreign "wasi_snapshot_preview1" "environ_sizes_get" ---
-
-clock_res_get  :: (id: ClockID, resolution: &Timestamp) -> Errno #foreign "wasi_snapshot_preview1" "clock_res_get" ---
-clock_time_get :: (id: ClockID, precision: Timestamp, time: &Timestamp) -> Errno #foreign "wasi_snapshot_preview1" "clock_time_get" ---
-
-fd_advise             :: (fd: FileDescriptor, offset: Filesize, len: Filesize, advice: Advice) -> Errno #foreign "wasi_snapshot_preview1" "fd_advise" ---
-fd_allocate           :: (fd: FileDescriptor, offset: Filesize, len: Filesize) -> Errno #foreign "wasi_snapshot_preview1" "fd_allocate" ---
-fd_close              :: (fd: FileDescriptor) -> Errno #foreign "wasi_snapshot_preview1" "fd_close" ---
-fd_datasync           :: (fd: FileDescriptor) -> Errno #foreign "wasi_snapshot_preview1" "fd_datasync" ---
-fd_fdstat_get         :: (fd: FileDescriptor, stat: &FDStat) -> Errno #foreign "wasi_snapshot_preview1" "fd_fdstat_get" ---
-fd_fdstat_set_flags   :: (fd: FileDescriptor, flags: FDFlags) -> Errno #foreign "wasi_snapshot_preview1" "fd_fdstat_set_flags" ---
-fd_fdstat_set_rights  :: (fd: FileDescriptor, rights_base: Rights, rights_inheriting: Rights) -> Errno #foreign "wasi_snapshot_preview1" "fd_fdstat_set_rights" ---
-fd_filestat_get       :: (fd: FileDescriptor, buf: &FileStat) -> Errno #foreign "wasi_snapshot_preview1" "fd_filestat_get" ---
-fd_filestat_set_size  :: (fd: FileDescriptor, size: Filesize) -> Errno #foreign "wasi_snapshot_preview1" "fd_filestat_set_size" ---
-fd_filestat_set_times :: (fd: FileDescriptor, atim: Timestamp, mtim: Timestamp, fst_flags: FSTFlags) -> Errno #foreign "wasi_snapshot_preview1" "fd_filestat_set_times" ---
-fd_pread              :: (fd: FileDescriptor, iovs: &IOVec, iovs_len: Size, offset: Filesize, nread: &Size) -> Errno #foreign "wasi_snapshot_preview1" "fd_pread" ---
-fd_prestat_get        :: (fd: FileDescriptor, buf: &PrestatTagged) -> Errno #foreign "wasi_snapshot_preview1" "fd_prestat_get" ---
-fd_prestat_dir_name   :: (fd: FileDescriptor, path: str) -> Errno #foreign "wasi_snapshot_preview1" "fd_prestat_dir_name" ---
-fd_pwrite             :: (fd: FileDescriptor, iovs: &IOVec, iovs_len: Size, offset: Filesize, nwritten: &Size) -> Errno #foreign "wasi_snapshot_preview1" "fd_pwrite" ---
-fd_read               :: (fd: FileDescriptor, iovs: &IOVec, iovs_len: Size, nread: &Size) -> Errno #foreign "wasi_snapshot_preview1" "fd_read" ---
-fd_readdir            :: (fd: FileDescriptor, buf: &u8, buf_len: Size, cookie: DirCookie, bufused: &Size) -> Errno #foreign "wasi_snapshot_preview1" "fd_readdir" ---
-fd_renumber           :: (fd: FileDescriptor, to: FileDescriptor) -> Errno #foreign "wasi_snapshot_preview1" "fd_renumber" ---
-fd_seek               :: (fd: FileDescriptor, offset: FileDelta, whence: Whence, newoffset: &Filesize) -> Errno #foreign "wasi_snapshot_preview1" "fd_seek" ---
-fd_sync               :: (fd: FileDescriptor) -> Errno #foreign "wasi_snapshot_preview1" "fd_sync" ---
-fd_tell               :: (fd: FileDescriptor, offset: &Filesize) -> Errno #foreign "wasi_snapshot_preview1" "fd_tell" ---
-fd_write              :: (fd: FileDescriptor, iovs: &IOVec, iovs_len: Size, nwritten: &Size) -> Errno #foreign "wasi_snapshot_preview1" "fd_write" ---
-
-path_create_directory   :: (fd: FileDescriptor, path: str) -> Errno #foreign "wasi_snapshot_preview1" "path_create_directory" ---
-path_filestat_get       :: (fd: FileDescriptor, flags: LookupFlags, path: str, buf: &FileStat) -> Errno #foreign "wasi_snapshot_preview1" "path_filestat_get" ---
-path_filestat_set_times :: (fd: FileDescriptor, flags: LookupFlags, path: str, atim: Timestamp, mtim: Timestamp, fst_flags: FSTFlags) -> Errno #foreign "wasi_snapshot_preview1" "path_filestat_set_times" ---
-
-path_link :: (fd: FileDescriptor, old_flags: LookupFlags, old_path: str, new_fd: FileDescriptor, new_path: str) -> Errno #foreign "wasi_snapshot_preview1" "path_link" ---
+args_get       :: (argv: & &u8, argv_buf: &u8) -> Errno #foreign WASI_VERSION "args_get"---
+args_sizes_get :: (argc: &Size, argv_buf_size: &Size) -> Errno #foreign WASI_VERSION "args_sizes_get" ---
+
+environ_get       :: (environ: & &u8, environ_buf: &u8) -> Errno #foreign WASI_VERSION "environ_get" ---
+environ_sizes_get :: (environc: &Size, environ_buf_size: &Size) -> Errno #foreign WASI_VERSION "environ_sizes_get" ---
+
+clock_res_get  :: (id: ClockID, resolution: &Timestamp) -> Errno #foreign WASI_VERSION "clock_res_get" ---
+clock_time_get :: (id: ClockID, precision: Timestamp, time: &Timestamp) -> Errno #foreign WASI_VERSION "clock_time_get" ---
+
+fd_advise             :: (fd: FileDescriptor, offset: Filesize, len: Filesize, advice: Advice) -> Errno #foreign WASI_VERSION "fd_advise" ---
+fd_allocate           :: (fd: FileDescriptor, offset: Filesize, len: Filesize) -> Errno #foreign WASI_VERSION "fd_allocate" ---
+fd_close              :: (fd: FileDescriptor) -> Errno #foreign WASI_VERSION "fd_close" ---
+fd_datasync           :: (fd: FileDescriptor) -> Errno #foreign WASI_VERSION "fd_datasync" ---
+fd_fdstat_get         :: (fd: FileDescriptor, stat: &FDStat) -> Errno #foreign WASI_VERSION "fd_fdstat_get" ---
+fd_fdstat_set_flags   :: (fd: FileDescriptor, flags: FDFlags) -> Errno #foreign WASI_VERSION "fd_fdstat_set_flags" ---
+fd_fdstat_set_rights  :: (fd: FileDescriptor, rights_base: Rights, rights_inheriting: Rights) -> Errno #foreign WASI_VERSION "fd_fdstat_set_rights" ---
+fd_filestat_get       :: (fd: FileDescriptor, buf: &FileStat) -> Errno #foreign WASI_VERSION "fd_filestat_get" ---
+fd_filestat_set_size  :: (fd: FileDescriptor, size: Filesize) -> Errno #foreign WASI_VERSION "fd_filestat_set_size" ---
+fd_filestat_set_times :: (fd: FileDescriptor, atim: Timestamp, mtim: Timestamp, fst_flags: FSTFlags) -> Errno #foreign WASI_VERSION "fd_filestat_set_times" ---
+fd_pread              :: (fd: FileDescriptor, iovs: &IOVec, iovs_len: Size, offset: Filesize, nread: &Size) -> Errno #foreign WASI_VERSION "fd_pread" ---
+fd_prestat_get        :: (fd: FileDescriptor, buf: &PrestatTagged) -> Errno #foreign WASI_VERSION "fd_prestat_get" ---
+fd_prestat_dir_name   :: (fd: FileDescriptor, path: str) -> Errno #foreign WASI_VERSION "fd_prestat_dir_name" ---
+fd_pwrite             :: (fd: FileDescriptor, iovs: &IOVec, iovs_len: Size, offset: Filesize, nwritten: &Size) -> Errno #foreign WASI_VERSION "fd_pwrite" ---
+fd_read               :: (fd: FileDescriptor, iovs: &IOVec, iovs_len: Size, nread: &Size) -> Errno #foreign WASI_VERSION "fd_read" ---
+fd_readdir            :: (fd: FileDescriptor, buf: &u8, buf_len: Size, cookie: DirCookie, bufused: &Size) -> Errno #foreign WASI_VERSION "fd_readdir" ---
+fd_renumber           :: (fd: FileDescriptor, to: FileDescriptor) -> Errno #foreign WASI_VERSION "fd_renumber" ---
+fd_seek               :: (fd: FileDescriptor, offset: FileDelta, whence: Whence, newoffset: &Filesize) -> Errno #foreign WASI_VERSION "fd_seek" ---
+fd_sync               :: (fd: FileDescriptor) -> Errno #foreign WASI_VERSION "fd_sync" ---
+fd_tell               :: (fd: FileDescriptor, offset: &Filesize) -> Errno #foreign WASI_VERSION "fd_tell" ---
+fd_write              :: (fd: FileDescriptor, iovs: &IOVec, iovs_len: Size, nwritten: &Size) -> Errno #foreign WASI_VERSION "fd_write" ---
+
+path_create_directory   :: (fd: FileDescriptor, path: str) -> Errno #foreign WASI_VERSION "path_create_directory" ---
+path_filestat_get       :: (fd: FileDescriptor, flags: LookupFlags, path: str, buf: &FileStat) -> Errno #foreign WASI_VERSION "path_filestat_get" ---
+path_filestat_set_times :: (fd: FileDescriptor, flags: LookupFlags, path: str, atim: Timestamp, mtim: Timestamp, fst_flags: FSTFlags) -> Errno #foreign WASI_VERSION "path_filestat_set_times" ---
+
+path_link :: (fd: FileDescriptor, old_flags: LookupFlags, old_path: str, new_fd: FileDescriptor, new_path: str) -> Errno #foreign WASI_VERSION "path_link" ---
 path_open :: (fd: FileDescriptor
     , dirflags: LookupFlags
     , path: str
@@ -410,26 +414,28 @@ path_open :: (fd: FileDescriptor
     , fdflags: FDFlags
     , opened_fd: &FileDescriptor
     ) -> Errno
-     #foreign "wasi_snapshot_preview1" "path_open" ---
-
-path_readlink :: (fd: FileDescriptor, path: str, buf: &u8, buf_len: Size, bufused: &Size) -> Errno #foreign "wasi_snapshot_preview1" "path_readlink" ---
-path_remove_directory :: (fd: FileDescriptor, path: str) -> Errno #foreign "wasi_snapshot_preview1" "path_remove_directory" ---
-path_rename  :: (fd: FileDescriptor, old_path: str, new_fd: FileDescriptor, new_path: str) -> Errno #foreign "wasi_snapshot_preview1" "path_rename" ---
-path_symlink :: (old_path: &u8, old_path_len: Size, fd: FileDescriptor, new_path: str) -> Errno #foreign "wasi_snapshot_preview1" "path_symlink" ---
-path_unlink_file :: (fd: FileDescriptor, path: str) -> Errno #foreign "wasi_snapshot_preview1" "path_unlink_file" ---
+     #foreign WASI_VERSION "path_open" ---
 
-poll_oneoff :: (in: &Subscription, out: &Event, nsubscriptions: Size, nevents: &Size) -> Errno #foreign "wasi_snapshot_preview1" "poll_oneoff" ---
+path_readlink :: (fd: FileDescriptor, path: str, buf: &u8, buf_len: Size, bufused: &Size) -> Errno #foreign WASI_VERSION "path_readlink" ---
+path_remove_directory :: (fd: FileDescriptor, path: str) -> Errno #foreign WASI_VERSION "path_remove_directory" ---
+path_rename  :: (fd: FileDescriptor, old_path: str, new_fd: FileDescriptor, new_path: str) -> Errno #foreign WASI_VERSION "path_rename" ---
+path_symlink :: (old_path: &u8, old_path_len: Size, fd: FileDescriptor, new_path: str) -> Errno #foreign WASI_VERSION "path_symlink" ---
+path_unlink_file :: (fd: FileDescriptor, path: str) -> Errno #foreign WASI_VERSION "path_unlink_file" ---
 
-proc_exit  :: (rval: ExitCode) -> void #foreign "wasi_snapshot_preview1" "proc_exit" ---
-proc_raise :: (sig: Signal) -> Errno #foreign "wasi_snapshot_preview1" "proc_raise" ---
+poll_oneoff :: (in: &Subscription, out: &Event, nsubscriptions: Size, nevents: &Size) -> Errno #foreign WASI_VERSION "poll_oneoff" ---
 
-sched_yield :: () -> Errno #foreign "wasi_snapshot_preview1" "sched_yield" ---
+proc_exit  :: (rval: ExitCode) -> void #foreign WASI_VERSION "proc_exit" ---
+proc_raise :: (sig: Signal) -> Errno #foreign WASI_VERSION "proc_raise" ---
 
-random_get :: (buf: &u8, buf_len: Size) -> Errno #foreign "wasi_snapshot_preview1" "random_get" ---
+sched_yield :: () -> Errno #foreign WASI_VERSION "sched_yield" ---
 
-sock_recv     :: (fd: FileDescriptor, ri_data: &IOVec, ri_data_len: Size, ri_flags: RIFlags, ro_datalen: &Size, ro_flags: &ROFlags) -> Errno #foreign "wasi_snapshot_preview1" "sock_recv" ---
-sock_send     :: (fd: FileDescriptor, si_data: &IOVec, ri_data_len: Size, si_flags: SIFlags, so_datalen: &Size) -> Errno #foreign "wasi_snapshot_preview1" "sock_send" ---
-sock_shutdown :: (fd: FileDescriptor, how: SDFlags) -> Errno #foreign "wasi_snapshot_preview1" "sock_shutdown" ---
+random_get :: (buf: &u8, buf_len: Size) -> Errno #foreign WASI_VERSION "random_get" ---
 
+sock_recv     :: (fd: FileDescriptor, ri_data: &IOVec, ri_data_len: Size, ri_flags: RIFlags, ro_datalen: &Size, ro_flags: &ROFlags) -> Errno #foreign WASI_VERSION "sock_recv" ---
+sock_send     :: (fd: FileDescriptor, si_data: &IOVec, ri_data_len: Size, si_flags: SIFlags, so_datalen: &Size) -> Errno #foreign WASI_VERSION "sock_send" ---
+sock_shutdown :: (fd: FileDescriptor, how: SDFlags) -> Errno #foreign WASI_VERSION "sock_shutdown" ---
 
+// Whoever decided that THIS ONE IMPORT should use '-' instead of '_' should be fired.
+// Spent way too long trying to figure out why I could not import this function...
+thread_spawn :: (td: rawptr) -> u32 #foreign WASI_VERSION "thread-spawn" ---
 
index 737ff91b8bb6c948a7401f48ba36144a1cddab75..84965a57eca4b7dcc52bf74d62dca4aa7f56c78d 100644 (file)
@@ -133,7 +133,7 @@ static void parse_type_section(build_context *ctx) {
 }
 
 static wasm_limits_t parse_limits(build_context *ctx) {
-    bool maximum_present = CONSUME_BYTE(ctx) == 0x01;
+    bool maximum_present = CONSUME_BYTE(ctx) & 0x01;
 
     wasm_limits_t limits;
     limits.min = uleb128_to_uint(ctx->binary.data, &ctx->offset);