Skip to main content

Reactor

Struct Reactor 

Source
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>

Source

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.

Source

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.

Source

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.

Source

pub const fn engine(&self) -> &E

The engine.

Source

pub const fn engine_mut(&mut self) -> &mut E

The engine, for a caller that owns both ends of it.

Source

pub const fn id(&self) -> usize

The shard this reactor is.

Source

pub const fn turns(&self) -> u64

Turns taken.

Source

pub const fn commands(&self) -> u64

Commands executed.

Source

pub const fn batches(&self) -> u64

Batches drained.

Source

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.

Source

pub const fn breaks(&self) -> u64

Batches ended early by a command that could not be prefetched with the rest of them.

Source

pub const fn idle_turns(&self) -> u64

Turns that found nothing to do.

Source

pub fn carried(&self) -> usize

Commands drained and waiting, which is only ever the tail of a broken batch.

Source

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.

Source

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.

Source

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.

Source

pub fn execute_all<I>(&mut self, work: I) -> usize
where 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.

Trait Implementations§

Source§

impl<E: Engine> Debug for Reactor<E>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<E> !RefUnwindSafe for Reactor<E>

§

impl<E> !Sync for Reactor<E>

§

impl<E> !UnwindSafe for Reactor<E>

§

impl<E> Freeze for Reactor<E>
where E: Freeze, Vec<Receiver<<E as Engine>::Work>>: Freeze, VecDeque<<E as Engine>::Work>: Freeze,

§

impl<E> Send for Reactor<E>
where E: Send, Vec<Receiver<<E as Engine>::Work>>: Send, VecDeque<<E as Engine>::Work>: Send,

§

impl<E> Unpin for Reactor<E>
where E: Unpin, Vec<Receiver<<E as Engine>::Work>>: Unpin, VecDeque<<E as Engine>::Work>: Unpin,

§

impl<E> UnsafeUnpin for Reactor<E>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.