1 use std::fmt;
2 
3 /// Tracks worker state
4 #[derive(Clone, Copy, Eq, PartialEq)]
5 pub(crate) struct State(usize);
6 
7 /// Set when the worker is pushed onto the scheduler's stack of sleeping
8 /// threads.
9 pub(crate) const PUSHED_MASK: usize = 0b001;
10 
11 /// Manages the worker lifecycle part of the state
12 const LIFECYCLE_MASK: usize = 0b1110;
13 const LIFECYCLE_SHIFT: usize = 1;
14 
15 #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Copy)]
16 #[repr(usize)]
17 pub(crate) enum Lifecycle {
18     /// The worker does not currently have an associated thread.
19     Shutdown = 0 << LIFECYCLE_SHIFT,
20 
21     /// The worker is doing work
22     Running = 1 << LIFECYCLE_SHIFT,
23 
24     /// The worker is currently asleep in the condvar
25     Sleeping = 2 << LIFECYCLE_SHIFT,
26 
27     /// The worker has been notified it should process more work.
28     Notified = 3 << LIFECYCLE_SHIFT,
29 
30     /// A stronger form of notification. In this case, the worker is expected to
31     /// wakeup and try to acquire more work... if it enters this state while
32     /// already busy with other work, it is expected to signal another worker.
33     Signaled = 4 << LIFECYCLE_SHIFT,
34 }
35 
36 impl State {
37     /// Returns true if the worker entry is pushed in the sleeper stack
is_pushed(&self) -> bool38     pub fn is_pushed(&self) -> bool {
39         self.0 & PUSHED_MASK == PUSHED_MASK
40     }
41 
set_pushed(&mut self)42     pub fn set_pushed(&mut self) {
43         self.0 |= PUSHED_MASK
44     }
45 
is_notified(&self) -> bool46     pub fn is_notified(&self) -> bool {
47         use self::Lifecycle::*;
48 
49         match self.lifecycle() {
50             Notified | Signaled => true,
51             _ => false,
52         }
53     }
54 
lifecycle(&self) -> Lifecycle55     pub fn lifecycle(&self) -> Lifecycle {
56         Lifecycle::from(self.0 & LIFECYCLE_MASK)
57     }
58 
set_lifecycle(&mut self, val: Lifecycle)59     pub fn set_lifecycle(&mut self, val: Lifecycle) {
60         self.0 = (self.0 & !LIFECYCLE_MASK) | (val as usize)
61     }
62 
is_signaled(&self) -> bool63     pub fn is_signaled(&self) -> bool {
64         self.lifecycle() == Lifecycle::Signaled
65     }
66 
notify(&mut self)67     pub fn notify(&mut self) {
68         use self::Lifecycle::Signaled;
69 
70         if self.lifecycle() != Signaled {
71             self.set_lifecycle(Signaled)
72         }
73     }
74 }
75 
76 impl Default for State {
default() -> State77     fn default() -> State {
78         // All workers will start pushed in the sleeping stack
79         State(PUSHED_MASK)
80     }
81 }
82 
83 impl From<usize> for State {
from(src: usize) -> Self84     fn from(src: usize) -> Self {
85         State(src)
86     }
87 }
88 
89 impl From<State> for usize {
from(src: State) -> Self90     fn from(src: State) -> Self {
91         src.0
92     }
93 }
94 
95 impl fmt::Debug for State {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result96     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
97         fmt.debug_struct("worker::State")
98             .field("lifecycle", &self.lifecycle())
99             .field("is_pushed", &self.is_pushed())
100             .finish()
101     }
102 }
103 
104 // ===== impl Lifecycle =====
105 
106 impl From<usize> for Lifecycle {
from(src: usize) -> Lifecycle107     fn from(src: usize) -> Lifecycle {
108         use self::Lifecycle::*;
109 
110         debug_assert!(
111             src == Shutdown as usize
112                 || src == Running as usize
113                 || src == Sleeping as usize
114                 || src == Notified as usize
115                 || src == Signaled as usize
116         );
117 
118         unsafe { ::std::mem::transmute(src) }
119     }
120 }
121 
122 impl From<Lifecycle> for usize {
from(src: Lifecycle) -> usize123     fn from(src: Lifecycle) -> usize {
124         let v = src as usize;
125         debug_assert!(v & LIFECYCLE_MASK == v);
126         v
127     }
128 }
129 
130 #[cfg(test)]
131 mod test {
132     use super::Lifecycle::*;
133     use super::*;
134 
135     #[test]
lifecycle_encode()136     fn lifecycle_encode() {
137         let lifecycles = &[Shutdown, Running, Sleeping, Notified, Signaled];
138 
139         for &lifecycle in lifecycles {
140             let mut v: usize = lifecycle.into();
141             v &= LIFECYCLE_MASK;
142 
143             assert_eq!(lifecycle, Lifecycle::from(v));
144         }
145     }
146 
147     #[test]
lifecycle_ord()148     fn lifecycle_ord() {
149         assert!(Running >= Shutdown);
150         assert!(Signaled >= Notified);
151         assert!(Signaled >= Sleeping);
152     }
153 }
154