use crate::delegate;
use std::{
sync::{OnceLock, mpsc},
thread,
};
use objc2_foundation::{NSDate, NSDefaultRunLoopMode, NSRunLoop};
const TICK_SECS: f64 = 0.05;
type Task = Box<dyn FnOnce() + Send + 'static>;
static WORKER: OnceLock<mpsc::Sender<Task>> = OnceLock::new();
fn handle() -> &'static mpsc::Sender<Task> {
WORKER.get_or_init(|| {
let (tx, rx) = mpsc::channel::<Task>();
thread::Builder::new()
.name("mac-usernotifications".into())
.spawn(move || {
log::debug!("thread started");
worker_loop(rx);
})
.expect("failed to spawn UN worker thread");
tx
})
}
fn worker_loop(rx: mpsc::Receiver<Task>) {
delegate::install();
let run_loop = NSRunLoop::currentRunLoop();
loop {
while let Ok(task) = rx.try_recv() {
log::debug!("running job");
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(task)) {
Ok(()) => log::debug!("task done"),
Err(_) => log::error!("task panicked"),
}
}
let until = NSDate::dateWithTimeIntervalSinceNow(TICK_SECS);
let _ = unsafe { run_loop.runMode_beforeDate(NSDefaultRunLoopMode, &until) };
}
}
pub(crate) fn dispatch<F: FnOnce() + Send + 'static>(task: F) {
if handle().send(Box::new(task)).is_err() {
log::error!("dispatch failed (worker thread is gone)");
}
}