diff --git a/.cargo/config.toml b/.cargo/config.toml index 3dce2ac..2805b26 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,11 +1,18 @@ -[build] -target = "wasm32-unknown-unknown" - [target.wasm32-unknown-unknown] -rustflags = ["-C", "target-feature=+atomics"] +rustflags = [ + "-C", "target-feature=+atomics", + "-Clink-args=--shared-memory", + "-Clink-args=--import-memory", + "-Clink-args=--max-memory=1073741824", + "-Clink-args=--export=__wasm_init_tls", + "-Clink-args=--export=__tls_size", + "-Clink-args=--export=__tls_align", + "-Clink-args=--export=__tls_base", +] # Ciantic: Tested +simd128 22.7.2021, didn't work! Got some wasm-opt problems. # 2024-10-01 - It now works, but threading works without it. So probably best to wait for it to stabilize. # 2025-06-12 - mutable-globals is enabled by default, and bulk-memory is enabled by default on Rust 1.87+ +# 2025-10-02 - rust now requires extra -Clink-args to enable shared-memory, see https://github.com/rust-lang/rust/pull/147225 [unstable] build-std = ["panic_abort", "std"] diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 557c7c1..8f30b39 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,7 +20,7 @@ jobs: rust: nightly rust-wasm: true rust-src: true - tool-cargo-binstall: txtpp + tool-cargo-install: txtpp - run: task install-ci working-directory: example - run: task build diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 122fb8e..b1289fb 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -20,5 +20,5 @@ jobs: rust: nightly rust-wasm: true rust-src: true - tool-cargo-binstall: txtpp + tool-cargo-install: txtpp - run: task check diff --git a/Cargo.toml b/Cargo.toml index 18a40db..48bbe9b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasm-bindgen-spawn" -version = "0.0.2" +version = "0.0.3" edition = "2024" description = "Web Worker Multithreading library for wasm-bindgen the uses shared memory" repository = "https://github.com/Pistonite/wasm-bindgen-spawn" @@ -20,12 +20,12 @@ include = [ ] [dependencies] -js-sys = "0.3.77" +js-sys = "0.3.83" oneshot = { version = "0.1.11", default-features = false, features = ["std"] } static_assertions = "1.1.0" -thiserror = "2.0.12" -wasm-bindgen = "0.2.100" -wasm-bindgen-futures = { version = "0.4.50", optional = true } +thiserror = "2.0.17" +wasm-bindgen = "0.2.106" +wasm-bindgen-futures = { version = "0.4.56", optional = true } [features] default = ["async"] diff --git a/README.md b/README.md index 9795934..6584553 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,16 @@ Read the full article for more details on the implications of Cross-Origin Isola 2. Add the following to `.cargo/config.toml` ```toml [target.wasm32-unknown-unknown] - rustflags = ["-C", "target-feature=+atomics"] + rustflags = [ + "-C", "target-feature=+atomics", + "-Clink-args=--shared-memory", + "-Clink-args=--import-memory", + "-Clink-args=--max-memory=1073741824", + "-Clink-args=--export=__wasm_init_tls", + "-Clink-args=--export=__tls_size", + "-Clink-args=--export=__tls_align", + "-Clink-args=--export=__tls_base", + ] # You also need `bulk-memory` for Rust < 1.87. For 1.87+ it's enabled by default # rustflags = ["-C", "target-feature=+atomics,+bulk-memory"] diff --git a/Taskfile.yml b/Taskfile.yml index d6f0b46..fd14537 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -9,6 +9,7 @@ includes: cargo: taskfile: ./mono-dev/task/cargo.yaml internal: true + optional: true tasks: install-cargo-extra-tools: diff --git a/example/.cargo/config.toml b/example/.cargo/config.toml index 27b65d5..2805b26 100644 --- a/example/.cargo/config.toml +++ b/example/.cargo/config.toml @@ -1,7 +1,18 @@ [target.wasm32-unknown-unknown] -rustflags = ["-C", "target-feature=+atomics,+bulk-memory,+mutable-globals"] +rustflags = [ + "-C", "target-feature=+atomics", + "-Clink-args=--shared-memory", + "-Clink-args=--import-memory", + "-Clink-args=--max-memory=1073741824", + "-Clink-args=--export=__wasm_init_tls", + "-Clink-args=--export=__tls_size", + "-Clink-args=--export=__tls_align", + "-Clink-args=--export=__tls_base", +] # Ciantic: Tested +simd128 22.7.2021, didn't work! Got some wasm-opt problems. -# 10.01.2024 - It now works, but threading works without it. So probably best to wait for it to stabilize. +# 2024-10-01 - It now works, but threading works without it. So probably best to wait for it to stabilize. +# 2025-06-12 - mutable-globals is enabled by default, and bulk-memory is enabled by default on Rust 1.87+ +# 2025-10-02 - rust now requires extra -Clink-args to enable shared-memory, see https://github.com/rust-lang/rust/pull/147225 [unstable] build-std = ["panic_abort", "std"] diff --git a/example/Cargo.lock b/example/Cargo.lock index bdf2485..0c37f06 100644 --- a/example/Cargo.lock +++ b/example/Cargo.lock @@ -4,15 +4,15 @@ version = 4 [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "console_error_panic_hook" @@ -37,20 +37,14 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", ] -[[package]] -name = "log" -version = "0.4.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" - [[package]] name = "once_cell" version = "1.21.3" @@ -65,27 +59,27 @@ checksum = "b4ce411919553d3f9fa53a0880544cda985a112117a0444d5ff1e870a893d6ea" [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.40" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" dependencies = [ "proc-macro2", ] [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "static_assertions" @@ -95,9 +89,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "syn" -version = "2.0.102" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6397daf94fa90f058bd0fd88429dd9e5738999cca8d701813c80723add80462" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ "proc-macro2", "quote", @@ -106,18 +100,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", @@ -126,41 +120,28 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.50" +version = "0.4.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" dependencies = [ "cfg-if", "js-sys", @@ -171,9 +152,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -181,29 +162,29 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] [[package]] name = "wasm-bindgen-spawn" -version = "0.0.2" +version = "0.0.3" dependencies = [ "js-sys", "oneshot", @@ -215,9 +196,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/example/Cargo.toml b/example/Cargo.toml index 8fd49cc..3dacd65 100644 --- a/example/Cargo.toml +++ b/example/Cargo.toml @@ -5,9 +5,9 @@ edition = "2024" [dependencies] console_error_panic_hook = "0.1.7" -js-sys = "0.3.77" -wasm-bindgen = "0.2.100" -wasm-bindgen-futures = "0.4.50" +js-sys = "0.3.83" +wasm-bindgen = "0.2.106" +wasm-bindgen-futures = "0.4.56" wasm-bindgen-spawn = { path = ".." } [lib] diff --git a/example/src/lib.rs b/example/src/lib.rs index 834db17..0cb0d49 100644 --- a/example/src/lib.rs +++ b/example/src/lib.rs @@ -1,6 +1,6 @@ use std::{ cell::OnceCell, - sync::{atomic::AtomicUsize, Arc, Mutex}, + sync::{Arc, Mutex, atomic::AtomicUsize}, }; use js_sys::Function; diff --git a/src/js/createDispatcher.js b/src/js/createDispatcher.js index dc3ef3a..1796d38 100644 --- a/src/js/createDispatcher.js +++ b/src/js/createDispatcher.js @@ -3,7 +3,7 @@ return (async function () { const DISPATCHER = wbg + ` -self.onmessage=async(_)=>{let{recv:a,start_send:i,url:o,memory:t,wasm:n}=_.data;await wasm_bindgen({memory:t,module_or_path:n}),wasm_bindgen.__dispatch_start(i);while(!0){let r=wasm_bindgen.__dispatch_recv(a);if(!r)break;let[w,d,m,c,p]=r;await new Promise((e)=>{let s=new Worker(o);s.onmessage=({data:g})=>{if(g)return s.postMessage({id:w,f:d,send:m,start:c,memory:t,wasm:n}),e();s.terminate()}});while(!wasm_bindgen.__dispatch_poll_worker(p))await new Promise((e)=>setTimeout(e,0))}wasm_bindgen.__dispatch_drop(a),self.postMessage(0)};self.postMessage(1); +self.onmessage=async(o)=>{let{recv:a,start_send:w,url:d,memory:t,wasm:n}=o.data;await wasm_bindgen({memory:t,module_or_path:n}),wasm_bindgen.__dispatch_start(w);while(!0){let r=wasm_bindgen.__dispatch_recv(a);if(!r)break;let[_,m,i,c,g]=r;await new Promise((s)=>{let e=new Worker(d);e.onmessage=({data:p})=>{switch(p){case 0:e.terminate();return;case 1:return e.postMessage({id:_,f:m,send:i,start:c,memory:t,wasm:n}),s();case 2:wasm_bindgen.__worker_send(_,i),e.terminate();return}}});while(!wasm_bindgen.__dispatch_poll_worker(g))await new Promise((s)=>setTimeout(s,0))}wasm_bindgen.__dispatch_drop(a),self.postMessage(0)};self.postMessage(1); `; const dispatcherUrl = URL.createObjectURL( @@ -12,7 +12,7 @@ self.onmessage=async(_)=>{let{recv:a,start_send:i,url:o,memory:t,wasm:n}=_.data; const WORKER = wbg + ` -self.onmessage=async(n)=>{let{id:s,f:_,send:a,start:o,memory:r,wasm:t}=n.data;await wasm_bindgen({memory:r,module_or_path:t});try{let e=wasm_bindgen.__worker_main(_,o);wasm_bindgen.__worker_send(s,a,e)}catch(e){self.console.error(e),wasm_bindgen.__worker_send(s,a)}self.postMessage(0)};self.postMessage(1); +self.onmessage=async(s)=>{let{id:a,f:n,send:o,start:r,memory:t,wasm:_}=s.data;await wasm_bindgen({memory:t,module_or_path:_});try{let e=wasm_bindgen.__worker_main(n,r);wasm_bindgen.__worker_send(a,o,e)}catch(e){self.console.error(e),self.postMessage(2);return}self.postMessage(0)};self.postMessage(1); `; const workerUrl = URL.createObjectURL( diff --git a/src/js/createDispatcher.min.js b/src/js/createDispatcher.min.js index feb6a06..366fb50 100644 --- a/src/js/createDispatcher.min.js +++ b/src/js/createDispatcher.min.js @@ -1,8 +1,8 @@ return async function(){let t=await(await fetch(args[1])).text(),n=t+` -self.onmessage=async(_)=>{let{recv:a,start_send:i,url:o,memory:t,wasm:n}=_.data;await wasm_bindgen({memory:t,module_or_path:n}),wasm_bindgen.__dispatch_start(i);while(!0){let r=wasm_bindgen.__dispatch_recv(a);if(!r)break;let[w,d,m,c,p]=r;await new Promise((e)=>{let s=new Worker(o);s.onmessage=({data:g})=>{if(g)return s.postMessage({id:w,f:d,send:m,start:c,memory:t,wasm:n}),e();s.terminate()}});while(!wasm_bindgen.__dispatch_poll_worker(p))await new Promise((e)=>setTimeout(e,0))}wasm_bindgen.__dispatch_drop(a),self.postMessage(0)};self.postMessage(1); +self.onmessage=async(o)=>{let{recv:a,start_send:w,url:d,memory:t,wasm:n}=o.data;await wasm_bindgen({memory:t,module_or_path:n}),wasm_bindgen.__dispatch_start(w);while(!0){let r=wasm_bindgen.__dispatch_recv(a);if(!r)break;let[_,m,i,c,g]=r;await new Promise((s)=>{let e=new Worker(d);e.onmessage=({data:p})=>{switch(p){case 0:e.terminate();return;case 1:return e.postMessage({id:_,f:m,send:i,start:c,memory:t,wasm:n}),s();case 2:wasm_bindgen.__worker_send(_,i),e.terminate();return}}});while(!wasm_bindgen.__dispatch_poll_worker(g))await new Promise((s)=>setTimeout(s,0))}wasm_bindgen.__dispatch_drop(a),self.postMessage(0)};self.postMessage(1); `,a=URL.createObjectURL(new Blob([n],{type:"text/javascript"})),o=t+` -self.onmessage=async(n)=>{let{id:s,f:_,send:a,start:o,memory:r,wasm:t}=n.data;await wasm_bindgen({memory:r,module_or_path:t});try{let e=wasm_bindgen.__worker_main(_,o);wasm_bindgen.__worker_send(s,a,e)}catch(e){self.console.error(e),wasm_bindgen.__worker_send(s,a)}self.postMessage(0)};self.postMessage(1); +self.onmessage=async(s)=>{let{id:a,f:n,send:o,start:r,memory:t,wasm:_}=s.data;await wasm_bindgen({memory:t,module_or_path:_});try{let e=wasm_bindgen.__worker_main(n,r);wasm_bindgen.__worker_send(a,o,e)}catch(e){self.console.error(e),self.postMessage(2);return}self.postMessage(0)};self.postMessage(1); -`,r=URL.createObjectURL(new Blob([o],{type:"text/javascript"})),i=await(await fetch(args[0])).arrayBuffer(),_=args[2],w=args[3],c=args[4],m=args[5],d=args[6],e=new Worker(a);await new Promise((s)=>{e.onmessage=({data:g})=>{if(g){s(),e.postMessage({recv:w,start_send:c,url:r,memory:_,wasm:i});return}URL.revokeObjectURL(a),URL.revokeObjectURL(r),e.terminate()}});while(!d(m))await new Promise((s)=>setTimeout(s,0))}(); +`,r=URL.createObjectURL(new Blob([o],{type:"text/javascript"})),i=await(await fetch(args[0])).arrayBuffer(),_=args[2],c=args[3],w=args[4],m=args[5],d=args[6],e=new Worker(a);await new Promise((s)=>{e.onmessage=({data:g})=>{if(g){s(),e.postMessage({recv:c,start_send:w,url:r,memory:_,wasm:i});return}URL.revokeObjectURL(a),URL.revokeObjectURL(r),e.terminate()}});while(!d(m))await new Promise((s)=>setTimeout(s,0))}(); diff --git a/src/js/dispatcher.js b/src/js/dispatcher.js index 7304fe5..1b74b66 100644 --- a/src/js/dispatcher.js +++ b/src/js/dispatcher.js @@ -11,11 +11,18 @@ self.onmessage = async (e) => { await new Promise((resolve) => { const worker = new Worker(url); worker.onmessage = ({ data }) => { - if (data) { - worker.postMessage({ id, f, send, start, memory, wasm }); - return resolve(); + switch (data) { + case 0: // success + worker.terminate(); + return; + case 1: // ready + worker.postMessage({ id, f, send, start, memory, wasm }); + return resolve(); + case 2: // panic + wasm_bindgen.__worker_send(id, send); + worker.terminate(); + return; } - worker.terminate(); }; }); while (!wasm_bindgen.__dispatch_poll_worker(next_start_recv)) { diff --git a/src/js/worker.js b/src/js/worker.js index 657f260..0a91ff3 100644 --- a/src/js/worker.js +++ b/src/js/worker.js @@ -6,7 +6,8 @@ self.onmessage = async (e) => { wasm_bindgen.__worker_send(id, send, value); } catch (e) { self.console.error(e); - wasm_bindgen.__worker_send(id, send); + self.postMessage(2); + return; } self.postMessage(0); }; diff --git a/src/lib.rs b/src/lib.rs index f548519..f5811eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -297,10 +297,11 @@ impl JoinHandle { /// Returns the value returned by the thread closure. If the thread panicked, /// this returns a [`JoinError`]. /// - /// # Unwind and Poisoning + /// # Note about panicking /// Note that `wasm32-unknown-unknown` target does not support unwinding yet. - /// This means safety mechanisms like poisoning are not available. Panicking - /// while holding a lock will not release the lock and will likely produce a dead lock. + /// This means no clean up (i.e. drop) is guaranteed, and safety mechanisms such as + /// poisoning are not available. Panicking while holding a lock will not release the lock + /// and will likely produce a dead lock. pub fn join(self) -> Result { // recv() will only error if somehow the thread terminated without sending a value let value = self.recv.recv().map_err(|_| JoinError::Panic(self.id))?; @@ -311,6 +312,12 @@ impl JoinHandle { let value = unsafe { Box::from_raw(value_raw) }; Ok(*value) } + + /// Check if the thread has finished executing, or panicked. + /// This can be used to implement non-blocking join. + pub fn is_finished(&self) -> bool { + self.recv.has_message() || self.recv.is_closed() + } } #[inline]