1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
//! `SyncFlag`s, or syncronization flags, are one-way Boolean values that can
//! be shared across threads, sending messages from a single producer to
//! any number of consumers.
//!
//! Like `std::sync::mpsc`, `SyncFlag`s are created in pairs: a transmitter
//! that can't be duplicated and a receiver that can be duplicated any number
//! of times. `SyncFlagTx` can be used in, for instance, a controller/main
//! thread, which can pass clones of the corresponding `SyncFlagRx` to the
//! worker threads it spawns in order to control them.
//!
//! # Examples
//!
//! ```
//! use workctl::new_syncflag;
//! use std::thread;
//!
//! // Create a new SyncFlag set to communicate with the spawned thread.
//! let (mut tx, rx) = new_syncflag(true);
//!
//! // This isn't technically needed in this case, but if we were spawning more
//! // than one thread we'd create a clone for each.
//! let thread_rx = rx.clone();
//! thread::spawn(move || {
//!     // Do nothing as long as the sync flag is true. Really, you'd do work here.
//!     while thread_rx.get() {
//!         thread::yield_now();
//!     }
//!     println!("Thread got signal to close.");
//! });
//!
//! // Do something here, like maybe adding work to a WorkQueue
//!
//! // The program has completed, so set the SyncFlag to false.
//! tx.set(false);
//! ```

use std::sync::{Arc, Mutex};

/// `SyncFlagTx` is the transmitting (mutable) half of a Single Producer,
/// Multiple Consumer Boolean (e.g. the opposite of `std::sync::mpsc`).
/// A single controller can use this to send info to any number of worker
/// threads, for instance.
///
/// `SyncFlagTx` is not Clone because it should only exist in one place.
///
/// # Panics
/// The functions on this type will panic if the underlying mutex became poisoned;
/// that is, if there was a panic during the execution of any mutex-acquiring
/// function. This is pretty unlikely.
pub struct SyncFlagTx {
    inner: Arc<Mutex<bool>>,
}

impl SyncFlagTx {
    /// Sets the interior value of the `SyncFlagTx` which will be read by any
    /// `SyncFlagRx` that exist for this SyncFlag.
    pub fn set(&mut self, state: bool) {
        if let Ok(mut v) = self.inner.lock() {
            // The * (deref operator) means assigning to what's inside the
            // MutexGuard, not the guard itself (which would be silly)
            *v = state;
        } else {
            panic!("SyncFlagTx::set() tried to lock a poisoned mutex.");
        }
    }
}

/// `SyncFlagRx` is the receiving (immutable) half of a Single Producer,
/// Multiple Consumer Boolean (e.g. the opposite of `std::sync::mpsc`).
/// An number of worker threads can use this to get info from a single
/// controller, for instance.
///
/// `SyncFlagRx` is Clone so it can be shared across threads.
///
/// # Panics
/// The functions on this type will panic if the underlying mutex became poisoned;
/// that is, if there was a panic during the execution of any mutex-acquiring
/// function. This is pretty unlikely.
#[derive(Clone)]
pub struct SyncFlagRx {
    inner: Arc<Mutex<bool>>,
}

impl SyncFlagRx {
    /// Gets the interior state of the `SyncFlagRx` to whatever the corresponding
    /// `SyncFlagTx` last set it to.
    ///
    /// # Errors
    /// If the underlying mutex is poisoned this might return an error.
    pub fn get(&self) -> bool {
        if let Ok(v) = self.inner.lock() {
            // Deref the MutexGuard to get at the bool inside
            *v
        } else {
            panic!("SyncFlagRx::get() tried to lock a poisoned mutex.");
        }
    }
}

/// Create a new `SyncFlagTx` and `SyncFlagRx` that can be used to share a bool
/// across a number of threads.
pub fn new_syncflag(initial_state: bool) -> (SyncFlagTx, SyncFlagRx) {
    let state = Arc::new(Mutex::new(initial_state));
    let tx = SyncFlagTx { inner: state.clone() };
    let rx = SyncFlagRx { inner: state.clone() };

    return (tx, rx);
}