1use futures::Future;
7use std::marker::PhantomData;
8use std::pin::Pin;
9use std::sync::atomic::{AtomicBool, Ordering};
10use std::sync::{Arc, Mutex};
11use thiserror::Error;
12use workflow_core::channel::{
13 Receiver, RecvError, SendError, Sender, TryRecvError, TrySendError, oneshot,
14};
15pub use workflow_task_macros::{set_task, task};
16
17#[derive(Debug, Error)]
19pub enum TaskError {
20 #[error("The task is not running")]
22 NotRunning,
23 #[error("The task is already running")]
25 AlreadyRunning,
26 #[error("Task channel send error {0}")]
28 SendError(String),
29 #[error("Task channel receive error: {0:?}")]
31 RecvError(#[from] RecvError),
32 #[error("Task channel try send error: {0}")]
34 TrySendError(String),
35 #[error("Task channel try receive {0:?}")]
37 TryRecvError(#[from] TryRecvError),
38}
39
40impl<T> From<SendError<T>> for TaskError {
41 fn from(err: SendError<T>) -> Self {
42 TaskError::SendError(err.to_string())
43 }
44}
45
46impl<T> From<TrySendError<T>> for TaskError {
47 fn from(err: TrySendError<T>) -> Self {
48 TaskError::SendError(err.to_string())
49 }
50}
51
52pub type TaskResult<T> = std::result::Result<T, TaskError>;
54
55pub type TaskFn<A, T> = Arc<Box<dyn Send + Sync + Fn(A, Receiver<()>) -> FnReturn<T> + 'static>>;
58pub type FnReturn<T> = Pin<Box<dyn Send + Sync + 'static + Future<Output = T>>>;
60
61struct TaskInner<A, T>
62where
63 A: Send,
64 T: 'static,
65{
66 termination: (Sender<()>, Receiver<()>),
67 completion: (Sender<T>, Receiver<T>),
68 running: Arc<AtomicBool>,
69 task_fn: Arc<Mutex<Option<TaskFn<A, T>>>>,
70 args: PhantomData<A>,
71}
72
73impl<A, T> TaskInner<A, T>
74where
75 A: Send + Sync + 'static,
76 T: Send + 'static,
77{
78 fn new_with_boxed_task_fn<FN>(task_fn: Box<FN>) -> Self
79 where
81 FN: Send + Sync + Fn(A, Receiver<()>) -> FnReturn<T> + 'static,
82 {
83 let termination = oneshot();
84 let completion = oneshot();
85
86 TaskInner {
87 termination,
88 completion,
89 running: Arc::new(AtomicBool::new(false)),
90 task_fn: Arc::new(Mutex::new(Some(Arc::new(task_fn)))),
91 args: PhantomData,
92 }
93 }
94
95 pub fn blank() -> Self {
96 let termination = oneshot();
97 let completion = oneshot();
98 TaskInner {
99 termination,
100 completion,
101 running: Arc::new(AtomicBool::new(false)),
102 task_fn: Arc::new(Mutex::new(None)),
103 args: PhantomData,
104 }
105 }
106
107 fn task_fn(&self) -> TaskFn<A, T> {
108 self.task_fn
109 .lock()
110 .unwrap()
111 .as_ref()
112 .expect("Task::task_fn is not initialized")
113 .clone()
114 }
115
116 fn set_boxed_task_fn(
119 &self,
120 task_fn: Box<dyn Send + Sync + Fn(A, Receiver<()>) -> FnReturn<T> + 'static>,
121 ) {
122 let task_fn = Arc::new(task_fn);
123 *self.task_fn.lock().unwrap() = Some(task_fn);
124 }
125
126 pub fn run(self: &Arc<Self>, args: A) -> TaskResult<&Arc<Self>> {
127 if !self.completion.1.is_empty() {
128 panic!("Task::run(): task completion channel is not empty");
129 }
130
131 if !self.termination.1.is_empty() {
132 panic!("Task::run(): task termination channel is not empty");
133 }
134
135 let this = self.clone();
136 let cb = self.task_fn();
137 workflow_core::task::spawn(async move {
138 this.running.store(true, Ordering::SeqCst);
139
140 let result = cb(args, this.termination.1.clone()).await;
141 this.running.store(false, Ordering::SeqCst);
142 this.completion
143 .0
144 .send(result)
145 .await
146 .expect("Error signaling task completion");
147 });
148
149 Ok(self)
150 }
151
152 pub fn stop(&self) -> TaskResult<()> {
153 if self.running.load(Ordering::SeqCst) {
154 self.termination.0.try_send(())?;
155 }
156 Ok(())
157 }
158
159 pub async fn join(&self) -> TaskResult<T> {
162 if self.running.load(Ordering::SeqCst) {
163 Ok(self.completion.1.recv().await?)
164 } else {
165 Err(TaskError::NotRunning)
166 }
167 }
168
169 pub async fn stop_and_join(&self) -> TaskResult<T> {
172 if self.running.load(Ordering::SeqCst) {
173 self.termination.0.send(()).await?;
174 Ok(self.completion.1.recv().await?)
175 } else {
176 Err(TaskError::NotRunning)
177 }
178 }
179
180 pub fn is_running(&self) -> bool {
181 self.running.load(Ordering::SeqCst)
182 }
183}
184
185#[derive(Clone)]
236pub struct Task<A, T>
237where
238 A: Send,
239 T: 'static,
240{
241 inner: Arc<TaskInner<A, T>>,
242}
243
244impl<A, T> Default for Task<A, T>
245where
246 A: Send + Sync + 'static,
247 T: Send + Sync + 'static,
248{
249 fn default() -> Self {
250 Task::blank()
251 }
252}
253
254impl<A, T> Task<A, T>
255where
256 A: Send + Sync + 'static,
257 T: Send + 'static,
258{
259 pub fn new<FN>(task_fn: FN) -> Task<A, T>
271 where
272 FN: Send + Sync + Fn(A, Receiver<()>) -> FnReturn<T> + 'static,
273 {
274 Self::new_with_boxed_task_fn(Box::new(task_fn))
275 }
276
277 fn new_with_boxed_task_fn<FN>(task_fn: Box<FN>) -> Task<A, T>
278 where
279 FN: Send + Sync + Fn(A, Receiver<()>) -> FnReturn<T> + 'static,
280 {
281 Task {
282 inner: Arc::new(TaskInner::new_with_boxed_task_fn(task_fn)),
283 }
284 }
285
286 pub fn blank() -> Self {
289 Task {
290 inner: Arc::new(TaskInner::blank()),
291 }
292 }
293
294 pub fn set_task_fn<FN>(&self, task_fn: FN)
299 where
300 FN: Send + Sync + Fn(A, Receiver<()>) -> FnReturn<T> + 'static,
301 {
302 self.inner.set_boxed_task_fn(Box::new(task_fn))
303 }
304
305 pub fn run(&self, args: A) -> TaskResult<&Self> {
308 self.inner.run(args)?;
309 Ok(self)
310 }
311
312 pub fn stop(&self) -> TaskResult<()> {
317 self.inner.stop()
318 }
319
320 pub async fn join(&self) -> TaskResult<T> {
323 self.inner.join().await
324 }
325
326 pub async fn stop_and_join(&self) -> TaskResult<T> {
329 self.inner.stop_and_join().await
330 }
331
332 pub fn is_running(&self) -> bool {
335 self.inner.is_running()
336 }
337}
338
339#[cfg(not(target_arch = "wasm32"))]
340#[cfg(test)]
341mod test {
342
343 use super::*;
344 use std::time::Duration;
345
346 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
347 pub async fn test_task() {
348 let task = Task::new(|args, stop| -> FnReturn<String> {
349 Box::pin(async move {
350 println!("starting task... {}", args);
351 for i in 0..10 {
352 if stop.try_recv().is_ok() {
353 println!("stopping task...");
354 break;
355 }
356 println!("t: {}", i);
357 workflow_core::task::sleep(Duration::from_millis(500)).await;
358 }
359 println!("exiting task...");
360 format!("finished {args}")
361 })
362 });
363
364 task.run("- first -").ok();
365
366 for i in 0..5 {
367 println!("m: {}", i);
368 workflow_core::task::sleep(Duration::from_millis(500)).await;
369 }
370
371 let ret1 = task.join().await.expect("[ret1] task wait failed");
372 println!("ret1: {:?}", ret1);
373
374 task.stop().ok();
375
376 task.run("- second -").ok();
377
378 for i in 0..5 {
379 println!("m: {}", i);
380 workflow_core::task::sleep(Duration::from_millis(500)).await;
381 }
382
383 task.stop().ok();
384 let ret2 = task.join().await.expect("[ret2] task wait failed");
385 println!("ret2: {:?}", ret2);
386
387 println!("done");
388 }
389}