pub struct Reactor<E: Engine> { /* private fields */ }Expand description
The loop, and what it keeps between turns.
Owns the engine outright. A shard is one thread, one engine and one of these, and none of the three is shared with anything.
Implementations§
Source§impl<E: Engine> Reactor<E>
impl<E: Engine> Reactor<E>
Sourcepub fn new(
engine: E,
id: usize,
epochs: Arc<Epochs>,
lanes: Vec<Receiver<E::Work>>,
) -> Self
pub fn new( engine: E, id: usize, epochs: Arc<Epochs>, lanes: Vec<Receiver<E::Work>>, ) -> Self
A reactor for shard id, taking work from lanes.
One lane per submitter, which is one per network reactor and one per embedded caller thread, and each is single producer single consumer, so no queue here ever has two writers.
§Panics
If id is not a shard epochs has a slot for. That is a wiring mistake
at startup and there is nothing sensible to do with it later.
Sourcepub fn inline(engine: E) -> Self
pub fn inline(engine: E) -> Self
A reactor with no lanes, for the caller who is the shard.
15 section 7’s embedded mode. There is no queue to cross and no thread
to hand to, so the loop stops being a loop and becomes
Reactor::execute, which runs the same dispatch the server path runs.
Y23 asks for the same code rather than the same idea, and this is what
that means in practice.
Sourcepub fn with_maintenance(self, units: u32) -> Self
pub fn with_maintenance(self, units: u32) -> Self
Change the maintenance allowance per turn.
Zero means no maintenance at all, which is what a benchmark measuring the command path alone wants and what nothing in production wants.
Sourcepub const fn engine_mut(&mut self) -> &mut E
pub const fn engine_mut(&mut self) -> &mut E
The engine, for a caller that owns both ends of it.
Sourcepub const fn full_batches(&self) -> u64
pub const fn full_batches(&self) -> u64
Batches that came out full, which is the number that says whether the batch size is doing anything.
A shard whose batches are never full is latency bound and the prefetch walk is buying it very little. One whose batches are always full is throughput bound, and the window is either the right size or too small.
Sourcepub const fn breaks(&self) -> u64
pub const fn breaks(&self) -> u64
Batches ended early by a command that could not be prefetched with the rest of them.
Sourcepub const fn idle_turns(&self) -> u64
pub const fn idle_turns(&self) -> u64
Turns that found nothing to do.
Sourcepub fn carried(&self) -> usize
pub fn carried(&self) -> usize
Commands drained and waiting, which is only ever the tail of a broken batch.
Sourcepub fn tick(&mut self) -> Result<Turn>
pub fn tick(&mut self) -> Result<Turn>
One turn of the six stages.
§Errors
From the ring, at either of the two stages that touch it. The turn stops at the failure rather than carrying on with half a batch, and nothing is lost: whatever was drained is still held for the next turn.
Sourcepub fn run_until(&mut self, stop: &AtomicBool) -> Result<()>
pub fn run_until(&mut self, stop: &AtomicBool) -> Result<()>
Turns until stop is set and there is nothing left to run.
The backoff is deliberately plain: spin for a while, then yield. A shard that is expected to be busy runs on a pinned thread and never gets here, and one that is not busy should give the core back rather than burn it.
§Errors
The first failure any turn reports. Whatever was drained stays drained,
so a caller that decides to carry on can call Reactor::tick again.
Sourcepub fn execute(&mut self, work: E::Work) -> Flow
pub fn execute(&mut self, work: E::Work) -> Flow
Execute one command directly, with no queue in the way.
The embedded path. The same prefetch and the same run the loop
calls, so a command has one implementation rather than an inline one and
a server one that drift apart.
The epoch is entered and left around the call, which is two stores and a
fence. That is what a caller pays for being allowed to hold on to what a
command returned, and Reactor::execute_all is how to pay it once for
many commands instead of once each.
Sourcepub fn execute_all<I>(&mut self, work: I) -> usizewhere
I: IntoIterator<Item = E::Work>,
pub fn execute_all<I>(&mut self, work: I) -> usizewhere
I: IntoIterator<Item = E::Work>,
Execute a batch directly, in the same two walks the loop uses.
Returns how many ran, which is short of what went in when one of them broke the batch. The rest are dropped rather than queued, because an inline caller is the one holding the work and a queue here would be a second place it can live.
The batch goes through the same buffer the loop drains into, so a caller doing this in a hot loop allocates on the first call and never again.