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
116
use crate::rc::FwdRc;
use crate::{Actor, Cx};
use static_assertions::assert_eq_size;

/// Forwarder for messages of type `M`
///
/// Typically this would be created using one of the `fwd_*!` macros.
/// This may be called many times to forward different messages to the
/// same end-point.  For situations where a callback can be used at
/// most one time, prefer [`Ret`], because it is less restrictive on
/// the types of data that may be captured by the closure.
///
/// This is a fat pointer to a ref-counted dynamic `Fn`, so it
/// consumes two `usize` locally, and the size of the `Fn` closure
/// plus the ref-count (a `usize`) on the heap.  It may be cloned
/// cheaply if another identical [`Fwd`] is required.
///
/// For zero arguments, use `Fwd<()>`.  For one argument, use
/// `Fwd<type>`, where `type` is the type of the argument.  For two or
/// more use a tuple: `Fwd<(type1, type2...)>`.  Call the [`Fwd::fwd`]
/// method to send a message or use the [`fwd!`] macro.  Sending a
/// message typically results in the asynchronous execution of an
/// actor call, but may have other effects depending on the type of
/// forwarder.
///
/// [`Fwd::fwd`]: struct.Fwd.html#method.fwd
/// [`Fwd`]: struct.Fwd.html
/// [`Ret`]: struct.Ret.html
/// [`fwd!`]: macro.fwd.html
pub struct Fwd<M: 'static>(FwdRc<M>);

assert_eq_size!(Fwd<()>, [usize; 2]);

// Design note: Adding an `is_valid` method was considered, which
// could allow stale `Fwd` instances to be dropped if the target goes
// away.  However this adds overhead to all `Fwd` types, and in the
// cases where this might have been useful, it would require scanning
// potentially long lists at intervals, i.e. O(N).  It is better to
// make sure that proper cleanup occurs instead.  For actors, that
// means when the actor dies, cleaning up the whole group of related
// actors.

impl<M> Fwd<M> {
    /// Forward a message through the [`Fwd`] instance
    ///
    /// [`Fwd`]: struct.Fwd.html
    pub fn fwd(&self, msg: M) {
        self.0.inner()(msg);
    }

    /// Create a [`Fwd`] instance that performs an arbitrary action
    /// with the message on being called.  The call is made
    /// synchronously at the point that the message is forwarded.
    ///
    /// [`Fwd`]: struct.Fwd.html
    #[inline]
    pub fn new(f: impl Fn(M) + 'static) -> Self {
        Self(FwdRc::new(f))
    }

    /// Create a [`Fwd`] instance that queues calls to an actor.  The
    /// `Fn` provided must be `Copy` because on each invocation a new
    /// copy is made and put on the queue.  Use [`Ret`] instead if
    /// this is too restrictive.
    ///
    /// [`Fwd`]: struct.Fwd.html
    /// [`Ret`]: struct.Ret.html
    #[allow(clippy::wrong_self_convention)]
    #[inline]
    pub fn to_actor<A: 'static>(
        actor: Actor<A>,
        f: impl Fn(&mut A, &mut Cx<'_, A>, M) + Copy + 'static,
    ) -> Self {
        // This relies on closures being inlined.  We expect this to
        // result in just two fully-inlined closures: one boxed and
        // returned as the Fwd value, the other appended to the defer
        // queue when that is called.
        Self::new(move |m| {
            let actor2 = actor.clone();
            actor.defer(move |s| actor2.apply(s, move |a, cx| f(a, cx, m)));
        })
    }

    /// Create a [`Fwd`] instance that queues calls to an actor whilst
    /// in the **Prep** phase.  Once the actor is **Ready**, any
    /// queued prep calls are dropped.
    ///
    /// [`Fwd`]: struct.Fwd.html
    #[allow(clippy::wrong_self_convention)]
    #[inline]
    pub fn to_actor_prep<A: 'static>(
        actor: Actor<A>,
        f: impl Fn(&mut Cx<'_, A>, M) -> Option<A> + Copy + 'static,
    ) -> Self {
        Self::new(move |m| {
            let actor2 = actor.clone();
            actor.defer(move |s| actor2.apply_prep(s, move |cx| f(cx, m)));
        })
    }

    /// Create a [`Fwd`] instance that panics with the given message
    /// when called
    ///
    /// [`Fwd`]: struct.Fwd.html
    #[inline]
    pub fn panic(msg: impl Into<String>) -> Self {
        let msg: String = msg.into();
        Self::new(move |_| panic!("{}", msg))
    }
}

impl<M> Clone for Fwd<M> {
    fn clone(&self) -> Self {
        Self(self.0.clone())
    }
}