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` package
Your 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 done
Let'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 files
It 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 done
Yay, 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.67s
It 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 executed
It 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