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}