my_ecs/ecs/
worker.rs

1use super::{sched::ctrl::SubContext, sys::system::SystemId};
2use crate::ds::ManagedConstPtr;
3use std::{any::Any, fmt};
4
5pub mod prelude {
6    pub use super::Work;
7}
8
9/// A trait for worker.
10///
11/// Worker should be able to `park` itself or `unpark`ed by a function call.
12/// When the worker is unparked, worker should get ready to handle messages
13/// using the passed [`SubContext`]. See an example code below.
14///
15/// ```ignore
16/// fn unpark(&mut self, cx: ManagedConstPtr<SubContext>) -> bool {
17///     // Sends `cx` to the associated worker.
18///     self.tx.send(cx).is_ok();
19/// }
20///
21/// fn worker_function(&self) {
22///     // Parking during idle.
23///     while let Ok(cx) = rx.recv() {
24///         // `SubContext` provides message handling function for you.
25///         SubContext::execute(cx);
26///     }
27/// }
28/// ```
29pub trait Work {
30    /// Wakes the associated worker then call [`SubContext::execute`] on the
31    /// worker, and then returns true if all is ok.
32    fn unpark(&mut self, cx: ManagedConstPtr<SubContext>) -> bool;
33
34    /// Blocks then returns true.
35    ///
36    /// It's highly recommended not to use long-term spin-lock because it may
37    /// last undefinitely.
38    fn park(&mut self) -> bool;
39
40    /// Returns worker name.
41    fn name(&self) -> &str;
42}
43
44#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)]
45pub(crate) struct WorkerId {
46    id: u32,
47    group_index: u16,
48    worker_index: u16,
49}
50
51impl WorkerId {
52    const DUMMY: Self = Self {
53        id: u32::MAX,
54        group_index: u16::MAX,
55        worker_index: u16::MAX,
56    };
57
58    pub(crate) const fn new(id: u32, group_index: u16, worker_index: u16) -> Self {
59        Self {
60            id,
61            group_index,
62            worker_index,
63        }
64    }
65
66    pub(crate) const fn dummy() -> Self {
67        Self::DUMMY
68    }
69
70    pub(crate) const fn group_index(&self) -> u16 {
71        self.group_index
72    }
73
74    pub(crate) const fn worker_index(&self) -> u16 {
75        self.worker_index
76    }
77}
78
79pub(crate) enum Message {
80    Handle(WorkerId),
81    /// When a worker finishes its task, it will send this message to the main thread.
82    //
83    // Channel is based on mpsc. So it's needed to include identification of sender.
84    Fin(WorkerId, SystemId),
85
86    Aborted(WorkerId, SystemId),
87
88    /// If a worker panics, the worker must notify it.
89    Panic(PanicMessage),
90}
91
92impl fmt::Debug for Message {
93    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94        match self {
95            Self::Handle(wid) => write!(f, "Message::Handle({wid:?})"),
96            Self::Fin(wid, sid) => write!(f, "Message::Fin({wid:?}, {sid:?})"),
97            Self::Aborted(wid, sid) => write!(f, "Message::Aborted({wid:?}, {sid:?})"),
98            Self::Panic(msg) => write!(f, "Message::Panic({msg:?})"),
99        }
100    }
101}
102
103pub(crate) struct PanicMessage {
104    pub(crate) wid: WorkerId,
105    pub(crate) sid: SystemId,
106    pub(crate) payload: Box<dyn Any + Send>,
107    pub(crate) unrecoverable: bool,
108}
109
110impl fmt::Debug for PanicMessage {
111    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112        f.debug_struct("PanicMessage")
113            .field("wid", &self.wid)
114            .field("sid", &self.sid)
115            .field("unrecoverable", &self.unrecoverable)
116            .finish_non_exhaustive()
117    }
118}