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
use crate::rc::FwdRc; use crate::{Actor, Cx}; use static_assertions::assert_eq_size; /// Forwarder for messages of type `M` /// /// 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()) } }