docs(roadmap): mark selective receive (#4) done
Record what landed for recv_match/try_recv_match and update the no-select gotcha: selective receive stayed per-channel rather than introducing a cross-channel mailbox.
This commit is contained in:
@@ -123,8 +123,30 @@ Shipped. What landed vs the plan:
|
|||||||
dead-pid link stops a non-trapper / messages a trapper; `unlink` prevents
|
dead-pid link stops a non-trapper / messages a trapper; `unlink` prevents
|
||||||
propagation.
|
propagation.
|
||||||
|
|
||||||
### 4. Selective receive (independent track)
|
### 4. Selective receive (independent track) ✅ DONE (`03f3875`)
|
||||||
Channels are strict FIFO MPSC; there's no pattern-matched `receive`.
|
Shipped. What landed vs the plan:
|
||||||
|
- `Receiver::recv_match(pred) -> Result<T, RecvError>` scans the queued
|
||||||
|
`VecDeque` front-to-back, removes+returns the first match, leaves the rest in
|
||||||
|
arrival order; parks and re-scans when nothing matches. `try_recv_match` (the
|
||||||
|
non-blocking variant, mirroring `try_recv`) rolled in same commit.
|
||||||
|
- Wakeup turned out cheaper than feared: `Sender::send` *already* took
|
||||||
|
`parked_receiver` on every push, so "wake on ANY send" needed no send-side
|
||||||
|
change. The one real edit was relaxing `Sender::drop` to unpark the parked
|
||||||
|
receiver on the last-sender drop regardless of queue emptiness — a selective
|
||||||
|
receiver can park on a non-empty no-match queue and must wake to observe
|
||||||
|
closure. No-op for plain `recv` (only ever parks on an empty queue); stress
|
||||||
|
suite stays green.
|
||||||
|
- `pred` is `Fn(&T) -> bool` (not `FnMut`) on purpose: it's re-run from scratch
|
||||||
|
on every scan, so a stateful predicate would re-count surprisingly. It runs
|
||||||
|
under the channel lock — keep it cheap/pure, don't re-enter the channel.
|
||||||
|
- Close semantics: `recv_match` returns `Err(RecvError)` only when closed AND no
|
||||||
|
queued message matches; a match is still returned on a closed channel.
|
||||||
|
Non-matches are left for a later `recv`.
|
||||||
|
- Tests (`tests/selective_recv.rs`): out-of-order match pulled first; non-matches
|
||||||
|
remain in order; park-on-non-empty then wake on a match; closed-with-only-
|
||||||
|
non-matches → Err; closed-but-match-present → match; `try_recv_match` states.
|
||||||
|
|
||||||
|
Original plan, for reference:
|
||||||
- Add `Receiver::recv_match(pred) -> T`: scan the queued `VecDeque`, remove+return
|
- Add `Receiver::recv_match(pred) -> T`: scan the queued `VecDeque`, remove+return
|
||||||
first match, leave the rest in order; park and re-scan on new arrivals.
|
first match, leave the rest in order; park and re-scan on new arrivals.
|
||||||
- This changes channel wakeup: a parked selective receiver must wake on ANY send
|
- This changes channel wakeup: a parked selective receiver must wake on ANY send
|
||||||
@@ -166,7 +188,10 @@ Channels are strict FIFO MPSC; there's no pattern-matched `receive`.
|
|||||||
- **No `select`, no unified per-process mailbox.** Why the supervisor uses the
|
- **No `select`, no unified per-process mailbox.** Why the supervisor uses the
|
||||||
single `supervisor_channel` funnel rather than N monitor channels. trap_exit
|
single `supervisor_channel` funnel rather than N monitor channels. trap_exit
|
||||||
resolved this by giving each trapping actor a dedicated `Receiver<ExitSignal>`
|
resolved this by giving each trapping actor a dedicated `Receiver<ExitSignal>`
|
||||||
inbox (see #3); selective-receive delivery still needs its own decision.
|
inbox (see #3); selective receive (#4) stayed *per-channel* (`recv_match`
|
||||||
|
scans one channel's queue) rather than introducing a cross-channel mailbox —
|
||||||
|
if selective receive ever needs to span the monitor/trap inboxes too, that
|
||||||
|
cross-channel merge is the still-unmade decision.
|
||||||
- **Cooperative-only**: preemption and (future) cancellation both depend on the
|
- **Cooperative-only**: preemption and (future) cancellation both depend on the
|
||||||
actor reaching `check!()`/yield/alloc/blocking points.
|
actor reaching `check!()`/yield/alloc/blocking points.
|
||||||
- `run()` is single-thread (`Config::exact(1)`); tests rely on deterministic
|
- `run()` is single-thread (`Config::exact(1)`); tests rely on deterministic
|
||||||
|
|||||||
Reference in New Issue
Block a user