exec_rs/
lib.rs

1use std::sync::Arc;
2
3#[cfg(feature = "sync")]
4pub mod sync;
5
6/// Function that implements using a reference to a [`Mode`](struct.Mode.html) to invoke a task.
7///
8/// Uses the iterator returned by calling [`ModeCombiner::iter`](trait.ModeCombiner.html#method.iter)
9/// on the [`ModeCombiner`](trait.ModeCombiner.html) created by the last [`Mode::with`](struct.Mode.html#method.with)
10/// invocation to unwrap the [`ModeCombiner`](trait.ModeCombiner.html) inside out and wrap the submitted
11/// task using [`ModeWrapper::wrap`](trait.ModeWrapper.html#method.wrap) at each step.
12///
13/// Then calls the produced task or simply calls the submitted task if no [`ModeWrapper`](trait.ModeWrapper.html)
14/// has been supplied to the [`Mode`](struct.Mode.html).
15///
16/// [`Mode::with`](struct.Mode.html#method.with) consumes the supplied [`ModeWrapper`](trait.ModeWrapper.html)
17/// to produce a [`ModeCombiner`](trait.ModeCombiner.html). The `ModeCombiner` is set on the `Mode` and,
18/// if there already is a `ModeCombiner` present, combined with the existing `ModeCombiner` by calling
19/// [`ModeCombiner::combine`](trait.ModeCombiner.html#method.combine). By default this produces a
20/// [`DelegatingModeCombiner`](struct.DelegatingModeCombiner.html) that combines `ModeCombiners` by
21/// setting the current `ModeCombiner` as the outer `ModeCombiner` of the newly added `ModeCombiner`
22/// so that the iterator walks the `ModeCombiners` in the reverse order of which they were added, meaning
23/// the `ModeCombiner` that was added first ends up wrapping the task last, meaning its task will be the
24/// outermost task.
25pub fn invoke<'f, T: 'f, F: FnOnce() -> T + 'f>(mode: &Mode<'f, T>, task: F) -> T {
26    let mut task: Box<dyn FnOnce() -> T + 'f> = Box::new(task);
27    if let Some(ref mode_combiner) = mode.mode_combiner {
28        for mode_wrapper in mode_combiner.iter() {
29            task = mode_wrapper.wrapper_ref().wrap(task);
30        }
31    }
32
33    task()
34}
35
36/// Trait that may be implemented for types that manage executing a task that do not care about
37/// the return type of the task. Implementors may simply override [`pre_invoke`](trait.Invoker.html#method.pre_invoke)
38/// and [`post_invoke`](trait.Invoker.html#method.post_invoke) to run code before or / and after
39/// invoking a task or override [`do_invoke`](trait.Invoker.html#method.do_invoke) to control
40/// exactly how a task is invoked, by default this simply calls the task if no mode was supplied
41/// or calls [`crate::invoke`] if a mode was supplied.
42///
43/// Calling `pre_invoke` and `post_invoke` is managed by [`invoke_with_mode_optional`](trait.Invoker.html#method.invoke_with_mode_optional)
44/// which is the function used by both [`invoke_with_mode`](trait.Invoker.html#method.invoke_with_mode)
45/// and [`invoke`](trait.Invoker.html#method.invoke) and internally calls [`do_invoke`](trait.Invoker.html#method.do_invoke).
46/// So if implementors override [`invoke_with_mode_optional`](trait.Invoker.html#method.invoke_with_mode_optional)
47/// they either must manage calling `pre_invoke` and `post_invoke` or not use these functions.
48pub trait Invoker {
49    /// Called by [`invoke_with_mode_optional`](trait.Invoker.html#method.invoke_with_mode_optional) before
50    /// invoking each task. Can be used to run code before a task is invoked.
51    fn pre_invoke(&self) {}
52
53    /// Invoke a task with a [`Mode`](struct.Mode.html). The default implementation for [`do_invoke`](trait.Invoker.html#method.do_invoke)
54    /// delegates to [`crate::invoke`] if a mode was supplied. The `Mode` is applied to
55    /// the task invoked by this `Invoker`, meaning the task of modes will run inside the
56    /// [`do_invoke`](trait.Invoker.html#method.do_invoke) invocation so that logic run by
57    /// the invoker before the task runs executes before the modes and logic run by the
58    /// invoker after the task executes after the modes.
59    fn invoke_with_mode<'f, T: 'f, F: FnOnce() -> T + 'f>(
60        &'f self,
61        mode: &'f Mode<'f, T>,
62        task: F,
63    ) -> T {
64        self.invoke_with_mode_optional(Some(mode), task)
65    }
66
67    /// Invoke a task using this invoker without a [`Mode`](struct.Mode.html).
68    ///
69    /// Calls [`invoke_with_mode_optional`](trait.Invoker.html#method.invoke_with_mode_optional) with
70    /// `None` as [`Mode`](struct.Mode.html), which in turn calls [`pre_invoke`](trait.Invoker.html#method.pre_invoke)
71    /// and [`post_invoke`](trait.Invoker.html#method.post_invoke) and invokes the task by calling
72    /// [`do_invoke`](trait.Invoker.html#method.do_invoke).
73    fn invoke<'f, T: 'f, F: FnOnce() -> T + 'f>(&'f self, task: F) -> T {
74        self.invoke_with_mode_optional(None, task)
75    }
76
77    /// Invoke a task, optionally with a [`Mode`](struct.Mode.html) supplied.
78    ///
79    /// Note that this function is used by both [`invoke_with_mode`](trait.Invoker.html#method.invoke_with_mode)
80    /// and [`invoke`](trait.Invoker.html#method.invoke) and responsible for invoking
81    /// [`pre_invoke`](trait.Invoker.html#method.pre_invoke) and [`post_invoke`](trait.Invoker.html#method.post_invoke)
82    /// and delegating invocation of the task to [`do_invoke`](trait.Invoker.html#method.do_invoke).
83    fn invoke_with_mode_optional<'f, T: 'f, F: FnOnce() -> T + 'f>(
84        &'f self,
85        mode: Option<&'f Mode<'f, T>>,
86        task: F,
87    ) -> T {
88        self.pre_invoke();
89
90        if self.invoke_post_invoke_on_panic() {
91            let mut sentinel = Sentinel {
92                invoker_ref: self,
93                cancelled: false,
94            };
95
96            let result = self.do_invoke(mode, task);
97
98            sentinel.cancelled = true;
99            self.post_invoke();
100            result
101        } else {
102            let result = self.do_invoke(mode, task);
103
104            self.post_invoke();
105            result
106        }
107    }
108
109    /// Core function responsible for actually invoking the task. This allows implementors to easily override
110    /// how tasks are invoked without having to worry about doing any administrative tasks such as calling
111    /// [`pre_invoke`](trait.Invoker.html#method.pre_invoke) and [`post_invoke`](trait.Invoker.html#method.post_invoke)
112    /// as would be the case when overriding [`invoke_with_mode_optional`](trait.Invoker.html#method.invoke_with_mode_optional).
113    fn do_invoke<'f, T: 'f, F: FnOnce() -> T + 'f>(
114        &'f self,
115        mode: Option<&'f Mode<'f, T>>,
116        task: F,
117    ) -> T {
118        if let Some(mode) = mode {
119            invoke(mode, task)
120        } else {
121            task()
122        }
123    }
124
125    /// Called by [`invoke_with_mode_optional`](trait.Invoker.html#method.invoke_with_mode_optional) after
126    /// invoking each task. Can be used to run code after a task is invoked.
127    fn post_invoke(&self) {}
128
129    /// Return true if [`post_invoke`](trait.Invoker.html#method.post_invoke) should be called even if
130    /// the task panicked using a [`Sentinel`](struct.Sentinel.html), defaults to false.
131    fn invoke_post_invoke_on_panic(&self) -> bool {
132        false
133    }
134
135    /// Combines this `Invoker` with another `Invoker` by creating a [`CombinedInvoker`](struct.CombinedInvoker.html)
136    /// that invokes tasks by first calling this `Invoker` with a task that submits the supplied task to the
137    /// other `Invoker`, meaning the other `Invoker` will run inside this `Invoker` in such a way that the logic
138    /// of this `Invoker` that runs before the task executes before the logic of the other `Invoker` but the logic
139    /// of this `Invoker` that runs after the task executes after the logic of the other `Invoker`.
140    fn and_then<I: Invoker>(self, inner: I) -> CombinedInvoker<Self, I>
141    where
142        Self: Sized,
143    {
144        CombinedInvoker { outer: self, inner }
145    }
146}
147
148/// Type that manages calling [`Invoker::post_invoke`](trait.Invoker.html#method.post_invoke) when
149/// dropped and [`Invoker::invoke_post_invoke_on_panic`](trait.Invoker.html#method.invoke_post_invoke_on_panic)
150/// is true in case the task panicked.
151pub struct Sentinel<'a, I: Invoker + ?Sized> {
152    invoker_ref: &'a I,
153    cancelled: bool,
154}
155
156impl<I: Invoker + ?Sized> Drop for Sentinel<'_, I> {
157    fn drop(&mut self) {
158        if !self.cancelled {
159            self.invoker_ref.post_invoke();
160        }
161    }
162}
163
164/// Struct that provides an empty [`Invoker`](trait.Invoker.html) implementation.
165pub struct BaseInvoker {}
166
167impl Invoker for BaseInvoker {}
168
169/// Struct that enables combining two [`Invokers`](trait.Invoker.html) by calling the second invoker
170/// inside the first one.
171pub struct CombinedInvoker<O: Invoker, I: Invoker> {
172    outer: O,
173    inner: I,
174}
175
176impl<O: Invoker, I: Invoker> CombinedInvoker<O, I> {
177    pub fn combine(outer: O, inner: I) -> CombinedInvoker<O, I> {
178        CombinedInvoker { outer, inner }
179    }
180}
181
182impl<O: Invoker, I: Invoker> Invoker for CombinedInvoker<O, I> {
183    fn invoke_with_mode_optional<'f, T: 'f, F: FnOnce() -> T + 'f>(
184        &'f self,
185        mode: Option<&'f Mode<'f, T>>,
186        task: F,
187    ) -> T {
188        self.outer.invoke_with_mode_optional(mode, move || {
189            self.inner.invoke_with_mode_optional(mode, task)
190        })
191    }
192}
193
194/// Struct that manages collecting and combining [`ModeWrapper`](trait.ModeWrapper.html).
195/// This is the type supplied when submitting tasks in order to apply `ModeWrappers`.
196///
197/// Modes may be used by implementing the [`ModeWrapper`](trait.ModeWrapper.html) trait to be able to
198/// wrap tasks in an enclosing function. Unlike [`Invokers`](trait.Invoker.html), Modes and
199/// `ModeWrappers` are generic over the return type of the tasks they may wrap and thus can
200/// directly interact with the return value of the task. This also means that the lifetime of the
201/// Mode is tied to the lifetime of the type they are generic over.
202pub struct Mode<'m, T: 'm> {
203    mode_combiner: Option<Box<dyn ModeCombiner<'m, T> + 'm + Send + Sync>>,
204}
205
206impl<'m, T: 'm> Mode<'m, T> {
207    /// Construct a new empty mode with no [`ModeCombiner`](trait.ModeCombiner.html).
208    ///
209    /// This Mode may already be used to invoke tasks with, in which case the Mode will simply
210    /// be ignored.
211    pub fn new() -> Self {
212        Self {
213            mode_combiner: None,
214        }
215    }
216
217    /// Add a new [`ModeWrapper`](trait.ModeWrapper.html) implementation to this Mode.
218    ///
219    /// This calls the [`ModeWrapper::into_combiner`](trait.ModeWrapper.html#method.into_combiner) function
220    /// of the `ModeWrapper` and sets the resulting [`ModeCombiner`](trait.ModeCombiner.html) on
221    /// this Mode and, if there already is `ModeCombiner` present, combines the two by calling
222    /// [`ModeCombiner::combine`](trait.ModeCombiner.html#method.combine) on the existing `ModeCombiner`.
223    pub fn with<M: ModeWrapper<'m, T> + 'm + Send + Sync>(mut self, mode_wrapper: M) -> Self {
224        if let Some(curr_combiner) = self.mode_combiner {
225            self.mode_combiner = Some(curr_combiner.combine(mode_wrapper.into_combiner()));
226        } else {
227            self.mode_combiner = Some(mode_wrapper.into_combiner());
228        }
229
230        self
231    }
232}
233
234impl<'m, T: 'm> Default for Mode<'m, T> {
235    fn default() -> Self {
236        Mode::new()
237    }
238}
239
240/// Trait to implement in order to apply a mode to a task. ModeWrappers are supplied to a [`Mode`](struct.Mode.html)
241/// using [`Mode::with`](trait.Mode.html#method.with) where they might be combined with other ModeWrappers
242/// using the [`ModeCombiner`](trait.ModeCombiner.html) supplied by [`ModeWrapper::into_combiner`](trait.ModeWrapper.html#method.into_combiner).
243/// Unlike [`Invokers`](trait.Invoker.html), `Modes` and `ModeWrappers` are generic over the return type
244/// of the tasks they may wrap and thus can directly interact with the return value of the task.
245/// This also means that the lifetime of the Mode is tied to the lifetime of the type they are generic over.
246pub trait ModeWrapper<'m, T: 'm> {
247    /// Applies this ModeWrapper to a task by wrapping the supplied boxed function into a new boxed function
248    /// with the same return type and lifetime, both of which this ModeWrapper is generic over.
249    fn wrap(self: Arc<Self>, task: Box<dyn FnOnce() -> T + 'm>) -> Box<dyn FnOnce() -> T + 'm>;
250
251    /// Consume this ModeWrapper and produce a [`ModeCombiner`](trait.ModeCombiner.html). This is used by
252    /// [`Mode::with`](trait.Mode.html#method.with) to be able to combine several ModeWrappers and can
253    /// reference back to this ModeWrapper to wrap tasks when applying a Mode to a task. Combining
254    /// ModeWrappers is implemented by a separate trait to be able to provide a default implementation
255    /// in [`DelegatingModeCombiner`](struct.DelegatingModeCombiner.html) that combines `ModeCombiners` by
256    /// setting the current `ModeCombiner` as the outer `ModeCombiner` of the newly added `ModeCombiner`
257    /// so that the iterator walks the `ModeCombiners` in the reverse order of which they were added, meaning
258    /// the `ModeCombiner` that was added first ends up wrapping the task last, meaning its task will be the
259    /// outermost task.
260    fn into_combiner(self) -> Box<dyn ModeCombiner<'m, T> + 'm + Send + Sync>
261    where
262        Self: Sized + Send + Sync + 'm,
263    {
264        Box::new(DelegatingModeCombiner {
265            wrapper: Arc::new(self),
266            outer: None,
267        })
268    }
269}
270
271/// Trait used to combine [`ModeWrappers`](trait.ModeWrapper.html) by allowing one `ModeWrapper` to
272/// delegate to another `ModeWrapper` and providing an iterator that can unwrap combined ModeWrappers.
273/// An implementation of this trait is returned by [`ModeWrapper::into_combiner`](trait.ModeWrapper.html#method.into_combiner)
274/// which returns a [`DelegatingModeCombiner`](struct.DelegatingModeCombiner.html) by default.
275pub trait ModeCombiner<'m, T: 'm> {
276    /// Combine this ModeCombiner with the supplied boxed ModeCombiner.
277    ///
278    /// The default implementation in [`DelegatingModeCombiner`](struct.DelegatingModeCombiner.html)
279    /// sets this ModeCombiner as the outer ModeCombiner of the supplied ModeCombiner so that the
280    /// iterator walks the `ModeCombiners` in the reverse order of which they were added, meaning
281    /// the `ModeCombiner` that was added first ends up wrapping the task last, meaning its task will be the
282    /// outermost task.
283    fn combine(
284        &self,
285        other: Box<dyn ModeCombiner<'m, T> + 'm + Send + Sync>,
286    ) -> Box<dyn ModeCombiner<'m, T> + 'm + Send + Sync>;
287
288    /// Return the outer ModeCombiner this ModeCombiner delegates to, this is the next ModeCombiner
289    /// the iterator returned by [`ModeCombiner::iter`](trait.ModeCombiner.html#method.iter) steps to.
290    fn get_outer(&self) -> Option<&(dyn ModeCombiner<'m, T> + Send + Sync)>;
291
292    /// Set the outer ModeCombiner this ModeCombiner delegates to, this is the next ModeCombiner
293    /// the iterator returned by [`ModeCombiner::iter`](trait.ModeCombiner.html#method.iter) steps to.
294    fn set_outer(&mut self, outer: Arc<dyn ModeCombiner<'m, T> + 'm + Send + Sync>);
295
296    /// Return an iterator that can unwrap combined ModeCombiners by stepping into the outer
297    /// ModeCombiner recursively.
298    fn iter<'a>(&'a self) -> ModeCombinerIterator<'a, 'm, T>;
299
300    /// Reference the source [`ModeWrapper`](trait.ModeWrapper.html). Used to wrap the task when
301    /// applying a [`Mode`](struct.Mode.html).
302    fn wrapper_ref(&self) -> Arc<dyn ModeWrapper<'m, T> + 'm + Send + Sync>;
303}
304
305/// Default implementation for the [`ModeWrapper`](trait.ModeWrapper.html) trait that combines `ModeCombiners` by
306/// setting the current `ModeCombiner` as the outer `ModeCombiner` of the newly added `ModeCombiner`
307/// so that the iterator walks the `ModeCombiners` in the reverse order of which they were added, meaning
308/// the `ModeCombiner` that was added first ends up wrapping the task last, meaning its task will be the
309/// outermost task.
310pub struct DelegatingModeCombiner<'m, T> {
311    wrapper: Arc<dyn ModeWrapper<'m, T> + 'm + Send + Sync>,
312    outer: Option<Arc<dyn ModeCombiner<'m, T> + 'm + Send + Sync>>,
313}
314
315impl<T> Clone for DelegatingModeCombiner<'_, T> {
316    fn clone(&self) -> Self {
317        DelegatingModeCombiner {
318            wrapper: self.wrapper.clone(),
319            outer: self.outer.clone(),
320        }
321    }
322}
323
324impl<'m, T> ModeCombiner<'m, T> for DelegatingModeCombiner<'m, T> {
325    fn combine(
326        &self,
327        mut other: Box<dyn ModeCombiner<'m, T> + 'm + Send + Sync>,
328    ) -> Box<dyn ModeCombiner<'m, T> + 'm + Send + Sync> {
329        let clone = self.clone();
330        other.set_outer(Arc::new(clone));
331        other
332    }
333
334    fn get_outer(&self) -> Option<&(dyn ModeCombiner<'m, T> + Send + Sync)> {
335        self.outer.as_ref().map(|outer| outer.as_ref())
336    }
337
338    fn set_outer(&mut self, outer: Arc<dyn ModeCombiner<'m, T> + 'm + Send + Sync>) {
339        self.outer = Some(outer);
340    }
341
342    fn iter<'a>(&'a self) -> ModeCombinerIterator<'a, 'm, T> {
343        ModeCombinerIterator {
344            mode_combiner: self,
345            curr_combiner: None,
346        }
347    }
348
349    fn wrapper_ref(&self) -> Arc<dyn ModeWrapper<'m, T> + 'm + Send + Sync> {
350        self.wrapper.clone()
351    }
352}
353
354/// Iterator that can unwrap combined ModeCombiners by stepping into the outer
355/// ModeCombiner recursively.
356pub struct ModeCombinerIterator<'a, 'm, T: 'm> {
357    mode_combiner: &'a dyn ModeCombiner<'m, T>,
358    curr_combiner: Option<&'a dyn ModeCombiner<'m, T>>,
359}
360
361impl<'a, 'm, T: 'm> Iterator for ModeCombinerIterator<'a, 'm, T> {
362    type Item = &'a dyn ModeCombiner<'m, T>;
363
364    fn next(&mut self) -> Option<<Self as Iterator>::Item> {
365        if let Some(curr_wrapper) = self.curr_combiner {
366            let curr_outer = curr_wrapper.get_outer();
367
368            if let Some(curr_outer) = curr_outer {
369                self.curr_combiner = Some(curr_outer);
370            } else {
371                return None;
372            }
373        } else {
374            self.curr_combiner = Some(self.mode_combiner);
375        }
376
377        self.curr_combiner
378    }
379}
380
381#[cfg(test)]
382mod tests {
383    use crate::{invoke, BaseInvoker, Invoker, Mode, ModeCombinerIterator, ModeWrapper};
384    use std::sync::{
385        atomic::{AtomicU16, Ordering},
386        Arc,
387    };
388
389    static PRE_COUNTER: AtomicU16 = AtomicU16::new(1);
390    static POST_COUNTER: AtomicU16 = AtomicU16::new(1);
391
392    struct MultiplyTwoMode {}
393    impl ModeWrapper<'static, i32> for MultiplyTwoMode {
394        fn wrap<'f>(
395            self: Arc<Self>,
396            task: Box<(dyn FnOnce() -> i32 + 'f)>,
397        ) -> Box<(dyn FnOnce() -> i32 + 'f)> {
398            Box::new(move || {
399                return task() * 2;
400            })
401        }
402    }
403
404    struct AddTwoMode {}
405    impl ModeWrapper<'static, i32> for AddTwoMode {
406        fn wrap<'f>(
407            self: Arc<Self>,
408            task: Box<(dyn FnOnce() -> i32 + 'f)>,
409        ) -> Box<(dyn FnOnce() -> i32 + 'f)> {
410            Box::new(move || {
411                return task() + 2;
412            })
413        }
414    }
415
416    struct CounterInvoker {}
417    impl Invoker for CounterInvoker {
418        fn pre_invoke(&self) {
419            PRE_COUNTER.fetch_add(1, Ordering::Relaxed);
420        }
421        fn post_invoke(&self) {
422            POST_COUNTER.fetch_add(1, Ordering::Relaxed);
423        }
424    }
425
426    struct MultInvoker {}
427    impl Invoker for MultInvoker {
428        fn pre_invoke(&self) {
429            PRE_COUNTER
430                .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |x| Some(x * 2))
431                .unwrap();
432        }
433        fn post_invoke(&self) {
434            POST_COUNTER
435                .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |x| Some(x * 2))
436                .unwrap();
437        }
438    }
439
440    struct StringRefMode<'a> {
441        str_ref: &'a str,
442    }
443    impl<'a> ModeWrapper<'a, &'a str> for StringRefMode<'a> {
444        fn wrap(
445            self: Arc<Self>,
446            task: Box<(dyn FnOnce() -> &'a str + 'a)>,
447        ) -> Box<(dyn FnOnce() -> &'a str + 'a)> {
448            Box::new(move || {
449                task();
450                self.str_ref
451            })
452        }
453    }
454
455    struct ModeCombinerIteratorMode {}
456    impl<'a, 'm> ModeWrapper<'a, ModeCombinerIterator<'a, 'm, &'m str>> for ModeCombinerIteratorMode {
457        fn wrap(
458            self: Arc<Self>,
459            task: Box<dyn FnOnce() -> ModeCombinerIterator<'a, 'm, &'m str> + 'a>,
460        ) -> Box<dyn FnOnce() -> ModeCombinerIterator<'a, 'm, &'m str> + 'a> {
461            Box::new(move || task())
462        }
463    }
464
465    #[test]
466    fn it_works() {
467        let mode = Mode::new().with(MultiplyTwoMode {}).with(AddTwoMode {});
468        assert_eq!(invoke(&mode, || 2 + 2), 12);
469    }
470
471    #[test]
472    fn test_combined_invoker() {
473        let invoker = CounterInvoker {}
474            .and_then(MultInvoker {})
475            .and_then(MultInvoker {})
476            .and_then(CounterInvoker {});
477
478        invoker.invoke(|| {
479            PRE_COUNTER
480                .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |x| Some(x * 3))
481                .unwrap();
482
483            POST_COUNTER
484                .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |x| Some(x * 3))
485                .unwrap();
486        });
487
488        assert_eq!(PRE_COUNTER.load(Ordering::Relaxed), 27);
489        assert_eq!(POST_COUNTER.load(Ordering::Relaxed), 17);
490    }
491
492    #[test]
493    fn test_lifetime_iterator() {
494        let s = String::from("test");
495        let m = StringRefMode { str_ref: &s };
496        let combiner = m.into_combiner();
497        let iter = combiner.iter();
498
499        let mode = Mode::new().with(ModeCombinerIteratorMode {});
500        let _iter = invoke(&mode, move || iter);
501    }
502
503    #[test]
504    fn test_lifetime_str_ref() {
505        let s = String::from("test");
506        let m = StringRefMode { str_ref: &s };
507
508        let mode = Mode::new().with(m);
509
510        assert_eq!("test", invoke(&mode, || { "fail" }));
511    }
512
513    #[test]
514    fn test_shorter_lifetime() {
515        let invoker = BaseInvoker {};
516
517        {
518            let shorter_lived_string = String::from("test");
519            let str_ref = invoker.invoke(|| &shorter_lived_string);
520            assert_eq!(str_ref, "test");
521        }
522    }
523
524    #[test]
525    fn test_post_invoke_on_panic() {
526        struct TestPostInvoke {
527            counter: Arc<AtomicU16>,
528        }
529
530        impl Invoker for TestPostInvoke {
531            fn post_invoke(&self) {
532                self.counter.fetch_add(1, Ordering::Relaxed);
533            }
534
535            fn invoke_post_invoke_on_panic(&self) -> bool {
536                true
537            }
538        }
539
540        let counter = Arc::new(AtomicU16::new(0));
541
542        let test = TestPostInvoke {
543            counter: counter.clone(),
544        };
545
546        let handle = std::thread::spawn(move || {
547            test.invoke(|| {
548                panic!("test panic");
549            });
550        });
551
552        let _ = handle.join();
553
554        assert_eq!(counter.load(Ordering::Relaxed), 1);
555    }
556
557    #[test]
558    fn test_not_post_invoke_on_panic() {
559        struct TestPostInvoke {
560            counter: Arc<AtomicU16>,
561        }
562
563        impl Invoker for TestPostInvoke {
564            fn post_invoke(&self) {
565                self.counter.fetch_add(1, Ordering::Relaxed);
566            }
567
568            fn invoke_post_invoke_on_panic(&self) -> bool {
569                false
570            }
571        }
572
573        let counter = Arc::new(AtomicU16::new(0));
574
575        let test = TestPostInvoke {
576            counter: counter.clone(),
577        };
578
579        let handle = std::thread::spawn(move || {
580            test.invoke(|| {
581                panic!("test panic");
582            });
583        });
584
585        let _ = handle.join();
586
587        assert_eq!(counter.load(Ordering::Relaxed), 0);
588    }
589
590    #[test]
591    fn test_send_mode() {
592        struct MultiplierMode {
593            multiplier: u16,
594        }
595
596        impl ModeWrapper<'static, u16> for MultiplierMode {
597            fn wrap(
598                self: Arc<Self>,
599                task: Box<(dyn FnOnce() -> u16)>,
600            ) -> Box<(dyn FnOnce() -> u16)> {
601                Box::new(move || {
602                    return task() * self.multiplier;
603                })
604            }
605        }
606
607        let mode = Arc::new(Mode::new().with(MultiplierMode { multiplier: 4 }));
608        let result = Arc::new(AtomicU16::new(0));
609
610        let m = mode.clone();
611        let r = result.clone();
612        let handle = std::thread::spawn(move || {
613            let result = invoke(&m, || 5);
614
615            r.store(result, Ordering::Relaxed);
616        });
617
618        handle.join().unwrap();
619
620        assert_eq!(result.load(Ordering::Relaxed), 20);
621    }
622}