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 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
use thiserror::Error;
use tokio::sync::watch;
pub const RAISED: bool = true;
pub const LOWERED: bool = false;
/// The gate was dropped, but we still know what value it had before dropping
#[derive(Debug, Error)]
#[error("gate was {0} (where true is RAISED and false is LOWERED) before dropping")]
pub struct BeforeGateDropped(pub bool);
/// The gate was dropped, so raising or lowering it achieves nothing
#[derive(Debug, Error)]
#[error("gate was dropped")]
pub struct GateDropped;
/// The lever was dropped, so waiting any longer for the gate to be raised or lowered can't be meaningful
#[derive(Debug, Error)]
#[error("lever was dropped")]
pub struct LeverDropped;
/// A lever that can [`raise`] and [`lower`] the gate it's associated with
pub struct Lever {
sender: watch::Sender<bool>,
}
impl Lever {
/// Raise the gate.
/// This wakes all tasks waiting on [`Gate::raised`]
/// (and later calls to it will resolve immediately until the gate is [`lower`]ed).
pub fn raise(&self) -> Result<(), GateDropped> {
if self.gate_was_dropped() {
Err(GateDropped)
} else {
self.sender.send_if_modified(|state| {
if *state == LOWERED {
*state = RAISED;
true
} else {
false
}
});
Ok(())
}
}
/// Lower the gate.
/// This wakes all tasks waiting on [`Gate::lowered`]
/// (and later calls to it will resolve immediately until the gate is [`raise`]d).
pub fn lower(&self) -> Result<(), GateDropped> {
if self.gate_was_dropped() {
Err(GateDropped)
} else {
self.sender.send_if_modified(|state| {
if *state == RAISED {
*state = LOWERED;
true
} else {
false
}
});
Ok(())
}
}
/// Returns `Ok(true)` if the gate is raised and `Ok(false)` if it's lowered,
/// or `Err(BeforeGateDropped(true))` if the gate was dropped and was raised before dropping
/// and `Err(BeforeGateDropped(false))` if the gate was dropped and was lowered before dropping.
pub fn is_raised(&self) -> Result<bool, BeforeGateDropped> {
let state = *self.sender.borrow() == RAISED;
if self.gate_was_dropped() {
Err(BeforeGateDropped(state))
} else {
Ok(state)
}
}
/// Returns `Ok(true)` if the gate is lowered and `Ok(false)` if it's raised,
/// or `Err(BeforeGateDropped(true))` if the gate was dropped and was lowered before dropping
/// and `Err(BeforeGateDropped(false))` if the gate was dropped and was raised before dropping.
pub fn is_lowered(&self) -> Result<bool, BeforeGateDropped> {
let state = *self.sender.borrow() == LOWERED;
if self.gate_was_dropped() {
Err(BeforeGateDropped(state))
} else {
Ok(state)
}
}
/// Returns `true` if the gate associated with this lever has been dropped
/// and `false` if it hasn't.
pub fn gate_was_dropped(&self) -> bool {
self.sender.is_closed()
}
}
/// A gate that can be checked if [`is_raised`] or [`is_lowered`] immediately,
/// or can be waited on to be [`raised`] or [`lowered`].
#[derive(Clone)]
pub struct Gate {
receiver: watch::Receiver<bool>,
}
impl Gate {
/// Returns true if the gate (even if the lever has been dropped) is raised and false if it's lowered.
pub fn is_raised(&self) -> bool {
*self.receiver.borrow() == RAISED
}
/// Returns true if the gate (even if the lever has been dropped) is lowered and false if it's raised.
pub fn is_lowered(&self) -> bool {
*self.receiver.borrow() == LOWERED
}
/// Wait until the gate is raised
/// (by a call to [`Lever::raise`])
pub async fn raised(&mut self) -> Result<(), LeverDropped> {
match self.receiver.wait_for(|state| *state == RAISED).await {
Ok(_) => Ok(()),
Err(_) => Err(LeverDropped),
}
}
/// Wait until the gate is lowered
/// (by a call to [`Lever::lower`])
pub async fn lowered(&mut self) -> Result<(), LeverDropped> {
match self.receiver.wait_for(|state| *state == LOWERED).await {
Ok(_) => Ok(()),
Err(_) => Err(LeverDropped),
}
}
/// Returns `true` if the lever associated with this gate has been dropped
/// and `false` if it hasn't.
pub fn lever_was_dropped(&self) -> bool {
self.receiver.has_changed().is_err()
}
}
/// Create a [`Gate`] that is initially raised.
/// The [`Lever`] that it is returned with can raise and lower the gate.
pub fn new_raised() -> (Lever, Gate) {
let (sender, receiver) = watch::channel(RAISED);
let lever = Lever { sender };
let gate = Gate { receiver };
(lever, gate)
}
/// Create a [`Gate`] that is initially lowered.
/// The [`Lever`] that it is returned with can raise and lower the gate.
pub fn new_lowered() -> (Lever, Gate) {
let (sender, receiver) = watch::channel(LOWERED);
let lever = Lever { sender };
let gate = Gate { receiver };
(lever, gate)
}