odem-rs 0.3.0

Object-based Discrete-Event Modelling in Rust using async/await
Documentation
//! This example demonstrates the use of control variables and the `until!`
//! macro for event-driven synchronization.
//!
//! Two concurrent processes each set a boolean signal after a delay. The main
//! process waits for both signals using `until!`, which suspends efficiently
//! until the compound condition becomes true. The example also shows the
//! equivalent manual construction via `ControlExpr`.

use core::pin::pin;
use odem_rs::{
	prelude::*,
	sync::control::ControlExpr,
	tracing::{Level, debug},
};

// 1. Process sending one of the release signals after a delay.
async fn releaser(sim: &Sim, sig: &Control<bool>, delay: f64) {
	sim.advance(delay).await;
	debug!("A signal has been sent.");
	sig.set(true);
}

// 2. The main simulation logic.
async fn gates(sim: &Sim) {
	debug!("Gate is closed. Waiting for signals.");

	let signal_a = Control::new(false);
	let signal_b = Control::new(false);

	// Activate two concurrent releaser processes.
	let r1 = pin!(Job::new(releaser(sim, &signal_a, 5.0)));
	let r2 = pin!(Job::new(releaser(sim, &signal_b, 10.0)));
	sim.activate(r1);
	sim.activate(r2);

	// 3a. Manual: Pass a tuple of dependencies and a closure.
	//     This is functionally equivalent to the macro.
	ControlExpr::new((&signal_a, &signal_b), |(a, b)| a.get() && b.get()).await;

	// 3b. Ergonomic: The macro generates the above code.
	until!(signal_a && signal_b).await;

	debug!("Both signals received. Gate is open!");
}

fn main() {
	// Initialize logging with a custom format and a debug level.
	tracing_subscriber::fmt()
		.with_max_level(Level::DEBUG)
		.with_target(false)
		.with_timer(model_time!("[{time:2}]"))
		.init();

	simulation(gates).expect("deadlock unexpected");
}