1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
//! Support code for asynchronous tasks

use crate::cell::cell::ActorCell;
use crate::{Core, Deferrer, Stakker};
use std::pin::Pin;
use std::rc::Rc;

/// An asynchronous task reference
///
/// **Stakker** provides only a very thin wrapping to enable tasks.
/// Full support is provided in a separate crate
/// (**stakker_async_await**).  A task is represented as an
/// implementation of the [`TaskTrait`](trait.TaskTrait.html) trait
/// which is called when the task needs to be resumed.  It is expected
/// that even if the task cannot advance, it will accept a spurious
/// call gracefully.  Spurious calls may be generated for example if
/// the task is woken from another thread.
///
/// It is guaranteed that the task is resumed directly from the main
/// loop, not within any actor call nor any deeper down the stack.
/// This may be important in order to avoid `RefCell` panics, or to
/// allow the use of `UnsafeCell`.  Internally this is statically
/// guaranteed by using an `ActorCell` to wrap the closure.  This code
/// also handles the pinning required for running futures.
///
/// Deeper down the stack whilst the task is running, it is possible
/// to get a reference to the running task by using
/// [`Task::from_context`](#method.from_context).  This may be
/// used to implement efficient same-thread wakers.
#[derive(Clone)]
pub struct Task {
    rc: Rc<ActorCell<Pin<Box<dyn TaskTrait>>>>,
}

impl Task {
    /// Create a new task from an implementation of the
    /// [`TaskTrait`](trait.TaskTrait.html), and return a reference to
    /// it.  (`Task` is like an `Rc` reference to the task.)  The
    /// `resume` method will be called each time the task needs to be
    /// resumed.
    pub fn new(core: &mut Core, inner: impl TaskTrait + 'static) -> Self {
        Self {
            rc: Rc::new(core.actor_maker.cell(Box::pin(inner))),
        }
    }

    /// Resume execution of the task.  The task will advance its state
    /// as much as possible, and then return.  It's intended that
    /// whatever poll-handler caused the task to yield will have saved
    /// a reference to this task to keep it alive until it can be
    /// woken again.
    pub fn resume(&mut self, s: &mut Stakker) {
        s.deferrer.task_replace(Some(self.clone()));
        s.actor_owner.rw(&self.rc).as_mut().resume(&mut s.core);
        s.deferrer.task_replace(None);
    }

    /// Obtain the [`Task`](struct.Task.html) reference of the
    /// currently-running task from the provided
    /// [`Deferrer`](../struct.Deferrer.html), if a task is currently
    /// running.
    pub fn from_context(deferrer: &Deferrer) -> Option<Self> {
        let v0 = deferrer.task_replace(None);
        let v1 = v0.clone();
        deferrer.task_replace(v0);
        v1
    }
}

/// Trait that tasks must implement
pub trait TaskTrait {
    /// Resume execution of the task.  This must handle spurious calls
    /// gracefully, even if nothing has changed.
    fn resume(self: Pin<&mut Self>, core: &mut Core);
}

// Not useful yet, as there's no way to pass an owner-borrow through
// `Future::poll`
//
// /// Statically-checked cell type for use within task-related code
// ///
// /// This requires access to `&mut Stakker` in order to borrow the cell
// /// contents, so cannot be used within actor code or anywhere else
// /// where a Stakker reference is not available.
// pub struct TaskCell<T: 'static>(ActorCell<T>);
//
// impl<T: 'static> TaskCell<T> {
//     /// Create a new TaskCell
//     pub fn new(core: &mut Core, value: T) -> Self {
//         Self(core.actor_maker.cell(value))
//     }
//
//     // Get a mutable reference to the cell contents
//     pub fn rw<'a>(&'a self, s: &'a mut Stakker) -> &'a mut T {
//         s.actor_owner.rw(&self.0)
//     }
// }