[Day32] Read Rust Atomics and Locks - A Simple Mutex-Based Channel

by Mara Bos

Recall

std::sync::Condvar

A more commonly used option for waiting for something to happen to data protected by a mutex

  • Two basic operations: wait and notify
  • std::sync::Condvar
  • Condvar must works together with a Mutex (downside)
  • Moreover, a Condvar is normally only used together with a single Mutex (no more Mutex).
  • Multiple threads can wait on the same Condar
  • Also, notifications can either be sent to one or all of waiting threads.
  • Use Cases: Build a channel
  • Condvar::wait_timeout(): If the thread does not get notified within a certain time, it should wake up and get the lock back.

Notes

Understanding Condvar is quite important for this article.

Function of Channel: Send data from one thread to another.

The simple implementation:

// source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/d945e828bd08719a2d7cb6d758be4611bd90ba2b/src/ch5_channels/s1_simple.rs use std::collections::VecDeque; use std::sync::Condvar; use std::sync::Mutex; pub struct Channel<T> { queue: Mutex<VecDeque<T>>, item_ready: Condvar, } impl<T> Channel<T> { pub fn new() -> Self { Self { queue: Mutex::new(VecDeque::new()), item_ready: Condvar::new(), } } pub fn send(&self, message: T) { self.queue.lock().unwrap().push_back(message); self.item_ready.notify_one(); } pub fn receive(&self) -> T { let mut b = self.queue.lock().unwrap(); loop { if let Some(message) = b.pop_front() { return message; } b = self.item_ready.wait(b).unwrap(); } } }
  • I personally feel that this implementation only encapsulates the use of Condvar into a channel.
  • This channel allows any number of sending and receiving threads.
  • (Downside) If VecDeque::push has to grow the capacity of the VecDeque, all sending and receiving threads will have to wait for that one thread to finish the reallocation.
  • (Downside) this channel’s queue might grow without limit.

References