ferroui_system/threading/tasks/
task.rs1use crate::ObservableError;
5use std::cell::{Cell, RefCell};
6use std::error::Error;
7use std::fmt;
8use std::future::Future;
9use std::pin::Pin;
10use std::rc::Rc;
11use std::sync::atomic::{AtomicU64, Ordering};
12use std::task::{Context, Poll, Waker};
13
14#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
16pub enum TaskStatus {
17 Created,
19 WaitingForActivation,
21 Running,
23 RanToCompletion,
25 Faulted,
27 Canceled,
29}
30
31#[derive(Clone, Debug, Default, PartialEq, Eq)]
33pub struct TaskCanceledError {
34 pub task_id: Option<u64>,
36}
37
38impl fmt::Display for TaskCanceledError {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 f.write_str("A task was canceled.")
41 }
42}
43
44impl Error for TaskCanceledError {}
45
46impl TaskCanceledError {
47 pub fn is_cancellation(error: &ObservableError) -> bool {
49 error.downcast_ref::<TaskCanceledError>().is_some()
50 }
51}
52
53fn next_task_id() -> u64 {
54 static NEXT: AtomicU64 = AtomicU64::new(1);
55 NEXT.fetch_add(1, Ordering::Relaxed)
56}
57
58type Continuation = Box<dyn FnOnce()>;
59
60#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
63pub struct ContinuationId(u64);
64
65struct TaskInner<T> {
66 id: u64,
67 status: Cell<TaskStatus>,
68 result: RefCell<Option<T>>,
69 error: RefCell<Option<ObservableError>>,
70 continuations: RefCell<Vec<(u64, Continuation)>>,
71 next_continuation: Cell<u64>,
72 wakers: RefCell<Vec<Waker>>,
73}
74
75pub struct Task<T> {
83 inner: Rc<TaskInner<T>>,
84}
85
86impl<T> Clone for Task<T> {
87 fn clone(&self) -> Self {
88 Self { inner: self.inner.clone() }
89 }
90}
91
92impl<T> fmt::Debug for Task<T> {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 f.debug_struct("Task").field("id", &self.inner.id).field("status", &self.inner.status.get()).finish()
95 }
96}
97
98impl<T: 'static> Task<T> {
99 pub(crate) fn with_status(status: TaskStatus) -> Self {
100 Self {
101 inner: Rc::new(TaskInner {
102 id: next_task_id(),
103 status: Cell::new(status),
104 result: RefCell::new(None),
105 error: RefCell::new(None),
106 continuations: RefCell::new(Vec::new()),
107 next_continuation: Cell::new(0),
108 wakers: RefCell::new(Vec::new()),
109 }),
110 }
111 }
112
113 pub fn from_result(result: T) -> Self {
115 let task = Self::with_status(TaskStatus::WaitingForActivation);
116 task.try_complete(Ok(result));
117 task
118 }
119
120 pub fn from_exception(error: ObservableError) -> Self {
123 let task = Self::with_status(TaskStatus::WaitingForActivation);
124 task.try_complete(Err(error));
125 task
126 }
127
128 pub fn from_canceled() -> Self {
130 let task = Self::with_status(TaskStatus::WaitingForActivation);
131 task.try_cancel();
132 task
133 }
134
135 pub fn spawn_local(future: impl Future<Output = Result<T, ObservableError>> + 'static) -> Self {
138 super::local_executor::spawn_local(future)
139 }
140
141 pub fn id(&self) -> u64 {
143 self.inner.id
144 }
145
146 pub fn status(&self) -> TaskStatus {
148 self.inner.status.get()
149 }
150
151 pub fn is_completed(&self) -> bool {
153 matches!(self.status(), TaskStatus::RanToCompletion | TaskStatus::Faulted | TaskStatus::Canceled)
154 }
155
156 pub fn is_completed_successfully(&self) -> bool {
158 self.status() == TaskStatus::RanToCompletion
159 }
160
161 pub fn is_faulted(&self) -> bool {
163 self.status() == TaskStatus::Faulted
164 }
165
166 pub fn is_canceled(&self) -> bool {
168 self.status() == TaskStatus::Canceled
169 }
170
171 pub fn exception(&self) -> Option<ObservableError> {
173 self.inner.error.borrow().clone()
174 }
175
176 pub fn result(&self) -> Result<T, ObservableError>
182 where
183 T: Clone,
184 {
185 match self.status() {
186 TaskStatus::RanToCompletion => Ok(self.inner.result.borrow().clone().expect("a completed task holds its result")),
187 TaskStatus::Faulted => Err(self.exception().expect("a faulted task holds its error")),
188 TaskStatus::Canceled => Err(Rc::new(TaskCanceledError { task_id: Some(self.id()) })),
189 _ => panic!("the result of a task that has not ended was asked for; await it or use continue_with"),
190 }
191 }
192
193 pub fn continue_with(&self, continuation: impl FnOnce(&Task<T>) + 'static) -> Option<ContinuationId> {
198 let task = self.clone();
199 if self.is_completed() {
200 continuation(&task);
201 return None;
202 }
203 let id = self.inner.next_continuation.get();
204 self.inner.next_continuation.set(id + 1);
205 self.inner.continuations.borrow_mut().push((id, Box::new(move || continuation(&task))));
206 Some(ContinuationId(id))
207 }
208
209 pub fn remove_continuation(&self, id: ContinuationId) -> bool {
211 let removed = {
212 let mut continuations = self.inner.continuations.borrow_mut();
213 let position = continuations.iter().position(|(entry, _)| *entry == id.0);
214 position.map(|position| continuations.remove(position))
215 };
216 removed.is_some()
218 }
219
220 pub(crate) fn try_complete(&self, outcome: Result<T, ObservableError>) -> bool {
223 if self.is_completed() {
224 return false;
225 }
226 match outcome {
227 Ok(result) => {
228 *self.inner.result.borrow_mut() = Some(result);
229 self.inner.status.set(TaskStatus::RanToCompletion);
230 }
231 Err(error) if TaskCanceledError::is_cancellation(&error) => {
232 self.inner.status.set(TaskStatus::Canceled);
233 }
234 Err(error) => {
235 *self.inner.error.borrow_mut() = Some(error);
236 self.inner.status.set(TaskStatus::Faulted);
237 }
238 }
239 self.finish();
240 true
241 }
242
243 pub(crate) fn try_cancel(&self) -> bool {
244 if self.is_completed() {
245 return false;
246 }
247 self.inner.status.set(TaskStatus::Canceled);
248 self.finish();
249 true
250 }
251
252 fn finish(&self) {
253 let continuations = std::mem::take(&mut *self.inner.continuations.borrow_mut());
254 for (_, continuation) in continuations {
255 continuation();
256 }
257 let wakers = std::mem::take(&mut *self.inner.wakers.borrow_mut());
258 for waker in wakers {
259 waker.wake();
260 }
261 }
262}
263
264impl<T: Clone + 'static> Future for Task<T> {
265 type Output = Result<T, ObservableError>;
266
267 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
268 if self.is_completed() {
269 return Poll::Ready(self.result());
270 }
271 let mut wakers = self.inner.wakers.borrow_mut();
272 if !wakers.iter().any(|waker| waker.will_wake(cx.waker())) {
273 wakers.push(cx.waker().clone());
274 }
275 Poll::Pending
276 }
277}
278
279pub struct TaskCompletionSource<T> {
281 task: Task<T>,
282}
283
284impl<T: 'static> Default for TaskCompletionSource<T> {
285 fn default() -> Self {
286 Self::new()
287 }
288}
289
290impl<T> Clone for TaskCompletionSource<T> {
291 fn clone(&self) -> Self {
292 Self { task: self.task.clone() }
293 }
294}
295
296impl<T: 'static> TaskCompletionSource<T> {
297 pub fn new() -> Self {
299 Self { task: Task::with_status(TaskStatus::WaitingForActivation) }
300 }
301
302 pub fn task(&self) -> Task<T> {
304 self.task.clone()
305 }
306
307 pub fn set_result(&self, result: T) {
312 assert!(self.try_set_result(result), "An attempt was made to transition a task to a final state when it had already completed.");
313 }
314
315 pub fn try_set_result(&self, result: T) -> bool {
317 self.task.try_complete(Ok(result))
318 }
319
320 pub fn set_exception(&self, error: ObservableError) {
325 assert!(self.try_set_exception(error), "An attempt was made to transition a task to a final state when it had already completed.");
326 }
327
328 pub fn try_set_exception(&self, error: ObservableError) -> bool {
330 self.task.try_complete(Err(error))
331 }
332
333 pub fn set_canceled(&self) {
338 assert!(self.try_set_canceled(), "An attempt was made to transition a task to a final state when it had already completed.");
339 }
340
341 pub fn try_set_canceled(&self) -> bool {
343 self.task.try_cancel()
344 }
345}