Documentation
Language Guide
Rust
Tutorials
Condition variables

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:

Cargo.toml
[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:

src/main.rs
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:

  1. 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
  1. 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
wasix-rust-examples/wasix-condvar