use bevy_ecs::prelude::*;
use bevy_tasks::{TaskPool, TaskPoolBuilder};
use std::future::Future;
use std::sync::Mutex;
use std::sync::mpsc;
pub type BoxedCommand = Box<dyn FnOnce(&mut World) + Send>;
pub type CommandSender = mpsc::Sender<BoxedCommand>;
#[derive(Resource)]
pub struct WintfTaskPool {
pool: TaskPool,
sender: mpsc::Sender<BoxedCommand>,
receiver: Mutex<mpsc::Receiver<BoxedCommand>>,
}
impl WintfTaskPool {
pub fn new() -> Self {
Self::from_pool(TaskPool::new())
}
pub fn with_threads(n: usize) -> Self {
Self::from_pool(TaskPoolBuilder::new().num_threads(n).build())
}
fn from_pool(pool: TaskPool) -> Self {
let (sender, receiver) = mpsc::channel();
Self {
pool,
sender,
receiver: Mutex::new(receiver),
}
}
pub fn spawn<F, Fut>(&self, f: F)
where
F: FnOnce(CommandSender) -> Fut + Send + 'static,
Fut: Future<Output = ()> + Send,
{
let tx = self.sender.clone();
self.pool
.spawn(async move {
f(tx).await;
})
.detach();
}
pub fn drain_and_apply(&self, world: &mut World) {
if let Ok(receiver) = self.receiver.lock() {
for cmd in receiver.try_iter() {
cmd(world);
}
}
}
pub fn drain_commands(&self) -> Vec<BoxedCommand> {
if let Ok(receiver) = self.receiver.lock() {
receiver.try_iter().collect()
} else {
Vec::new()
}
}
#[cfg(test)]
pub fn send_command(&self, cmd: BoxedCommand) {
let _ = self.sender.send(cmd);
}
#[cfg(test)]
pub fn is_empty(&self) -> bool {
true
}
}
impl Default for WintfTaskPool {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
#[path = "task_pool_tests.rs"]
mod tests;