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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
//! Contains helper structures to deal with time-related functionality.

use std::time::Duration;

use crate::ap::messages::{RequestMessage, ShutdownMessage};
use crate::ap::{AbstractProcess, DeferredRequestHandler, ProcessRef, RequestHandler};
use crate::host;
use crate::serializer::CanSerialize;

/// A reference to a timer created from send_after.
#[derive(Clone, Copy)]
pub struct TimerRef(u64);

impl TimerRef {
    pub(crate) fn new(timer_id: u64) -> Self {
        TimerRef(timer_id)
    }

    /// Cancel the timer, blocking until the timer is canceled.
    pub fn cancel(self) -> bool {
        unsafe { host::api::timer::cancel_timer(self.0) == 1 }
    }
}

/// Modifies `T` so that all functions on it will return a timeout.
///
/// It's used to time out calls such as [`ProcessRef::shutdown`],
/// [`ProcessRef::request`], etc.
pub struct WithTimeout<T> {
    timeout: Duration,
    item: T,
}

impl<T: AbstractProcess> WithTimeout<ProcessRef<T>> {
    pub fn from(timeout: Duration, item: ProcessRef<T>) -> Self {
        Self { timeout, item }
    }

    /// Shuts the [`AbstractProcess`] down.
    ///
    /// The function will only wait for the duration of the specified timeout on
    /// the process to shut down, before returning `Err(Timeout)`.
    #[track_caller]
    pub fn shutdown(&self) -> Result<(), Timeout>
    where
        // The serializer needs to be able to serialize values of `ShutdownMessage` & `()` for the
        // return value.
        T::Serializer: CanSerialize<ShutdownMessage<T::Serializer>>,
        T::Serializer: CanSerialize<()>,
    {
        self.item.shutdown_timeout(Some(self.timeout))
    }

    /// Make a request to the process.
    ///
    /// The function will only wait for the duration of the specified timeout on
    /// the response, before returning `Err(Timeout)`.
    #[track_caller]
    pub fn request<R: 'static>(&self, request: R) -> Result<T::Response, Timeout>
    where
        T: RequestHandler<R>,
        T::Serializer: CanSerialize<R>,
        T::Serializer: CanSerialize<T::Response>,
        T::Serializer: CanSerialize<RequestMessage<R, T::Response, T::Serializer>>,
    {
        self.item.request_timeout(request, Some(self.timeout))
    }

    /// Make a deferred request to the process.
    ///
    /// The function will only wait for the duration of the specified timeout on
    /// the response, before returning `Err(Timeout)`.
    #[track_caller]
    pub fn deferred_request<R: 'static>(&self, request: R) -> Result<T::Response, Timeout>
    where
        T: DeferredRequestHandler<R>,
        T::Serializer: CanSerialize<R>,
        T::Serializer: CanSerialize<T::Response>,
        T::Serializer: CanSerialize<RequestMessage<R, T::Response, T::Serializer>>,
    {
        self.item
            .deferred_request_timeout(request, Some(self.timeout))
    }
}

/// Error result for [`ProcessRef::shutdown`] & [`ProcessRef::request`].
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Timeout;

/// Modifies `T` so that all functions on it will be performed with a delay.
///
/// It's used to delay calls such as [`ProcessRef::send`].
pub struct WithDelay<T> {
    duration: Duration,
    item: T,
}

impl<T: AbstractProcess> WithDelay<ProcessRef<T>> {
    pub fn from(duration: Duration, item: ProcessRef<T>) -> Self {
        Self { duration, item }
    }

    /// Send message to the process after the specified duration has passed.
    ///
    /// This is a non-blocking function, meaning that `send` is going to be
    /// performed in the background while the execution continues. The call will
    /// return a reference to the timer allowing you to cancel the operation.
    #[track_caller]
    pub fn send<M: 'static>(&self, message: M) -> TimerRef
    where
        T::Serializer: CanSerialize<M>,
    {
        self.item.delayed_send(message, self.duration)
    }
}