Skip to main content

fyrox_core/
task.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21#[cfg(not(target_arch = "wasm32"))]
22use crate::futures::executor::ThreadPool;
23use crate::SafeLock;
24use parking_lot::Mutex;
25use std::{
26    any::Any,
27    future::Future,
28    sync::mpsc::{self, Receiver, Sender},
29};
30use uuid::Uuid;
31
32// ========
33// Non-WASM
34#[cfg(not(target_arch = "wasm32"))]
35pub trait AsyncTaskResult: Any + Send + 'static {
36    fn into_any(self: Box<Self>) -> Box<dyn Any>;
37}
38
39#[cfg(not(target_arch = "wasm32"))]
40impl<T> AsyncTaskResult for T
41where
42    T: Any + Send + 'static,
43{
44    fn into_any(self: Box<Self>) -> Box<dyn Any> {
45        self
46    }
47}
48
49#[cfg(not(target_arch = "wasm32"))]
50pub trait AsyncTask<R: AsyncTaskResult>: Future<Output = R> + Send + 'static {}
51
52#[cfg(not(target_arch = "wasm32"))]
53impl<T, R: AsyncTaskResult> AsyncTask<R> for T where T: Future<Output = R> + Send + 'static {}
54
55// ========
56// WASM
57#[cfg(target_arch = "wasm32")]
58pub trait AsyncTaskResult: Any + 'static {
59    fn into_any(self: Box<Self>) -> Box<dyn Any>;
60}
61
62#[cfg(target_arch = "wasm32")]
63impl<T> AsyncTaskResult for T
64where
65    T: Any + 'static,
66{
67    fn into_any(self: Box<Self>) -> Box<dyn Any> {
68        self
69    }
70}
71
72#[cfg(target_arch = "wasm32")]
73pub trait AsyncTask<R: AsyncTaskResult>: Future<Output = R> + 'static {}
74
75#[cfg(target_arch = "wasm32")]
76impl<T, R: AsyncTaskResult> AsyncTask<R> for T where T: Future<Output = R> + 'static {}
77
78// ========
79// Common
80impl dyn AsyncTaskResult {
81    pub fn downcast<T: AsyncTaskResult>(self: Box<Self>) -> Result<Box<T>, Box<dyn Any>> {
82        self.into_any().downcast()
83    }
84}
85
86pub struct TaskResult {
87    pub id: Uuid,
88    pub payload: Box<dyn AsyncTaskResult>,
89}
90
91pub struct TaskPool {
92    #[cfg(not(target_arch = "wasm32"))]
93    thread_pool: ThreadPool,
94    sender: Sender<TaskResult>,
95    receiver: Mutex<Receiver<TaskResult>>,
96}
97
98impl Default for TaskPool {
99    fn default() -> Self {
100        Self::new()
101    }
102}
103
104impl TaskPool {
105    #[inline]
106    pub fn new() -> Self {
107        let (sender, receiver) = mpsc::channel();
108        Self {
109            #[cfg(not(target_arch = "wasm32"))]
110            thread_pool: ThreadPool::new().unwrap(),
111            sender,
112            receiver: Mutex::new(receiver),
113        }
114    }
115
116    #[inline]
117    #[cfg(target_arch = "wasm32")]
118    pub fn spawn_task<F>(&self, future: F)
119    where
120        F: Future<Output = ()> + 'static,
121    {
122        crate::wasm_bindgen_futures::spawn_local(future);
123    }
124
125    #[inline]
126    #[cfg(not(target_arch = "wasm32"))]
127    pub fn spawn_task<F>(&self, future: F)
128    where
129        F: Future<Output = ()> + Send + 'static,
130    {
131        self.thread_pool.spawn_ok(future);
132    }
133
134    #[inline]
135    pub fn spawn_with_result<F, T>(&self, future: F) -> Uuid
136    where
137        F: AsyncTask<T>,
138        T: AsyncTaskResult,
139    {
140        let id = Uuid::new_v4();
141        let sender = self.sender.clone();
142        self.spawn_task(async move {
143            let result = future.await;
144            sender
145                .send(TaskResult {
146                    id,
147                    payload: Box::new(result),
148                })
149                .unwrap();
150        });
151        id
152    }
153
154    #[inline]
155    pub fn next_task_result(&self) -> Option<TaskResult> {
156        self.receiver.safe_lock().try_recv().ok()
157    }
158}