use std::sync::{Arc, Mutex, Condvar};
use std::thread;
use std::time::Duration;
// Oneshot channel: one value, one send, one receive
struct OneshotSender<T> {
state: Arc<(Mutex<Option<T>>, Condvar)>,
}
struct OneshotReceiver<T> {
state: Arc<(Mutex<Option<T>>, Condvar)>,
}
fn oneshot<T>() -> (OneshotSender<T>, OneshotReceiver<T>) {
let state = Arc::new((Mutex::new(None), Condvar::new()));
(
OneshotSender { state: Arc::clone(&state) },
OneshotReceiver { state },
)
}
impl<T> OneshotSender<T> {
fn send(self, value: T) {
let (lock, cvar) = &*self.state;
*lock.lock().unwrap() = Some(value);
cvar.notify_one();
// self is consumed โ cannot send again
}
}
impl<T> OneshotReceiver<T> {
fn recv(self) -> T {
let (lock, cvar) = &*self.state;
let mut guard = lock.lock().unwrap();
while guard.is_none() {
guard = cvar.wait(guard).unwrap();
}
guard.take().unwrap()
}
fn try_recv(&self) -> Option<T> {
self.state.0.lock().unwrap().take()
}
}
// Use case: request-response pattern
fn handle_request(data: String) -> (String, OneshotReceiver<String>) {
let (tx, rx) = oneshot();
thread::spawn(move || {
thread::sleep(Duration::from_millis(5));
let response = format!("processed: {data}");
tx.send(response);
});
(data.clone(), rx)
}
fn main() {
// Basic oneshot
let (tx, rx) = oneshot::<i32>();
thread::spawn(move || {
thread::sleep(Duration::from_millis(10));
tx.send(42);
});
let value = rx.recv();
println!("Got: {value}");
// Request-response
let requests: Vec<_> = vec!["alpha", "beta", "gamma"]
.into_iter()
.map(|s| handle_request(s.to_string()))
.collect();
for (req, rx) in requests {
println!("{req} -> {}", rx.recv());
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn oneshot_delivers_value() {
let (tx, rx) = oneshot::<i32>();
tx.send(99);
assert_eq!(rx.recv(), 99);
}
#[test]
fn oneshot_across_threads() {
let (tx, rx) = oneshot::<String>();
thread::spawn(move || { tx.send("hello".into()); });
assert_eq!(rx.recv(), "hello");
}
#[test]
fn try_recv_before_send_is_none() {
let (_tx, rx) = oneshot::<i32>();
assert!(rx.try_recv().is_none());
}
}
(* OCaml: oneshot via Mutex + flag *)
type 'a oneshot = {
mutable value : 'a option;
mutex : Mutex.t;
cond : Condition.t;
}
let make () =
{ value = None; mutex = Mutex.create (); cond = Condition.create () }
let send os v =
Mutex.lock os.mutex;
os.value <- Some v;
Condition.signal os.cond;
Mutex.unlock os.mutex
let recv os =
Mutex.lock os.mutex;
while os.value = None do
Condition.wait os.cond os.mutex
done;
let v = Option.get os.value in
Mutex.unlock os.mutex;
v
let () =
let os = make () in
let _ = Thread.create (fun () ->
Thread.delay 0.01;
send os 42
) () in
let v = recv os in
Printf.printf "Received: %d\n" v