Skip to content

Commit 482994b

Browse files
committed
docs: Document the slot module
1 parent 2086082 commit 482994b

File tree

2 files changed

+76
-3
lines changed

2 files changed

+76
-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: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,34 @@ 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. Cancellation is best-effort:
25+
/// cancelling a callback that has already run is not an error, and does not
26+
/// signal this to the caller of `cancel`.
1027
pub struct Slot<T> {
1128
state: AtomicUsize,
1229
slot: Lock<Option<T>>,
1330
on_full: Lock<Option<Box<FnBox<T>>>>,
1431
on_empty: Lock<Option<Box<FnBox<T>>>>,
1532
}
1633

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

@@ -30,8 +48,17 @@ pub struct OnFullError(());
3048
#[derive(Debug, PartialEq)]
3149
pub struct OnEmptyError(());
3250

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

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

3764
fn _is_send<T: Send>() {}
@@ -49,6 +76,9 @@ const STATE_BITS: usize = 3;
4976
const STATE_MASK: usize = (1 << STATE_BITS) - 1;
5077

5178
impl<T: 'static> Slot<T> {
79+
/// Creates a new `Slot` containing `val`, which may be `None` to create an
80+
/// empty `Slot`.
81+
// TODO: Separate this into `new`, and `with_value` functions?
5282
pub fn new(val: Option<T>) -> Slot<T> {
5383
Slot {
5484
state: AtomicUsize::new(if val.is_some() {DATA} else {0}),
@@ -59,6 +89,14 @@ impl<T: 'static> Slot<T> {
5989
}
6090

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

92130
// PRODUCER
131+
/// Registers `f` as a callback to run when the slot becomes empty. The
132+
/// callback will run immediately if the slot is already empty. Returns a
133+
/// token that can be used to cancel the callback.
134+
///
135+
/// This method is to be called by the producer.
136+
///
137+
/// # Panics
138+
///
139+
/// Panics if another callback was already registered via `on_empty` or
140+
/// `on_full`.
93141
pub fn on_empty<F>(&self, f: F) -> Token
94142
where F: FnOnce(&Slot<T>) + Send + 'static
95143
{
96144
let mut state = State(self.state.load(Ordering::SeqCst));
145+
// TODO: return OnEmptyError instead of assert.
97146
assert!(!state.flag(ON_EMPTY));
98147
if !state.flag(DATA) {
99148
f(self);
100149
return Token(0)
101150
}
151+
// TODO: return OnEmptyError instead of assert?
102152
assert!(!state.flag(ON_FULL));
103153
let mut slot = self.on_empty.try_lock().expect("on_empty interference");
104154
assert!(slot.is_none());
@@ -129,6 +179,13 @@ impl<T: 'static> Slot<T> {
129179
}
130180

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

162219
// CONSUMER
220+
/// Registers `f` as a callback to run when the slot becomes full. The
221+
/// callback will run immediately if the slot is already full. Returns a
222+
/// token that can be used to cancel the callback.
223+
///
224+
/// This method is to be called by the consumer.
225+
///
226+
/// # Panics
227+
///
228+
/// Panics if another callback was already registered via `on_empty` or
229+
/// `on_full`.
163230
pub fn on_full<F>(&self, f: F) -> Token
164231
where F: FnOnce(&Slot<T>) + Send + 'static
165232
{
166233
let mut state = State(self.state.load(Ordering::SeqCst));
234+
// TODO: return OnFullError instead of assert.
167235
assert!(!state.flag(ON_FULL));
168236
if state.flag(DATA) {
169237
f(self);
170238
return Token(0)
171239
}
240+
// TODO: return OnFullError instead of assert?
172241
assert!(!state.flag(ON_EMPTY));
173242
let mut slot = self.on_full.try_lock().expect("on_full interference");
174243
assert!(slot.is_none());
@@ -198,6 +267,8 @@ impl<T: 'static> Slot<T> {
198267
}
199268
}
200269

270+
/// Cancels the callback associated with `token`. Returns successfully even
271+
/// if the callback had already run; see [Cancellation](#cancellation).
201272
pub fn cancel(&self, token: Token) {
202273
let token = token.0;
203274
if token == 0 {
@@ -241,6 +312,7 @@ impl<T: 'static> Slot<T> {
241312
}
242313

243314
impl<T> TryProduceError<T> {
315+
/// Extracts the value that was attempted to be produced.
244316
pub fn into_inner(self) -> T {
245317
self.0
246318
}

0 commit comments

Comments
 (0)