blob: e16519e4d85f11b8b1f77c3ae73c29b1560bed33 [file] [log] [blame]
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::{Condvar, Mutex};
use std::usize;
use sleep::Sleep;
/// We define various kinds of latches, which are all a primitive signaling
/// mechanism. A latch starts as false. Eventually someone calls `set()` and
/// it becomes true. You can test if it has been set by calling `probe()`.
///
/// Some kinds of latches, but not all, support a `wait()` operation
/// that will wait until the latch is set, blocking efficiently. That
/// is not part of the trait since it is not possibly to do with all
/// latches.
///
/// The intention is that `set()` is called once, but `probe()` may be
/// called any number of times. Once `probe()` returns true, the memory
/// effects that occurred before `set()` become visible.
///
/// It'd probably be better to refactor the API into two paired types,
/// but that's a bit of work, and this is not a public API.
///
/// ## Memory ordering
///
/// Latches need to guarantee two things:
///
/// - Once `probe()` returns true, all memory effects from the `set()`
/// are visible (in other words, the set should synchronize-with
/// the probe).
/// - Once `set()` occurs, the next `probe()` *will* observe it. This
/// typically requires a seq-cst ordering. See [the "tickle-then-get-sleepy" scenario in the sleep
/// README](/src/sleep/README.md#tickle-then-get-sleepy) for details.
pub trait Latch: LatchProbe {
/// Set the latch, signalling others.
fn set(&self);
}
pub trait LatchProbe {
/// Test if the latch is set.
fn probe(&self) -> bool;
}
/// Spin latches are the simplest, most efficient kind, but they do
/// not support a `wait()` operation. They just have a boolean flag
/// that becomes true when `set()` is called.
pub struct SpinLatch {
b: AtomicBool,
}
impl SpinLatch {
#[inline]
pub fn new() -> SpinLatch {
SpinLatch {
b: AtomicBool::new(false),
}
}
}
impl LatchProbe for SpinLatch {
#[inline]
fn probe(&self) -> bool {
self.b.load(Ordering::SeqCst)
}
}
impl Latch for SpinLatch {
#[inline]
fn set(&self) {
self.b.store(true, Ordering::SeqCst);
}
}
/// A Latch starts as false and eventually becomes true. You can block
/// until it becomes true.
pub struct LockLatch {
m: Mutex<bool>,
v: Condvar,
}
impl LockLatch {
#[inline]
pub fn new() -> LockLatch {
LockLatch {
m: Mutex::new(false),
v: Condvar::new(),
}
}
/// Block until latch is set.
pub fn wait(&self) {
let mut guard = self.m.lock().unwrap();
while !*guard {
guard = self.v.wait(guard).unwrap();
}
}
}
impl LatchProbe for LockLatch {
#[inline]
fn probe(&self) -> bool {
// Not particularly efficient, but we don't really use this operation
let guard = self.m.lock().unwrap();
*guard
}
}
impl Latch for LockLatch {
#[inline]
fn set(&self) {
let mut guard = self.m.lock().unwrap();
*guard = true;
self.v.notify_all();
}
}
/// Counting latches are used to implement scopes. They track a
/// counter. Unlike other latches, calling `set()` does not
/// necessarily make the latch be considered `set()`; instead, it just
/// decrements the counter. The latch is only "set" (in the sense that
/// `probe()` returns true) once the counter reaches zero.
#[derive(Debug)]
pub struct CountLatch {
counter: AtomicUsize,
}
impl CountLatch {
#[inline]
pub fn new() -> CountLatch {
CountLatch {
counter: AtomicUsize::new(1),
}
}
#[inline]
pub fn increment(&self) {
debug_assert!(!self.probe());
self.counter.fetch_add(1, Ordering::Relaxed);
}
}
impl LatchProbe for CountLatch {
#[inline]
fn probe(&self) -> bool {
// Need to acquire any memory reads before latch was set:
self.counter.load(Ordering::SeqCst) == 0
}
}
impl Latch for CountLatch {
/// Set the latch to true, releasing all threads who are waiting.
#[inline]
fn set(&self) {
self.counter.fetch_sub(1, Ordering::SeqCst);
}
}
/// A tickling latch wraps another latch type, and will also awaken a thread
/// pool when it is set. This is useful for jobs injected between thread pools,
/// so the source pool can continue processing its own work while waiting.
pub struct TickleLatch<'a, L: Latch> {
inner: L,
sleep: &'a Sleep,
}
impl<'a, L: Latch> TickleLatch<'a, L> {
#[inline]
pub fn new(latch: L, sleep: &'a Sleep) -> Self {
TickleLatch {
inner: latch,
sleep: sleep,
}
}
}
impl<'a, L: Latch> LatchProbe for TickleLatch<'a, L> {
#[inline]
fn probe(&self) -> bool {
self.inner.probe()
}
}
impl<'a, L: Latch> Latch for TickleLatch<'a, L> {
#[inline]
fn set(&self) {
self.inner.set();
self.sleep.tickle(usize::MAX);
}
}