pub struct ExecutorState {Show 14 fields
pub state_owner: String,
pub wakeup_priority: PriorityOrder,
pub global_tasks: SharedTaskQueue,
pub current_task: Rc<RefCell<Option<Entry>>>,
pub local_tasks: Rc<RefCell<EntryList<BoxedExecutionIterator>>>,
pub task_graph: Rc<RefCell<HashMap<Entry, Entry>>>,
pub packed_tasks: Rc<RefCell<HashMap<Entry, bool>>>,
pub processing: Rc<RefCell<VecDeque<Entry>>>,
pub rng: Rc<RefCell<ChaCha8Rng>>,
pub sleepers: Sleepers<Sleepable>,
pub idler: Rc<RefCell<IdleMan>>,
pub depends_true_violations: Rc<RefCell<HashMap<Entry, u32>>>,
pub root: Ctx,
pub task_ctx: Rc<RefCell<HashMap<Entry, Ctx>>>,
}Expand description
Underlying state of an executor.
It is the inner structure used by a LocalExecutorEngine to manage
all relevant tasks it takes on.
It’s important to note that if a task panic, so will the ExecutorState
which can’t be restored and all tasks owned will be lost, so its important
you always handle panic in the actual task itself by using either the DoNext,
CollectNext and OnNext and if you implement your own ExecutionIterator to
ensure to follow the pattern demonstrated in those implementations to correctly
handle panic from tasks even at the trade of some runtime cost from Mutex which
allow you use std::panic::catch_unwind.
Fields§
§state_owner: String§wakeup_priority: PriorityOrderwhat priority should waking task be placed.
global_tasks: SharedTaskQueueglobal_tasks are the shared tasks coming from the main thread
they generally will always come in fifo order and will be processed
in the order received.
current_task: Rc<RefCell<Option<Entry>>>indicates the current task currently being handled for safety checks.
local_tasks: Rc<RefCell<EntryList<BoxedExecutionIterator>>>tasks owned by the executor which should be processed first before taking on the next task from the global queue, task generally come in here from the current task taking from the global queue. This allows us keep the overall execution of any async iterator within the same thread and keep a one thread execution.
task_graph: Rc<RefCell<HashMap<Entry, Entry>>>task_graph provides dependency mapping of a Task (Key) lifted
by another Task (Value) allowing us to go from lifted task
to it’s tree of dependents.
packed_tasks: Rc<RefCell<HashMap<Entry, bool>>>map used to identify that a task was packed.
processing: Rc<RefCell<VecDeque<Entry>>>the queue used for performing perioritization, usually the global task taking will first be stored in the local tasks list and have it’s related Entry pointer added into the queue for prioritization, and we will never take any new tasks from the global queue until all the entries in this queue is empty, this ensures we can consistently complete every received tasks till it finishes.
rng: Rc<RefCell<ChaCha8Rng>>this provides consistent and repeatable random number generation this is exposed by the executor to it’s callers via scopes or direct use that lets you borrow a random number that always produces the same sets of values when given the same seed for repeatability.
sleepers: Sleepers<Sleepable>sleepers contain a means to register tasks currently being executed as sleeping, forcing the executing to either in a single threaded environment like WebAssembly spin endlessly until one of them is ready for progress once the sleeper indicate the task is ready for waking up.
Each executor may treat these differently in that they may process one task at a time until completion or concurrently process multiple tasks at a time with allotted time slot between them.
idler: Rc<RefCell<IdleMan>>sleepy provides a managed indicator of how many times we’ve been idle and recommends how much sleep should the executor take next.
depends_true_violations: Rc<RefCell<HashMap<Entry, u32>>>Track consecutive State::Depends(true) violations per entry.
After DEPENDS_TRUE_PANIC_THRESHOLD consecutive violations, the task panics.
root: CtxThe engine-owned root Ctx. Forked once per ExecutionIterator when a
task enters the engine; each fork is stored in task_ctx and shared (by
value clone) with the task on every poll.
task_ctx: Rc<RefCell<HashMap<Entry, Ctx>>>Per-entry Ctx fork: one per ExecutionIterator, keyed by its Entry.
Implementations§
Source§impl ExecutorState
impl ExecutorState
pub fn new( state_owner: String, global_tasks: SharedTaskQueue, wakeup_priority: PriorityOrder, rng: ChaCha8Rng, idler: IdleMan, ) -> Self
Source§impl ExecutorState
impl ExecutorState
Sourcepub fn get_rng(&self) -> Rc<RefCell<ChaCha8Rng>>
pub fn get_rng(&self) -> Rc<RefCell<ChaCha8Rng>>
Returns a borrowed immutable
Sourcepub fn get_task_dependents(&self, target: Entry) -> Vec<Entry>
pub fn get_task_dependents(&self, target: Entry) -> Vec<Entry>
Returns the list of other task dependent on this giving tasks and their dependents in order.
Sourcepub fn is_packed(&self, target: &Entry) -> bool
pub fn is_packed(&self, target: &Entry) -> bool
Returns true/false indicative if the provided task entry is considered packed.
Sourcepub fn wake_up(&self, target: Entry)
pub fn wake_up(&self, target: Entry)
wake_up adds the entry into the list of wakers
that should be woken up by the executor.
§Design Note (Feature 01: Sleeper Lifecycle Safety)
Includes defensive check to skip stale entries that no longer exist in
local_tasks. This prevents panics when a sleeper matures after its
associated task was combined/removed. See remove_sleepers_for_entry
for the full stale sleeper lifecycle explanation.
Sourcepub fn wakeup_ready_sleepers(&self)
pub fn wakeup_ready_sleepers(&self)
next_wakeup checks all registered sleepers to see if they
matured to the age of being woken up and placed back into
the processing queue.
pub fn number_of_sleepers(&self) -> usize
Sourcepub fn number_of_depends_true_violations(&self) -> usize
pub fn number_of_depends_true_violations(&self) -> usize
How many tasks currently have a streak of Depends on an already-ready
signal (see DEPENDS_TRUE_PANIC_THRESHOLD).
pub fn has_sleeping_tasks(&self) -> bool
Sourcepub fn time_until_next_wakeup(&self) -> Option<Duration>
pub fn time_until_next_wakeup(&self) -> Option<Duration>
Returns the duration until the next sleeper wakes up.
Used for sleeper-aware yielding - when no work is available, threads can sleep until the earliest sleeper deadline instead of using a fixed yield duration.
Returns None if no sleepers are registered. Returns Duration::ZERO if a sleeper is already past its deadline.
Sourcepub fn has_local_tasks(&self) -> bool
pub fn has_local_tasks(&self) -> bool
Returns True/False indicative if the executor has any local task still processing
Sourcepub fn has_active_tasks(&self) -> bool
pub fn has_active_tasks(&self) -> bool
Returns if there is any local task is active which means:
- Not sleeping
- In local task queue
Sourcepub fn has_inflight_task(&self) -> bool
pub fn has_inflight_task(&self) -> bool
Returns true/false if processing queue has task.
pub fn has_incoming_global_tasks(&self) -> bool
Sourcepub fn has_runnable_work(&self) -> bool
pub fn has_runnable_work(&self) -> bool
Whether this worker has real work to run on the current tick — an active task, an in-flight (processing-queue) task, or a global task waiting to be acquired.
When this is false the worker is kept alive solely by sleeping tasks and
is busy-spinning schedule_and_do_work until one of them becomes ready.
Sourcepub fn total_inprocess_tasks(&self) -> usize
pub fn total_inprocess_tasks(&self) -> usize
Returns totla
pub fn total_sleeping_tasks(&self) -> usize
Sourcepub fn total_tasks(&self) -> usize
pub fn total_tasks(&self) -> usize
Returns the total in queue regardless of sleeping or in-progress.
Sourcepub fn total_active_tasks(&self) -> usize
pub fn total_active_tasks(&self) -> usize
Returns the total remaining tasks that are active and not sleeping.
§Design Note
Uses saturating_sub to prevent underflow panic when sleepers reference
entries that have been removed (stale sleeper entries). This can happen when
a task sleeps, then spawns a child via SpawnFinished and gets combined into
a new task. The old entry is removed but its sleeper remains until cleaned up.
Sourcepub fn remove_sleepers_for_entry(&self, entry: &Entry)
pub fn remove_sleepers_for_entry(&self, entry: &Entry)
Removes any sleepers referencing the given entry.
§Design Note (Feature 01: Sleeper Lifecycle Safety)
Must be called when a task is removed to prevent stale sleeper entries. Stale entries occur when:
- Task A sleeps (registered in sleepers with entry E)
- Task A spawns child via SpawnFinished, gets combined into new task
- Old entry E is removed from local_tasks via take()
- Sleeper for E remains in sleepers -> stale entry
- When sleeper matures, wake_up tries to process non-existent entry -> panic
Calling this before take() prevents the stale entry issue. This was the root cause of the “entry must always have a task attached” panic.
Sourcepub fn schedule_next(&self) -> ScheduleOutcome
pub fn schedule_next(&self) -> ScheduleOutcome
schedule_next will attempt to pull a new task from the
global queue if no task is pending on the local queue
and if so returns true to indicate success else a false
to indicate no task was taking from the global queue
as the local queue had a task or no task was found.
pub fn request_global_task(&self) -> ProgressIndicator
pub fn schedule_and_do_work( &self, engine: BoxedExecutionEngine, ) -> ProgressIndicator
pub fn check_processing_queue(&self) -> Option<ProgressIndicator>
Sourcepub fn do_work(&self, engine: BoxedExecutionEngine) -> ProgressIndicator
pub fn do_work(&self, engine: BoxedExecutionEngine) -> ProgressIndicator
do_work attempts to call the current iterator to progress
executing the next operation internally till it’s ready for
work to begin.
§Panics
- Will panic if when popping from the processing queue, we fail to get the top entry
as at this point work should be in queue before
do_workis called.
Source§impl ExecutorState
impl ExecutorState
Sourcepub fn sequenced(
&self,
task: BoxedExecutionIterator,
parent: Entry,
) -> AnyResult<SpawnInfo, ExecutorError>
pub fn sequenced( &self, task: BoxedExecutionIterator, parent: Entry, ) -> AnyResult<SpawnInfo, ExecutorError>
sequenced defers from the ExecutorState::lift method
where instead we create a co-linked sequential loop between
the parent (via its entry id) creating this task and itself.
Where the parent will be pulled and used to create a DualSequeunceChildAndParentLinkedTask
executor which will trigger the child then the parent in sequence.
This means the currently processed task that might have
called ExecutorState::lift since the executor will only
ever execute a singular task each time (even if it concurrently)
processes them (based on their operating semantics).
But even if its from outside a task, understand the new task will take priority till it’s done.
Returns:
The result of the spawn operation with the SpawnInfo containing the
key details of parent and child and what type of operation was performed.
Sourcepub fn lift(
&self,
task: BoxedExecutionIterator,
parent: Option<Entry>,
) -> AnyResult<SpawnInfo, ExecutorError>
pub fn lift( &self, task: BoxedExecutionIterator, parent: Option<Entry>, ) -> AnyResult<SpawnInfo, ExecutorError>
lift defers from the ExecutorState::schedule method
where instead when adding the task into the local queue
but also lifts the task as the highest priority task
to be processed.
This if the parent is supplied should create a FinishChildBeforeParentTask
executor which will process the child until its done then move to
the parent in sequence.
This means the currently processed task that might have
called ExecutorState::lift since the executor will only
ever execute a singular task each time (even if it concurrently)
processes them (based on their operating semantics).
But even if its from outside a task, understand the new task will take priority till it’s done.
Sourcepub fn schedule(
&self,
task: BoxedExecutionIterator,
) -> AnyResult<SpawnInfo, ExecutorError>
pub fn schedule( &self, task: BoxedExecutionIterator, ) -> AnyResult<SpawnInfo, ExecutorError>
schedule appends the provided task into the end of the local task queue which then gets processed accordingly when its turns comes up once all previous tasks and it’s sub-tasks have been resolved.
Scheduled tasks do not create relationships at all any task can schedule new task to the executor and it never affects its execution or priority.
Source§impl ExecutorState
impl ExecutorState
Sourcepub fn broadcast(&self, task: GlobalTask) -> AnyResult<SpawnInfo, ExecutorError>
pub fn broadcast(&self, task: GlobalTask) -> AnyResult<SpawnInfo, ExecutorError>
Delivers a task (Iterator) type to the global execution queue and in such a case you do not have an handle to the task as we no more have control as to where it gets allocated.