use crate::event::Event;
use crate::handler::{HandlerFn, HandlerId, HandlerRegistry};
use crossbeam_channel::{Receiver, RecvTimeoutError, SendError, Sender};
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::{Arc, Mutex, RwLock};
use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PutError {
Stopped,
}
pub struct EventEngine {
interval: Duration,
sender: RwLock<Option<Sender<Event>>>,
receiver: Mutex<Option<Receiver<Event>>>,
handlers: Arc<HandlerRegistry>,
active: Arc<AtomicBool>,
worker: Mutex<Option<std::thread::JoinHandle<()>>>,
timer: Mutex<Option<std::thread::JoinHandle<()>>>,
pending: Arc<AtomicU64>,
}
impl Default for EventEngine {
fn default() -> Self {
Self::new()
}
}
impl EventEngine {
pub fn new() -> Self {
Self::with_interval(Duration::from_secs(1))
}
pub fn with_interval(interval: Duration) -> Self {
let (tx, rx) = crossbeam_channel::unbounded();
Self {
interval,
sender: RwLock::new(Some(tx)),
receiver: Mutex::new(Some(rx)),
handlers: Arc::default(),
active: Arc::new(AtomicBool::new(false)),
worker: Mutex::new(None),
timer: Mutex::new(None),
pending: Arc::new(AtomicU64::new(0)),
}
}
pub fn start(&self) {
let Some(rx) = self.receiver.lock().unwrap().take() else {
return;
};
self.active.store(true, Ordering::SeqCst);
let worker = std::thread::Builder::new()
.name("ek-event-dispatch".into())
.spawn({
let handlers = self.handlers.clone();
let active = self.active.clone();
let pending = self.pending.clone();
move || run_dispatch(rx, handlers, active, pending)
})
.expect("spawn dispatch thread");
let timer = std::thread::Builder::new()
.name("ek-event-timer".into())
.spawn({
let sender = self.sender.read().unwrap().clone();
let active = self.active.clone();
let step = self.interval;
let pending = self.pending.clone();
move || run_timer(sender, active, step, pending)
})
.expect("spawn timer thread");
*self.worker.lock().unwrap() = Some(worker);
*self.timer.lock().unwrap() = Some(timer);
}
pub fn put(&self, event: Event) -> Result<(), PutError> {
self.pending.fetch_add(1, Ordering::AcqRel);
let sent = match self.sender.read().unwrap().as_ref() {
Some(tx) => tx.send(event),
None => {
self.pending.fetch_sub(1, Ordering::AcqRel);
return Err(PutError::Stopped);
}
};
match sent {
Ok(()) => Ok(()),
Err(SendError(_)) => {
self.pending.fetch_sub(1, Ordering::AcqRel);
Err(PutError::Stopped)
}
}
}
pub fn register(&self, event_type: impl Into<String>, f: HandlerFn) -> HandlerId {
self.handlers.register(event_type, f)
}
pub fn unregister(&self, id: HandlerId) -> bool {
self.handlers.unregister(id)
}
pub fn register_general(&self, f: HandlerFn) -> HandlerId {
self.handlers.register_general(f)
}
pub fn unregister_general(&self, id: HandlerId) -> bool {
self.handlers.unregister_general(id)
}
pub fn is_active(&self) -> bool {
self.active.load(Ordering::SeqCst)
}
pub fn pending_events(&self) -> u64 {
self.pending.load(Ordering::SeqCst)
}
pub fn stop(&self) {
if !self.active.swap(false, Ordering::SeqCst) {
return; }
*self.sender.write().unwrap() = None;
let timer_handle = self.timer.lock().unwrap().take();
if let Some(t) = timer_handle {
let _ = t.join();
}
let worker_handle = self.worker.lock().unwrap().take();
if let Some(w) = worker_handle {
let _ = w.join();
}
self.pending.store(0, Ordering::SeqCst);
}
}
fn run_dispatch(
rx: Receiver<Event>,
handlers: Arc<HandlerRegistry>,
active: Arc<AtomicBool>,
pending: Arc<AtomicU64>,
) {
const POLL: Duration = Duration::from_secs(1);
loop {
if !active.load(Ordering::SeqCst) {
break;
}
match rx.recv_timeout(POLL) {
Ok(event) => {
pending.fetch_sub(1, Ordering::AcqRel);
process_event(&event, &handlers);
}
Err(RecvTimeoutError::Timeout) => continue, Err(RecvTimeoutError::Disconnected) => break,
}
}
}
fn run_timer(
sender: Option<Sender<Event>>,
active: Arc<AtomicBool>,
step: Duration,
pending: Arc<AtomicU64>,
) {
let Some(sender) = sender else { return };
loop {
std::thread::sleep(step);
if !active.load(Ordering::SeqCst) {
break;
}
pending.fetch_add(1, Ordering::AcqRel);
if sender.send(Event::new(crate::EVENT_TIMER)).is_err() {
pending.fetch_sub(1, Ordering::AcqRel);
break;
}
}
}
fn process_event(event: &Event, handlers: &HandlerRegistry) {
for f in handlers.snapshot(&event.event_type) {
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| f(event)));
}
}