Skip to content

Commit 441d689

Browse files
committed
docs: Document the slot module
1 parent 2086082 commit 441d689

File tree

2 files changed

+79
-3
lines changed

2 files changed

+79
-3
lines changed

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod lock;
2+
/// A slot in memory for communicating between a producer and a consumer.
23
mod slot;
34
mod util;
45

src/slot.rs

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,35 @@ use lock::Lock;
55
/// A slot in memory intended to represent the communication channel between one
66
/// producer and one consumer.
77
///
8-
/// Each slot contains space for a piece of data, `T`, and space for callbacks.
9-
/// The callbacks can be run when the data is either full or empty.
8+
/// Each slot contains space for a piece of data of type `T`, and can have callbacks
9+
/// registered to run when the slot is either full or empty.
10+
///
11+
/// # Registering callbacks
12+
///
13+
/// [`on_empty`](#method.on_empty) registers a callback to run when the slot
14+
/// becomes empty, and [`on_full`](#method.on_full) registers one to run when it
15+
/// becomes full. In both cases, the callback will run immediately if possible.
16+
///
17+
/// At most one callback can be registered at any given time: it is an error to
18+
/// attempt to register a callback with `on_full` if one is currently registered
19+
/// via `on_empty`, or any other combination.
20+
///
21+
/// # Cancellation
22+
///
23+
/// Registering a callback returns a `Token` which can be used to
24+
/// [`cancel`](#method.cancel) the callback. Only callbacks that have not yet
25+
/// started running can be canceled. Canceling a callback that has already run
26+
/// is not an error, and `cancel` does not signal whether or not the callback
27+
/// was actually canceled to the caller.
1028
pub struct Slot<T> {
1129
state: AtomicUsize,
1230
slot: Lock<Option<T>>,
1331
on_full: Lock<Option<Box<FnBox<T>>>>,
1432
on_empty: Lock<Option<Box<FnBox<T>>>>,
1533
}
1634

17-
/// Error value returned from erroneous calls to `try_produce`.
35+
/// Error value returned from erroneous calls to `try_produce`, which contains
36+
/// the value that was passed to `try_produce`.
1837
#[derive(Debug, PartialEq)]
1938
pub struct TryProduceError<T>(T);
2039

@@ -30,8 +49,17 @@ pub struct OnFullError(());
3049
#[derive(Debug, PartialEq)]
3150
pub struct OnEmptyError(());
3251

52+
/// A `Token` represents a registered callback, and can be used to cancel the callback.
3353
pub struct Token(usize);
3454

55+
/// Slot state: the lowest 3 bits are flags; the remaining bits are used to
56+
/// store the `Token` for the currently registered callback. The special token
57+
/// value 0 means no callback is registered.
58+
///
59+
/// The flags are:
60+
/// - `DATA`: the `Slot` contains a value
61+
/// - `ON_FULL`: the `Slot` has an `on_full` callback registered
62+
/// - `ON_EMPTY`: the `Slot` has an `on_empty` callback registered
3563
struct State(usize);
3664

3765
fn _is_send<T: Send>() {}
@@ -49,6 +77,9 @@ const STATE_BITS: usize = 3;
4977
const STATE_MASK: usize = (1 << STATE_BITS) - 1;
5078

5179
impl<T: 'static> Slot<T> {
80+
/// Creates a new `Slot` containing `val`, which may be `None` to create an
81+
/// empty `Slot`.
82+
// TODO: Separate this into `new`, and `with_value` functions?
5283
pub fn new(val: Option<T>) -> Slot<T> {
5384
Slot {
5485
state: AtomicUsize::new(if val.is_some() {DATA} else {0}),
@@ -59,6 +90,14 @@ impl<T: 'static> Slot<T> {
5990
}
6091

6192
// PRODUCER
93+
/// Attempts to store `t` in the slot.
94+
///
95+
/// This method is to be called by the producer.
96+
///
97+
/// # Errors
98+
///
99+
/// Returns `Err` if the slot is already full. The value you attempted to
100+
/// store is included in the error value.
62101
pub fn try_produce(&self, t: T) -> Result<(), TryProduceError<T>> {
63102
let mut state = State(self.state.load(Ordering::SeqCst));
64103
assert!(!state.flag(ON_EMPTY));
@@ -90,15 +129,27 @@ impl<T: 'static> Slot<T> {
90129
}
91130

92131
// PRODUCER
132+
/// Registers `f` as a callback to run when the slot becomes empty. The
133+
/// callback will run immediately if the slot is already empty. Returns a
134+
/// token that can be used to cancel the callback.
135+
///
136+
/// This method is to be called by the producer.
137+
///
138+
/// # Panics
139+
///
140+
/// Panics if another callback was already registered via `on_empty` or
141+
/// `on_full`.
93142
pub fn on_empty<F>(&self, f: F) -> Token
94143
where F: FnOnce(&Slot<T>) + Send + 'static
95144
{
96145
let mut state = State(self.state.load(Ordering::SeqCst));
146+
// TODO: return OnEmptyError instead of assert.
97147
assert!(!state.flag(ON_EMPTY));
98148
if !state.flag(DATA) {
99149
f(self);
100150
return Token(0)
101151
}
152+
// TODO: return OnEmptyError instead of assert?
102153
assert!(!state.flag(ON_FULL));
103154
let mut slot = self.on_empty.try_lock().expect("on_empty interference");
104155
assert!(slot.is_none());
@@ -129,6 +180,13 @@ impl<T: 'static> Slot<T> {
129180
}
130181

131182
// CONSUMER
183+
/// Attempts to consume the value stored in the slot.
184+
///
185+
/// This method is to be called by the consumer.
186+
///
187+
/// # Errors
188+
///
189+
/// Returns `Err` if the slot is already empty.
132190
pub fn try_consume(&self) -> Result<T, TryConsumeError> {
133191
let mut state = State(self.state.load(Ordering::SeqCst));
134192
assert!(!state.flag(ON_FULL));
@@ -160,15 +218,27 @@ impl<T: 'static> Slot<T> {
160218
}
161219

162220
// CONSUMER
221+
/// Registers `f` as a callback to run when the slot becomes full. The
222+
/// callback will run immediately if the slot is already full. Returns a
223+
/// token that can be used to cancel the callback.
224+
///
225+
/// This method is to be called by the consumer.
226+
///
227+
/// # Panics
228+
///
229+
/// Panics if another callback was already registered via `on_empty` or
230+
/// `on_full`.
163231
pub fn on_full<F>(&self, f: F) -> Token
164232
where F: FnOnce(&Slot<T>) + Send + 'static
165233
{
166234
let mut state = State(self.state.load(Ordering::SeqCst));
235+
// TODO: return OnFullError instead of assert.
167236
assert!(!state.flag(ON_FULL));
168237
if state.flag(DATA) {
169238
f(self);
170239
return Token(0)
171240
}
241+
// TODO: return OnFullError instead of assert?
172242
assert!(!state.flag(ON_EMPTY));
173243
let mut slot = self.on_full.try_lock().expect("on_full interference");
174244
assert!(slot.is_none());
@@ -198,6 +268,10 @@ impl<T: 'static> Slot<T> {
198268
}
199269
}
200270

271+
/// Cancels the callback associated with `token`.
272+
///
273+
/// Canceling a callback that has already started running, or has already
274+
/// run will do nothing, and is not an error. See [Cancellation](#cancellation).
201275
pub fn cancel(&self, token: Token) {
202276
let token = token.0;
203277
if token == 0 {
@@ -241,6 +315,7 @@ impl<T: 'static> Slot<T> {
241315
}
242316

243317
impl<T> TryProduceError<T> {
318+
/// Extracts the value that was attempted to be produced.
244319
pub fn into_inner(self) -> T {
245320
self.0
246321
}

0 commit comments

Comments
 (0)