use iris_core::HasState; use std::{pin::Pin, sync::Arc}; use tokio::{ runtime::Runtime, sync::mpsc::{ UnboundedReceiver as AsyncReceiver, UnboundedSender as AsyncSender, unbounded_channel as async_channel, }, }; pub trait TaskUpdate: FnOnce(&mut Rsc::State, &mut Rsc) + Send {} impl TaskUpdate for F {} /// Hands an update from a task to the thread that owns the ui. Delivery and /// waking are one act: a host posts the update as a message its loop already /// carries, so nothing has to wake the loop separately, or claim a redraw to /// be looked at. pub trait TaskQueue: Send + Sync + 'static { fn send(&self, update: Box>); } pub struct Tasks { start: AsyncSender, queue: Arc>, } pub struct TaskCtx { queue: Arc>, } impl TaskCtx { pub fn update(&mut self, f: impl TaskUpdate + 'static) { self.queue.send(Box::new(f)); } } type BoxTask = Pin + Send>>; impl Tasks { pub fn init(queue: Arc>) -> Self { let (start, start_recv) = async_channel(); std::thread::spawn(|| { let rt = Runtime::new().unwrap(); rt.block_on(listen(start_recv)) }); Self { start, queue } } pub fn spawn) + 'static + std::marker::Send>(&mut self, task: F) where F::CallOnceFuture: Send, { let queue = self.queue.clone(); let _ = self.start.send(Box::pin(async move { task(TaskCtx { queue }).await; })); } } async fn listen(mut recv: AsyncReceiver) { while let Some(task) = recv.recv().await { tokio::spawn(task); } }