use super::{Controller, Error, ThreadHandle, ThreadState};
use orphan_crippler::Receiver;
use std::{any::Any, cell::Cell, sync::Arc, thread_local};
use thread_safe::ThreadKey;
thread_local! {
static IS_BREAD_THREAD: Cell<bool> = Cell::new(false);
}
pub struct BreadThread<'evh, Ctrl: Controller> {
state: Arc<ThreadState<'evh, Ctrl>>,
key: ThreadKey,
}
impl<'evh, Ctrl: Controller> BreadThread<'evh, Ctrl> {
#[inline]
pub fn try_new(controller: Ctrl) -> Result<Self, Error<Ctrl::Error>> {
if IS_BREAD_THREAD.with(|ibt| ibt.replace(true)) {
Err(Error::NotInBreadThread)
} else {
Ok(Self {
state: Arc::new(ThreadState::new(controller)),
key: ThreadKey::get(),
})
}
}
#[inline]
pub fn new(controller: Ctrl) -> Self {
Self::try_new(controller).unwrap_or_else(|_| panic!("Thread is already a bread thread"))
}
#[inline]
pub fn with<T, F: FnOnce(&Ctrl) -> T>(&self, f: F) -> T {
self.state.with(f, self.key).unwrap()
}
#[inline]
pub fn with_mut<T, F: FnOnce(&mut Ctrl) -> T>(&mut self, f: F) -> T {
Arc::get_mut(&mut self.state)
.expect("Already has handles")
.with_mut(f, self.key)
.unwrap()
}
#[inline]
pub fn send_directive<T: Any + Send>(
&self,
directive: Ctrl::Directive,
) -> Result<Receiver<T>, Error<Ctrl::Error>> {
self.state.send_directive(directive, self.key)
}
#[inline]
pub fn set_event_handler<
F: FnMut(&Ctrl, Ctrl::Event) -> Result<(), Ctrl::Error> + Send + 'evh,
>(
&self,
event_handler: F,
) {
self.state.set_event_handler(event_handler);
}
#[inline]
pub fn process_event(&self, event: Ctrl::Event) -> Result<(), Error<Ctrl::Error>> {
self.state.process_event(event, self.key)
}
#[inline]
pub fn main_loop(self) -> Result<(), Error<Ctrl::Error>> {
while self.state.loop_cycle(self.key)? {}
Ok(())
}
#[inline]
pub fn handle(&self) -> ThreadHandle<'evh, Ctrl> {
self.state.init_directive_channel();
ThreadHandle::from_weak(Arc::downgrade(&self.state))
}
}
impl<'evh, Ctrl: Controller> Drop for BreadThread<'evh, Ctrl> {
#[inline]
fn drop(&mut self) {
let _ = IS_BREAD_THREAD.try_with(|ibt| ibt.set(false));
}
}