1#![forbid(unsafe_code)]
2
3#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
4pub struct TaskId(u64);
5
6impl TaskId {
7 pub fn new(raw: u64) -> Self {
8 Self(raw)
9 }
10
11 pub fn get(self) -> u64 {
12 self.0
13 }
14}
15
16impl std::fmt::Display for TaskId {
17 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18 write!(f, "task#{}", self.0)
19 }
20}
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq)]
23#[non_exhaustive]
24pub enum JoinError {
25 Cancelled,
26
27 Panicked,
28}
29
30impl std::fmt::Display for JoinError {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 match self {
33 Self::Cancelled => write!(f, "task was cancelled"),
34 Self::Panicked => write!(f, "task panicked on worker"),
35 }
36 }
37}
38
39impl std::error::Error for JoinError {}
40
41#[derive(Clone)]
42pub struct TaskHandle {
43 id: TaskId,
44 cancel: std::rc::Rc<dyn Fn(TaskId) -> bool>,
45}
46
47impl TaskHandle {
48 pub fn new(id: TaskId, cancel: impl Fn(TaskId) -> bool + 'static) -> Self {
49 Self {
50 id,
51 cancel: std::rc::Rc::new(cancel),
52 }
53 }
54
55 pub fn id(&self) -> TaskId {
56 self.id
57 }
58
59 pub fn cancel(&self) -> bool {
60 (self.cancel)(self.id)
61 }
62}
63
64impl std::fmt::Debug for TaskHandle {
65 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66 f.debug_struct("TaskHandle").field("id", &self.id).finish()
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 #[test]
75 fn handle_delegates_cancel_to_closure() {
76 let seen = std::rc::Rc::new(std::cell::RefCell::new(Vec::new()));
77 let probe = std::rc::Rc::clone(&seen);
78 let h = TaskHandle::new(TaskId::new(7), move |id| {
79 probe.borrow_mut().push(id.get());
80 true
81 });
82 assert_eq!(h.id().get(), 7);
83 assert!(h.cancel());
84 assert_eq!(*seen.borrow(), vec![7]);
85 }
86
87 #[test]
88 fn task_id_orders_and_displays() {
89 assert!(TaskId::new(1) < TaskId::new(2));
90 assert_eq!(format!("{}", TaskId::new(3)), "task#3");
91 }
92}