tokio/runtime/scheduler/multi_thread/
idle.rs1use crate::loom::sync::atomic::AtomicUsize;
4use crate::runtime::scheduler::multi_thread::Shared;
5
6use std::fmt;
7use std::sync::atomic::Ordering::{self, SeqCst};
8
9pub(super) struct Idle {
10 state: AtomicUsize,
15
16 num_workers: usize,
18}
19
20pub(super) struct Synced {
22 sleepers: Vec<usize>,
24}
25
26const UNPARK_SHIFT: usize = 16;
27const UNPARK_MASK: usize = !SEARCH_MASK;
28const SEARCH_MASK: usize = (1 << UNPARK_SHIFT) - 1;
29
30#[derive(Copy, Clone)]
31struct State(usize);
32
33impl Idle {
34 pub(super) fn new(num_workers: usize) -> (Idle, Synced) {
35 let init = State::new(num_workers);
36
37 let idle = Idle {
38 state: AtomicUsize::new(init.into()),
39 num_workers,
40 };
41
42 let synced = Synced {
43 sleepers: Vec::with_capacity(num_workers),
44 };
45
46 (idle, synced)
47 }
48
49 pub(super) fn worker_to_notify(&self, shared: &Shared) -> Option<usize> {
52 if !self.notify_should_wakeup() {
62 return None;
63 }
64
65 let mut lock = shared.synced.lock();
67
68 if !self.notify_should_wakeup() {
70 return None;
71 }
72
73 State::unpark_one(&self.state, 1);
76
77 let ret = lock.idle.sleepers.pop();
79 debug_assert!(ret.is_some());
80
81 ret
82 }
83
84 pub(super) fn transition_worker_to_parked(
87 &self,
88 shared: &Shared,
89 worker: usize,
90 is_searching: bool,
91 ) -> bool {
92 let mut lock = shared.synced.lock();
94
95 debug_assert!(!lock.idle.sleepers.contains(&worker));
98
99 let ret = State::dec_num_unparked(&self.state, is_searching);
101
102 lock.idle.sleepers.push(worker);
104
105 ret
106 }
107
108 pub(super) fn transition_worker_to_searching(&self) -> bool {
109 let state = State::load(&self.state, SeqCst);
110 if 2 * state.num_searching() >= self.num_workers {
111 return false;
112 }
113
114 State::inc_num_searching(&self.state, SeqCst);
118 true
119 }
120
121 pub(super) fn transition_worker_from_searching(&self) -> bool {
126 State::dec_num_searching(&self.state)
127 }
128
129 pub(super) fn unpark_worker_by_id(&self, shared: &Shared, worker_id: usize) -> bool {
134 let mut lock = shared.synced.lock();
135 let sleepers = &mut lock.idle.sleepers;
136
137 for index in 0..sleepers.len() {
138 if sleepers[index] == worker_id {
139 sleepers.swap_remove(index);
140
141 State::unpark_one(&self.state, 0);
143
144 return true;
145 }
146 }
147
148 false
149 }
150
151 pub(super) fn is_parked(&self, shared: &Shared, worker_id: usize) -> bool {
153 let lock = shared.synced.lock();
154 lock.idle.sleepers.contains(&worker_id)
155 }
156
157 fn notify_should_wakeup(&self) -> bool {
158 let state = State(self.state.fetch_add(0, SeqCst));
162 state.num_searching() == 0 && state.num_unparked() < self.num_workers
163 }
164}
165
166impl State {
167 fn new(num_workers: usize) -> State {
168 let ret = State(num_workers << UNPARK_SHIFT);
170 debug_assert_eq!(num_workers, ret.num_unparked());
171 debug_assert_eq!(0, ret.num_searching());
172 ret
173 }
174
175 fn load(cell: &AtomicUsize, ordering: Ordering) -> State {
176 State(cell.load(ordering))
177 }
178
179 fn unpark_one(cell: &AtomicUsize, num_searching: usize) {
180 cell.fetch_add(num_searching | (1 << UNPARK_SHIFT), SeqCst);
181 }
182
183 fn inc_num_searching(cell: &AtomicUsize, ordering: Ordering) {
184 cell.fetch_add(1, ordering);
185 }
186
187 fn dec_num_searching(cell: &AtomicUsize) -> bool {
189 let state = State(cell.fetch_sub(1, SeqCst));
190 state.num_searching() == 1
191 }
192
193 fn dec_num_unparked(cell: &AtomicUsize, is_searching: bool) -> bool {
197 let mut dec = 1 << UNPARK_SHIFT;
198
199 if is_searching {
200 dec += 1;
201 }
202
203 let prev = State(cell.fetch_sub(dec, SeqCst));
204 debug_assert!(prev.num_unparked() > 0, "{prev:?}");
205 is_searching && prev.num_searching() == 1
206 }
207
208 fn num_searching(self) -> usize {
210 self.0 & SEARCH_MASK
211 }
212
213 fn num_unparked(self) -> usize {
215 (self.0 & UNPARK_MASK) >> UNPARK_SHIFT
216 }
217}
218
219impl From<usize> for State {
220 fn from(src: usize) -> State {
221 State(src)
222 }
223}
224
225impl From<State> for usize {
226 fn from(src: State) -> usize {
227 src.0
228 }
229}
230
231impl fmt::Debug for State {
232 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
233 fmt.debug_struct("worker::State")
234 .field("num_unparked", &self.num_unparked())
235 .field("num_searching", &self.num_searching())
236 .finish()
237 }
238}
239
240#[test]
241fn test_state() {
242 assert_eq!(0, UNPARK_MASK & SEARCH_MASK);
243 assert_eq!(0, !(UNPARK_MASK | SEARCH_MASK));
244
245 let state = State::new(10);
246 assert_eq!(10, state.num_unparked());
247 assert_eq!(0, state.num_searching());
248}