Expand description
The slot that holds the one inner subscription an operator keeps at a time.
An operator such as Switch or
ConcatAll is subscribed to at most one
inner observable at a time, and swaps that subscription as the source emits. Subscribing is an
external API call, so it must happen with no lock held, which splits every swap into two
locked steps around an unlocked one: reserve the slot, subscribe, fill the slot. Between the
two steps the inner observable can terminate the operator synchronously and release the slot,
so the fill has to be able to give the new subscription back.
SubscriptionSlot is that three-step state machine and nothing else. It carries no lock of
its own: it lives inside a model already guarded by the delivery lock — see
SubscriptionContext::update
— and every method takes &mut self. It also disposes nothing: each method hands the
subscription it evicts back to the caller, which passes it to
UpdateOutcome::with_drop_outside
so it is dropped outside the lock.
§When a slot is the right type
Reserved is the whole of what this type adds. Idle and
Active are what an Option<D> already says, so a host that needs only those two keeps its
Option. Reach for a slot only when the host must tell “a value is on its way” apart from
“nothing is held”, which takes all three of:
- the value is built by an external call that has to run with the lock released, so the state is observable by someone else while the build is in flight;
- the slot can be released inside that window, and filling a released slot would install a value that is already dead — in a host that keeps one task alive while there is work to do, that means storing the handle of a task that already stopped, after which no new task is ever started and the queued events stall;
- there is exactly one such value. Several of them are keyed, and an absent key already means
Idle, so each entry collapses back toOption— see the maps inMergeAllandAmb, which run this same reserve/fill protocol without this type.
When the third state is unreachable, a slot only widens the state space with a variant the host’s invariants forbid, which is the opposite of what it is for.
§Why this is not SharedDisposal
The two are the same shape — idle, building, active — but not the same contract, so neither is built on the other:
SharedDisposalis itself a disposal, so it needs a terminalDisposedstate that absorbs later replacements. A slot needs none: its host stops through the delivery, which stops running updates at all (DeliveryStopped), and whatever is left in the slot is disposed when the model is dropped.SharedDisposalreleases its own lock while the builder runs, so a secondreplacecan race the first and it needs a generation id to tell whether a finished build is still current. A slot cannot be raced: the reserve/fill pair is serialized by the delivery lock, andReservedmakes a second reserve unreachable.
Merging them would give every slot a state it never enters and an id it never reads.
Enums§
- Subscription
Slot - The one inner subscription an operator holds at a time.