Condvar with WASIX
This sample project demonstrates condvar to synchronize threads.
Condvar stands for "condition variable," which is a synchronization primitive in many threading systems, including Rust. It's part of Rust's standard library in the std::sync module. In this tutorial, we'll learn how condvar can be used to synchronize threads, and then build the same program with WASIX.
Prerequisites
Please check that you have the latest version of wasmer runtime as this tutorial depends on version 4.1.1 or higher.
The project requires the following tools to be installed on your system:
Start a new project
$ cargo new --bin wasix-condvar Created binary (application) `wasix-condvar` packageYour wasix-condvar directory structure should look like this:
Your Cargo.toml should look like this:
[package] name = "wasix-condvar" version = "0.1.0" edition = "2021" [dependencies]Writing the Application
Basic Application Setup
We will create a condvar and a mutex to synchronize threads. We will spawn a thread that will sleep for 1 second and then notify the condvar. The main thread instantiates a loop which will wait for the condvar to be notified.
Let's write code for the above:
use std::{ sync::{Arc, Condvar, Mutex}, thread, time::Duration, }; /// Inside of our lock, spawn a new thread, and then wait for it to start. fn main() { // create a condvar, which is a shared object that threads can use to wait for another thread to wake them up. let pair = Arc::new((Mutex::new(false), Condvar::new())); // We enter a lock let (lock, cvar) = &*pair; // acquire the lock on the boolean let mut started = lock.lock().unwrap(); println!("condvar-secondary thread spawn"); { let pair = Arc::clone(&pair); thread::spawn(move || { { println!("condvar-secondary thread started"); let (lock, cvar) = &*pair; //sleep the secondary thread for 1 second so that the main thread can wait for it println!("condvar-secondary thread sleep(1sec) start"); thread::sleep(Duration::from_secs(1)); println!("condvar-secondary thread sleep(1sec) end"); // acquire the lock on the boolean and let mut started = lock.lock().unwrap(); *started = true; println!("condvar-secondary thread set condition"); // We notify the condvar that the value has changed. cvar.notify_one(); println!("condvar-secondary thread notify"); } thread::sleep(Duration::from_millis(50)); println!("condvar-secondary thread exit"); }); } thread::sleep(Duration::from_millis(100)); // Wait for the thread to start up. println!("condvar-main loop"); // check the boolean value and if it is false, wait for the condvar to be notified while !*started { println!("condvar-main wait"); // We wait for the condvar to be notified. started = cvar.wait(started).unwrap(); println!("condvar-main woken"); } println!("condvar-main parent done"); // sleep for 1 second so that the secondary thread can exit thread::sleep(Duration::from_millis(100)); println!("all done"); } Running the Application
Run the application with the cargo:
$ cargo run Compiling wasix-condvar v0.1.0 (/wasix-condvar) Finished dev [unoptimized + debuginfo] target(s) in 0.48s Running `target/debug/wasix-condvar` condvar-secondary thread spawn condvar-secondary thread started condvar-secondary thread sleep(1sec) start condvar-main loop condvar-main wait condvar-secondary thread sleep(1sec) end condvar-secondary thread set condition condvar-secondary thread notify condvar-main woken condvar-main parent done condvar-secondary thread exit all doneLet's try to build this example with WASIX.
Compiling with WASIX
$ cargo wasix build Compiling wasix-condvar v0.1.0 (/wasix-condvar) Finished dev [unoptimized + debuginfo] target(s) in 0.29s info: Post-processing WebAssembly filesIt builds! Now, let's try to run it:
$ cargo wasix run Finished dev [unoptimized + debuginfo] target(s) in 0.00s Running `cargo-wasix target/wasm32-wasmer-wasi/debug/wasix-condvar.wasm` info: Post-processing WebAssembly files Running `target/wasm32-wasmer-wasi/debug/wasix-condvar.wasm` condvar-secondary thread spawn condvar-secondary thread started condvar-secondary thread sleep(1sec) start condvar-main loop condvar-main wait condvar-secondary thread sleep(1sec) end condvar-secondary thread set condition condvar-secondary thread notify condvar-main woken condvar-main parent done condvar-secondary thread exit all doneYay, it works! đ
Does it works with WASI?
Let's try to build the same application with WASI:
You might need to install wasi as a target for your system using rustup target add wasm32-wasi.
$ cargo build --target wasm32-wasi Compiling wasix-condvar v0.1.0 (/wasix-condvar) Finished dev [unoptimized + debuginfo] target(s) in 0.67sIt builds! Now, let's try to run it:
- Using
wasmer:
$wasmer target/wasm32-wasi/debug/wasix-condvar.wasm --enable-all condvar-secondary thread spawn thread 'main' panicked at 'failed to spawn thread: Error { kind: Unsupported, message: "operation not supported on this platform" }', /rustc/7908a1d65496b88626e4b7c193c81d777005d6f3/library/std/src/thread/mod.rs:683:29 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace error: failed to run `target/wasm32-wasi/debug/wasix-condvar.wasm` â°ââļ 1: RuntimeError: unreachable- Using
wasmtime:
$ wasmtime target/wasm32-wasi/debug/wasix-condvar.wasm --wasm-features all condvar-secondary thread spawn thread 'main' panicked at 'failed to spawn thread: Error { kind: Unsupported, message: "operation not supported on this platform" }', /rustc/7908a1d65496b88626e4b7c193c81d777005d6f3/library/std/src/thread/mod.rs:683:29 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace Error: failed to run main module `target/wasm32-wasi/debug/wasix-condvar.wasm` Caused by: 0: failed to invoke command default 1: error while executing at wasm backtrace: 0: 0xf51b - <unknown>!__rust_start_panic 1: 0xf23b - <unknown>!rust_panic 2: 0xf20e - <unknown>!std::panicking::rust_panic_with_hook::hb6c4238e5a98e6ff 3: 0xe1d6 - <unknown>!std::panicking::begin_panic_handler::{{closure}}::h86b4a68243584c88 4: 0xe100 - <unknown>!std::sys_common::backtrace::__rust_end_short_backtrace::hae24bf3ad5d55f98 5: 0xe8eb - <unknown>!rust_begin_unwind 6: 0x13f6f - <unknown>!core::panicking::panic_fmt::haad5666f18c11649 7: 0x15501 - <unknown>!core::result::unwrap_failed::hc92bb94ce7f212ab 8: 0x1b5f - <unknown>!std::thread::spawn::h7f61a4092607114f 9: 0x692a - <unknown>!wasix_condvar::main::h0f9742bbd1eccea0 10: 0x2b26 - <unknown>!core::ops::function::FnOnce::call_once::h2eacb91305e287b4 11: 0xc3b - <unknown>!std::sys_common::backtrace::__rust_begin_short_backtrace::h7a4e994fb8688e77 12: 0x6713 - <unknown>!std::rt::lang_start::{{closure}}::h8fd988fc0a170a71 13: 0xb1e6 - <unknown>!std::rt::lang_start_internal::h56b23c8d718bfa7e 14: 0x66b0 - <unknown>!std::rt::lang_start::h92986fb925c8daf9 15: 0x6bf7 - <unknown>!__main_void 16: 0x465 - <unknown>!_start note: using the `WASMTIME_BACKTRACE_DETAILS=1` environment variable may show more debugging information 2: wasm trap: wasm `unreachable` instruction executedIt doesn't work! đĸ. Currently, with wasm32-wasi.
Conclusion
In this tutorial we learned:
- How to use condvar to synchronize threads.
- How to compile a Rust program to WASIX