pub trait PersistentTask: 'static + Send {
    type State<'state>;
    type Input: 'static + Send;
    type Output: 'static + Send;
    type Affinity: Affinity;

    const CHANNEL_CAPACITY: usize = 0usize;

    // Required methods
    fn init<'frame, 'life0, 'async_trait>(
        &'life0 mut self,
        frame: AsyncGcFrame<'frame>
    ) -> Pin<Box<dyn Future<Output = JlrsResult<Self::State<'frame>>> + 'async_trait>>
       where Self: 'async_trait,
             'frame: 'async_trait,
             'life0: 'async_trait;
    fn run<'frame, 'state, 'life0, 'life1, 'async_trait>(
        &'life0 mut self,
        frame: AsyncGcFrame<'frame>,
        state: &'life1 mut Self::State<'state>,
        input: Self::Input
    ) -> Pin<Box<dyn Future<Output = JlrsResult<Self::Output>> + 'async_trait>>
       where Self: 'async_trait,
             'frame: 'async_trait,
             'state: 'async_trait + 'frame,
             'life0: 'async_trait,
             'life1: 'async_trait;

    // Provided methods
    fn register<'frame, 'async_trait>(
        _frame: AsyncGcFrame<'frame>
    ) -> Pin<Box<dyn Future<Output = JlrsResult<()>> + 'async_trait>>
       where 'frame: 'async_trait { ... }
    fn exit<'frame, 'life0, 'life1, 'async_trait>(
        &'life0 mut self,
        _frame: AsyncGcFrame<'frame>,
        _state: &'life1 mut Self::State<'frame>
    ) -> Pin<Box<dyn Future<Output = ()> + 'async_trait>>
       where Self: 'async_trait,
             'frame: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait { ... }
}
Expand description

A task that can be called multiple times.

In order to schedule the task you must use AsyncJulia::persistent.

Example:

use jlrs::prelude::*;

struct AccumulatorTask {
    n_values: usize,
}

struct AccumulatorTaskState<'state> {
    array: TypedArray<'state, 'static, usize>,
    offset: usize,
}

// Only the runtime thread can call the Julia C API, so the async trait
// methods of `PersistentTask` must not return a future that implements
// `Send` or `Sync`.
#[async_trait(?Send)]
impl PersistentTask for AccumulatorTask {
    // The type of the result of the task if it succeeds.
    type Output = usize;

    // The type of the task's internal state.
    type State<'state> = AccumulatorTaskState<'state>;

    // The type of the additional data that the task must be called with.
    type Input = usize;

    // The thread-affinity of this task. This lets you control whether this kind of task is
    // always dispatched to the main thread, to a worker thread if they're used, or to either.
    // This task can be dispatched to either the main thread or a worker thread.
    type Affinity = DispatchAny;

    // This method is called before the handle is returned. Note that the
    // lifetime of the frame is `'static`: the frame is not dropped until
    // the task has completed, so the task's internal state can contain
    // Julia data rooted in this frame.
    async fn init<'frame>(
        &mut self,
        mut frame: AsyncGcFrame<'frame>,
    ) -> JlrsResult<Self::State<'frame>> {
        // A `Vec` can be moved from Rust to Julia if the element type
        // implements `IntoJulia`.
        let data = vec![0usize; self.n_values];
        let array =
            TypedArray::from_vec(&mut frame, data, self.n_values)?.into_jlrs_result()?;

        Ok(AccumulatorTaskState { array, offset: 0 })
    }

    // Whenever the task is called through its handle this method
    // is called. Unlike `init`, the frame that this method can use
    // is dropped after `run` returns.
    async fn run<'frame, 'state: 'frame>(
        &mut self,
        mut frame: AsyncGcFrame<'frame>,
        state: &mut Self::State<'state>,
        input: Self::Input,
    ) -> JlrsResult<Self::Output> {
        {
            // Array data can be directly accessed from Rust.
            // The data is tracked first to ensure it's not
            // already borrowed from Rust.
            unsafe {
                let mut tracked = state.array.track_exclusive()?;
                let mut data = tracked.bits_data_mut()?;
                data[state.offset] = input;
            };

            state.offset += 1;
            if (state.offset == self.n_values) {
                state.offset = 0;
            }
        }

        // Return the sum of the contents of `state.array`.
        unsafe {
            Module::base(&frame)
                .function(&mut frame, "sum")?
                .call1(&mut frame, state.array.as_value())
                .into_jlrs_result()?
                .unbox::<usize>()
        }
    }
}

Required Associated Types§

source

type State<'state>

The type of the result which is returned if init completes successfully.

This data is provided to every call of run.

source

type Input: 'static + Send

The type of the data that must be provided when calling this persistent through its handle.

source

type Output: 'static + Send

The type of the result which is returned if run completes successfully.

source

type Affinity: Affinity

The thread-affinity of this task. Can be set to DispatchAny, DispatchMain, or DispatchWorker

Provided Associated Constants§

Required Methods§

source

fn init<'frame, 'life0, 'async_trait>( &'life0 mut self, frame: AsyncGcFrame<'frame> ) -> Pin<Box<dyn Future<Output = JlrsResult<Self::State<'frame>>> + 'async_trait>>
where Self: 'async_trait, 'frame: 'async_trait, 'life0: 'async_trait,

Initialize the task.

You can interact with Julia inside this method, the frame is not dropped until the task itself is dropped. This means that State can contain arbitrary Julia data rooted in this frame. This data is provided to every call to run.

source

fn run<'frame, 'state, 'life0, 'life1, 'async_trait>( &'life0 mut self, frame: AsyncGcFrame<'frame>, state: &'life1 mut Self::State<'state>, input: Self::Input ) -> Pin<Box<dyn Future<Output = JlrsResult<Self::Output>> + 'async_trait>>
where Self: 'async_trait, 'frame: 'async_trait, 'state: 'async_trait + 'frame, 'life0: 'async_trait, 'life1: 'async_trait,

Run the task.

This method takes an AsyncGcFrame, which lets you interact with Julia. It’s also provided with a mutable reference to its state and the input provided by the caller. While the state is mutable, it’s not possible to allocate a new Julia value in run and assign it to the state because the frame doesn’t live long enough.

See the trait docs for an example implementation.

Provided Methods§

source

fn register<'frame, 'async_trait>( _frame: AsyncGcFrame<'frame> ) -> Pin<Box<dyn Future<Output = JlrsResult<()>> + 'async_trait>>
where 'frame: 'async_trait,

Register this persistent task.

Note that this method is not called automatically, but only if AsyncJulia::register_persistentis used. This method can be implemented to take care of everything required to execute the task successfully, like loading packages.

source

fn exit<'frame, 'life0, 'life1, 'async_trait>( &'life0 mut self, _frame: AsyncGcFrame<'frame>, _state: &'life1 mut Self::State<'frame> ) -> Pin<Box<dyn Future<Output = ()> + 'async_trait>>
where Self: 'async_trait, 'frame: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Method that is called when all handles to the task have been dropped.

This method is called with the same frame as init.

Object Safety§

This trait is not object safe.

Implementors§