pinenut_log/
runloop.rs

1//! An event loop implementation.
2
3use std::{any::Any, sync::mpsc, thread, thread::JoinHandle};
4
5use thiserror::Error;
6
7/// The runloop error.
8///
9/// When this error is occurred, either the runloop is actively stopped or the
10/// thread is panicked.
11#[derive(Error, Debug)]
12#[error("sending event on a stopped runloop")]
13pub struct Error;
14
15/// A handler for handling received events in a runloop.
16pub(crate) trait Handle {
17    type Event;
18
19    /// Handles the received event.
20    fn handle(&mut self, event: Self::Event, context: &mut Context);
21
22    /// Starts a new associated runloop.
23    #[inline]
24    fn run(self) -> Runloop<Self::Event>
25    where
26        Self: Sized + Send + 'static,
27        Self::Event: Send + 'static,
28    {
29        Runloop::run(self)
30    }
31}
32
33/// A context that is passed during the runloop run.
34#[derive(Debug)]
35pub(crate) struct Context {
36    is_stopped: bool,
37}
38
39impl Context {
40    #[inline]
41    fn new() -> Self {
42        Self { is_stopped: false }
43    }
44
45    /// Stop the runloop.
46    #[inline]
47    pub(crate) fn stop(&mut self) {
48        self.is_stopped = true;
49    }
50
51    /// Whether the runloop has stopped.
52    #[inline]
53    pub(crate) fn is_stopped(&self) -> bool {
54        self.is_stopped
55    }
56}
57
58pub(crate) struct Runloop<Event> {
59    sender: mpsc::Sender<Event>,
60    thread_handle: JoinHandle<()>,
61}
62
63impl<Event> Runloop<Event>
64where
65    Event: Send + 'static,
66{
67    /// Starts a new runloop with handler.
68    pub(crate) fn run<H>(mut handler: H) -> Self
69    where
70        H: Handle<Event = Event> + Send + 'static,
71    {
72        let (sender, receiver) = mpsc::channel();
73
74        let thread_handle = thread::spawn(move || {
75            let mut context = Context::new();
76            while !context.is_stopped() && let Ok(event) = receiver.recv() {
77                handler.handle(event, &mut context);
78            }
79        });
80
81        Self { sender, thread_handle }
82    }
83
84    /// Sends an event to the runloop.
85    ///
86    /// When the runloop has stopped, it returns [`Err`].
87    #[inline]
88    pub(crate) fn on(&self, event: Event) -> Result<(), Error> {
89        self.sender.send(event).map_err(|_| Error)
90    }
91
92    /// Waits for the runloop to finish.
93    ///
94    /// If the associated thread in runloop panics, [`Err`] is returned with the
95    /// parameter given to [`panic!`].
96    #[inline]
97    pub(crate) fn join(self) -> Result<(), Box<dyn Any + Send + 'static>> {
98        self.thread_handle.join()
99    }
100}