Module std::sync
Waiting between tasks, over the executor's keys (std::task::Tasks's waker, park and
wake): a task that waits is parked in its executor's table, never polling, and the task that
wakes it readies it there. Both work under every executor (std::task::Pool, std::net's
Evented and Blocking, std::sim's worlds). Under weir explore of a searched world every
park is a scheduling point, and so is the start of every wait (Tasks.preempt()), where a
wake-up from another thread can land between a check and the wait that depends on it.
Source: std/sync.weir; committed interface: std/sync.iface. Index
- struct
Notify: A wake-up one task sends another, as tokio'sNotify:notified()waits for a notification,notify_one()wakes the oldest waiting task or, with none waiting, keeps one notification for the nextnotified(), which then returns at once, so a notification that comes before the wait is not lost (at most one is kept);notify_all()wakes every task waiting now and keeps none. A task cancelled while it waits takes no notification with it, but its place in the queue stays, and thenotify_one()that reaches it wakes no one. - struct
Watch: A value tasks watch for changes, as tokio'swatchchannel:Watch::sendstores a new value and wakes every task waiting inWatch::changed, andWatch::getanswers the value now. Each value sent has a version, one past the one before (the first value's is 0). A watcher keeps the version it last saw in ausizeof its own,seen, andchanged(&mut seen)waits until the version differs from it, so asendthat lands between the watcher's look and its wait is not lost: the wait queues the task's key before it looks at the version. A check followed byNotify::notified()misses anotify_all()in between, which keeps no notification. A watcher sees the latest value, not every one: two sends before it looks are one change to it.sendwakes only the tasks waiting when it is called, so a watcher that was woken and waits again waits for the nextsend. - struct
Channel: A bounded multi-producer, multi-consumer channel, as Go's buffered channels or Rust'sstd::sync::mpsc::sync_channelwith any number of receivers: one value, lent to the tasks that send and receive, which may run on any of an executor's threads (Tis portable).sendwaits while the channel holdscapvalues andrecvwhile it holds none, each parked until the other side makes room or sends.close()ends it: asendfrom then on hands its value back inErr, andrecvanswers what is still buffered, thenNone. Values arrive in the order their senders claimed their places, each received exactly once.
struct Notify
pub struct Notify {
/* private fields */
}
A wake-up one task sends another, as tokio's Notify: notified() waits for a notification,
notify_one() wakes the oldest waiting task or, with none waiting, keeps one notification for
the next notified(), which then returns at once, so a notification that comes before the
wait is not lost (at most one is kept); notify_all() wakes every task waiting now and keeps
none. A task cancelled while it waits takes no notification with it, but its place in the
queue stays, and the notify_one() that reaches it wakes no one.
impl Notify
method new
pub fn new() -> Notify with Alloc
A Notify with no task waiting and no notification kept; allocates its wait queue.
Committed interface (std/sync.iface):
bound Alloc: Alloc any
aborts: Alloc.alloc
method notified
pub fn notified(&self) with Tasks, Lock portable, Alloc portable
Waits for a notification: the kept one if there is one, else the next notify_one() or
notify_all(). Parks the task (Tasks); Lock guards the queue, Alloc grows it. It
begins with Tasks.preempt(), the point where another thread's notify_all() could land
between the caller's check and its wait, so a searched world tries that (systems.md
§21.17).
Committed interface (std/sync.iface):
bound Tasks: Tasks portable cross
bound Lock: Lock any portable
bound Alloc: Alloc any portable
migrates: Tasks.wake migrates inline | Tasks.waker migrates inline
aborts: Tasks.park, Tasks.preempt, Alloc.alloc
scheduling point: always
holds self across a migration: Tasks resumes elsewhere | Tasks.waker migrates inline
hands a closure entering Alloc to Lock
reads regions _
method notify_one
pub fn notify_one(&self) with Tasks, Lock portable
Wakes the oldest waiting task, or keeps the notification for the next notified().
Never waits.
Committed interface (std/sync.iface):
bound Tasks: Tasks portable cross
bound Lock: Lock any portable
migrates: Tasks.wake migrates inline
scheduling point: Lock not quiet | Tasks not quiet
reads regions _
method notify_all
pub fn notify_all(&self) with Tasks, Lock portable
Wakes every task waiting now; keeps no notification. Never waits.
Committed interface (std/sync.iface):
bound Tasks: Tasks portable cross
bound Lock: Lock any portable
migrates: Tasks.wake migrates inline
scheduling point: Lock not quiet | Tasks not quiet
holds self across a migration: Tasks.wake migrates inline
reads regions _
struct Watch
pub struct Watch<T> {
/* private fields */
}
A value tasks watch for changes, as tokio's watch channel: Watch::send stores a new
value and wakes every task waiting in Watch::changed, and Watch::get answers the value
now. Each value sent has a version, one past the one before (the first value's is 0). A
watcher keeps the version it last saw in a usize of its own, seen, and changed(&mut
seen) waits until the version differs from it, so a send that lands between the watcher's
look and its wait is not lost: the wait queues the task's key before it looks at the version.
A check followed by Notify::notified() misses a notify_all() in between, which keeps no
notification. A watcher sees the latest value, not every one: two sends before it looks are
one change to it. send wakes only the tasks waiting when it is called, so a watcher that
was woken and waits again waits for the next send.
impl<T: copy + owned + portable> Watch<T>
method new
pub fn new(v: T) -> Watch<T> with Alloc
A watch holding v at version 0; allocates the wait queue.
Committed interface (std/sync.iface):
bound Alloc: Alloc any
aborts: Alloc.alloc
method get
pub fn get(&self) -> T with Lock
The value now.
Committed interface (std/sync.iface):
bound Lock: Lock any
scheduling point: Lock not quiet
method read
pub fn read(&self, seen: &mut usize) -> T with Lock
The value now, with its version stored in seen: what a watcher reads before it waits in
Watch::changed.
Committed interface (std/sync.iface):
bound Lock: Lock any
scheduling point: Lock not quiet
writes through seen: everything and its shape
method send
pub fn send(&self, v: T) with Tasks, Lock portable
Stores v as the next version and wakes every task waiting in Watch::changed. Never
waits.
Committed interface (std/sync.iface):
bound Tasks: Tasks portable cross
bound Lock: Lock any portable
migrates: Tasks migrates inline | Tasks.wake migrates inline
scheduling point: always
holds self across a migration: Tasks.wake migrates inline
reads regions _
method send_if_modified
pub fn send_if_modified(&self, f: fn(&mut T) -> Bool env {A}) -> Bool with
Tasks,
Lock portable,
Changes the value in place with f under the watch's lock: when f answers true the
result is the next version and every task waiting in Watch::changed is woken, as
send does, and when it answers false nothing changes and none is woken (tokio's
send_if_modified). One call is one step, so |x| if *x < n { *x = n; true } else {
false } keeps a maximum however many tasks call it at once. Answers what f answered.
Committed interface (std/sync.iface):
bound Tasks: Tasks portable cross
bound Lock: Lock any portable
migrates: Tasks migrates inline | Tasks.wake migrates inline
scheduling point: always
holds what may not move at a scheduling point: always
holds self across a migration: Tasks.wake migrates inline
callback f: envelope {A}; handed to Lock; holds self; runs under a lock
reads regions _
method changed
pub fn changed(&self, seen: &mut usize) with Tasks, Lock portable, Alloc portable
Waits until the version differs from seen, then stores the version now in seen;
returns at once when it already differs. Parks the task (Tasks); Lock guards the
value and the wait queue, Alloc grows the queue.
Committed interface (std/sync.iface):
bound Tasks: Tasks portable cross
bound Lock: Lock any portable
bound Alloc: Alloc any portable
may diverge
migrates: Tasks.wake migrates inline | Tasks.waker migrates inline
aborts: Tasks.park, Alloc.alloc
scheduling point: always
holds self across a migration: Tasks resumes elsewhere | Tasks.wake migrates inline | Tasks.waker migrates inline
holds seen across a migration: Tasks resumes elsewhere | Tasks.wake migrates inline | Tasks.waker migrates inline
writes through seen: everything
hands a closure entering Alloc to Lock
reads regions _
struct Channel
pub struct Channel<T> {
/* private fields */
}
A bounded multi-producer, multi-consumer channel, as Go's buffered channels or Rust's
std::sync::mpsc::sync_channel with any number of receivers: one value, lent to the tasks
that send and receive, which may run on any of an executor's threads (T is portable).
send waits while the channel holds cap values and recv while it holds none, each parked
until the other side makes room or sends. close() ends it: a send from then on hands its
value back in Err, and recv answers what is still buffered, then None. Values arrive in
the order their senders claimed their places, each received exactly once.
impl<T: portable> Channel<T>
method new
pub fn new(cap: usize) -> Channel<T> with Alloc {alloc: tail} requires 0usize < cap
A channel that holds up to cap values; requires cap > 0: there is no rendezvous
channel, so Channel::new(0) is rejected where it is written (R-EntryFact). Allocates
the ring.
Committed interface (std/sync.iface):
bound Alloc: Alloc { alloc: tail } any
method send
pub fn send(&self, v: T) -> Result<Unit, T> with Tasks, Lock portable, Alloc portable
Sends v, parking while the channel is full; Err(v) once it is closed, including for a
sender parked when close comes.
Committed interface (std/sync.iface):
bound Tasks: Tasks portable cross
bound Lock: Lock any portable
bound Alloc: Alloc any portable
may diverge
migrates: Tasks migrates inline | Tasks.wake migrates inline | Tasks.waker migrates inline
aborts: Tasks.park, Tasks.preempt, Alloc.alloc
scheduling point: always
holds self across a migration: Tasks migrates inline | Tasks resumes elsewhere | Tasks.wake migrates inline | Tasks.waker migrates inline
holds v across a migration: Tasks migrates inline | Tasks resumes elsewhere | Tasks.wake migrates inline | Tasks.waker migrates inline
destroys values of T
stores T in regions _
hands a closure entering Alloc to Lock
reads regions _
method recv
pub fn recv(&self) -> Option<T> with Tasks, Lock portable, Alloc portable
The oldest value sent, parking while the channel is empty; None once it is closed and
every value sent has been received (a parked receiver is woken by close).
Committed interface (std/sync.iface):
bound Tasks: Tasks portable cross
bound Lock: Lock any portable
bound Alloc: Alloc any portable
may diverge
migrates: Tasks migrates inline | Tasks.wake migrates inline | Tasks.waker migrates inline
aborts: Tasks.park, Tasks.preempt, Alloc.alloc
scheduling point: always
holds self across a migration: Tasks migrates inline | Tasks resumes elsewhere | Tasks.wake migrates inline | Tasks.waker migrates inline
destroys values of T
hands a closure entering Alloc to Lock
reads regions _
method close
pub fn close(&self) with Tasks, Lock portable
Closes the channel and wakes every task waiting on it; values already sent stay to be received. Closing twice is closing once. Never waits.
Committed interface (std/sync.iface):
bound Tasks: Tasks portable cross
bound Lock: Lock any portable
migrates: Tasks migrates inline | Tasks.wake migrates inline
scheduling point: always
holds self across a migration: Tasks migrates inline | Tasks resumes elsewhere | Tasks.wake migrates inline
reads regions _