pub struct StaticScheduler(/* private fields */);
Expand description

A statically-initialized scheduler implementation.

This type stores the core of the scheduler behind an &'static reference, which is passed into each task spawned on the scheduler. This means that, in order to spawn tasks, the StaticScheduler must be stored in a static variable.

The use of a &'static reference allows StaticSchedulers to be used without liballoc. In addition, spawning and deallocating tasks is slightly cheaper than when using the reference-counted Scheduler type, because an atomic reference count increment/decrement is not required.

Usage

A StaticScheduler may be created one of two ways, depending on how the stub task used by the MPSC queue algorithm is created: either with a statically-allocated stub task by using the StaticScheduler::new_with_static_stub function, or with a heap-allocated stub task using StaticScheduler::new or StaticScheduler::default (which require the “alloc” feature).

The new_with_static_stub function is a const fn, which allows a StaticScheduler to be constructed directly in a static initializer. However, it requires the TaskStub to be constructed manually by the caller and passed in to initialize the scheduler. Furthermore, this function is unsafe to call, as it requires that the provided TaskStub not be used by any other StaticScheduler instance, which the function cannot ensure.

For example:

use maitake::scheduler::{self, StaticScheduler};

static SCHEDULER: StaticScheduler = {
    // create a new static stub task *inside* the initializer for the
    // `StaticScheduler`. since the stub task static cannot be referenced
    // outside of this block, we can ensure that it is not used by any
    // other calls to `StaticScheduler::new_with_static_stub`.
    static STUB_TASK: scheduler::TaskStub = scheduler::TaskStub::new();

    // now, create the scheduler itself:
    unsafe {
        // safety: creating the stub task inside the block used as an
        // initializer expression for the scheduler static ensures that
        // the stub task is not used by any other scheduler instance.
        StaticScheduler::new_with_static_stub(&STUB_TASK)
    }
};

// now, we can use the scheduler to spawn tasks:
SCHEDULER.spawn(async { /* ... */ });

The scheduler::new_static! macro abstracts over the above code, allowing a static StaticScheduler to be initialized without requiring the caller to manually write unsafe code:

use maitake::scheduler::{self, StaticScheduler};

// this macro expands to code identical to the previous example.
static SCHEDULER: StaticScheduler = scheduler::new_static!();

// now, we can use the scheduler to spawn tasks:
SCHEDULER.spawn(async { /* ... */ });

Alternatively, the new and default constructors can be used to create a new StaticScheduler with a heap-allocated stub task. This does not require the user to manually create a stub task and ensure that it is not used by any other StaticScheduler instances. However, these constructors are not const fns and require the “alloc” feature to be enabled.

Because StaticScheduler::new and StaticScheduler::default are not const fns, but the scheduler must still be stored in a static to be used, some form of lazy initialization of the StaticScheduler is necessary:

use maitake::scheduler::StaticScheduler;
use mycelium_util::sync::Lazy;

static SCHEDULER: Lazy<StaticScheduler> = Lazy::new(StaticScheduler::new);

// now, we can use the scheduler to spawn tasks:
SCHEDULER.spawn(async { /* ... */ });

Although the scheduler itself is no longer constructed in a const fn static initializer in this case, storing it in a static rather than an Arc still provides a minor performance benefit, as it avoids atomic reference counting when spawning tasks.

Implementations§

source§

impl StaticScheduler

source

pub fn try_steal( &self ) -> Result<Stealer<'_, &'static StaticScheduler>, TryStealError>

Attempt to steal tasks from this scheduler’s run queue.

Returns
  • Ok(Stealer`)) if tasks can be stolen from this scheduler’s queue.
  • Err(TryStealError::Empty) if there were no tasks in this scheduler’s run queue.
  • Err(TryStealError::Busy) if another worker was already stealing from this scheduler’s run queue.
source§

impl StaticScheduler

source

pub const DEFAULT_TICK_SIZE: usize = 256usize

How many tasks are polled per call to StaticScheduler::tick.

Chosen by fair dice roll, guaranteed to be random.

source

pub const unsafe fn new_with_static_stub(stub: &'static TaskStub) -> Self

Create a StaticScheduler with a static “stub” task entity

This is used for creating a StaticScheduler as a static variable.

Safety

The “stub” provided must ONLY EVER be used for a single StaticScheduler. Re-using the stub for multiple schedulers may lead to undefined behavior.

For a safe alternative, consider using the new_static! macro to initialize a StaticScheduler in a static variable.

source

pub fn spawn_allocated<F, STO>( &'static self, task: STO::StoredTask ) -> JoinHandle<F::Output>
where F: Future + Send + 'static, F::Output: Send + 'static, STO: Storage<&'static Self, F>,

Spawn a pre-allocated task

This method is used to spawn a task that requires some bespoke procedure of allocation, typically of a custom Storage implementor. See the documentation for the Storage trait for more details on using custom task storage.

This method returns a JoinHandle that can be used to await the task’s output. Dropping the JoinHandle detaches the spawned task, allowing it to run in the background without awaiting its output.

When tasks are spawned on a scheduler, the scheduler must be ticked in order to drive those tasks to completion. See the module-level documentation for more information on implementing a system’s run loop.

source

pub fn build_task<'a>(&'static self) -> Builder<'a, &'static Self>

Returns a new task Builder for configuring tasks prior to spawning them on this scheduler.

source

pub fn current_task(&'static self) -> Option<TaskRef>

Returns a TaskRef referencing the task currently being polled by this scheduler, if a task is currently being polled.

Returns
  • Some(TaskRef) referencing the currently-polling task, if a task is currently being polled (i.e., the scheduler is ticking and the queue of scheduled tasks is non-empty).

  • None if the scheduler is not currently being polled (i.e., the scheduler is not ticking or its run queue is empty and all polls have completed).

source

pub fn tick(&'static self) -> Tick

Tick this scheduler, polling up to Self::DEFAULT_TICK_SIZE tasks from the scheduler’s run queue.

Only a single CPU core/thread may tick a given scheduler at a time. If another call to tick is in progress on a different core, this method will immediately return.

See the module-level documentation for more information on using this function to implement a system’s run loop.

Returns

A Tick struct with data describing what occurred during the scheduler tick.

source§

impl StaticScheduler

source

pub fn new() -> Self

Available on crate feature alloc only.

Returns a new StaticScheduler with a heap-allocated stub task.

Unlike StaticScheduler::new_with_static_stub, this is not a const fn, as it performs a heap allocation for the stub task. However, the returned StaticScheduler must still be stored in a static variable in order to be used.

This method is generally used with lazy initialization of the scheduler static.

source

pub fn spawn<F>(&'static self, future: F) -> JoinHandle<F::Output>
where F: Future + Send + 'static, F::Output: Send + 'static,

Available on crate feature alloc only.

Spawn a task.

This method returns a JoinHandle that can be used to await the task’s output. Dropping the JoinHandle detaches the spawned task, allowing it to run in the background without awaiting its output.

When tasks are spawned on a scheduler, the scheduler must be ticked in order to drive those tasks to completion. See the module-level documentation for more information on implementing a system’s run loop.

Examples

Spawning a task and awaiting its output:

use maitake::scheduler::{self, StaticScheduler};
static SCHEDULER: StaticScheduler = scheduler::new_static!();

// spawn a new task, returning a `JoinHandle`.
let task = SCHEDULER.spawn(async move {
    // ... do stuff ...
   42
});

// spawn another task that awaits the output of the first task.
SCHEDULER.spawn(async move {
    // await the `JoinHandle` future, which completes when the task
    // finishes, and unwrap its output.
    let output = task.await.expect("task is not cancelled");
    assert_eq!(output, 42);
});

// run the scheduler, driving the spawned tasks to completion.
while SCHEDULER.tick().has_remaining {}

Spawning a task to run in the background, without awaiting its output:

use maitake::scheduler::{self, StaticScheduler};
static SCHEDULER: StaticScheduler = scheduler::new_static!();

// dropping the `JoinHandle` allows the task to run in the background
// without awaiting its output.
SCHEDULER.spawn(async move {
    // ... do stuff ...
});

// run the scheduler, driving the spawned tasks to completion.
while SCHEDULER.tick().has_remaining {}

Trait Implementations§

source§

impl Debug for StaticScheduler

source§

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

Formats the value using the given formatter. Read more
source§

impl Default for StaticScheduler

source§

fn default() -> StaticScheduler

Returns the “default value” for a type. Read more
source§

impl Schedule for &'static StaticScheduler

source§

fn schedule(&self, task: TaskRef)

Schedule a task on this scheduler. Read more
source§

fn current_task(&self) -> Option<TaskRef>

Returns a TaskRef referencing the task currently being polled by this scheduler, if a task is currently being polled.
source§

fn build_task<'a>(&self) -> Builder<'a, Self>

Returns a new task Builder for configuring tasks prior to spawning them on this scheduler.

Auto Trait Implementations§

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.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
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>,

§

type Error = Infallible

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

§

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.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more