1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
//! # Constants
//! Constants used throughout the crate
use Duration;
/// Defines the crossover between pausing the thread
/// when sleeping or starting a syscall
pub const SLEEP_TOLERANCE: Duration = from_micros;
/// Amount of kevents that can be processed
/// by a single `kevent`
pub const KEVENT_COUNT: usize = 16;
/// Delay before the first restart of a dead `Reactor`,
/// multiplied by the number of consecutive failures
pub const RESTART_BACKOFF: Duration = from_millis;
/// How long a `Reactor` has to survive before its
/// restart is treated as a one off rather than a cycle
pub const RESTART_WINDOW: Duration = from_secs;
/// Consecutive restarts inside `RESTART_WINDOW`
/// before the supervisor stops trying
pub const RESTART_LIMIT: u32 = 5;
/// Stored in place of the kqueue id once no
/// `Reactor` is listening on one
pub const DEAD_KQUEUE_ID: i32 = -1;
/// Bytes between the start of a task's slot and its payload
///
/// Also the budget for a slot's header, which `TaskData::init`
/// asserts. Aligns the payload for anything up to 128 byte
/// alignment
pub const PAYLOAD_OFFSET: usize = 128;
/// Bytes one task slot takes up in a table block
///
/// A cache line of its own, so two tasks never share the word
/// their listeners are blocked on
pub const SLOT_SIZE: usize = 256;
/// Bytes of output a slot can hold beside its header
///
/// Anything larger gets a mapping of its own, which costs a
/// whole page
pub const INLINE_PAYLOAD: usize = SLOT_SIZE - PAYLOAD_OFFSET;
/// Slots in the first block of the task table
pub const FIRST_BLOCK: usize = 256;
/// `FIRST_BLOCK` as a power of two
pub const FIRST_BLOCK_LOG2: u32 = FIRST_BLOCK.trailing_zeros;
/// Blocks in the task table, each twice the size of the last
///
/// Capped by the `u32` free list and queue links, which need
/// an id plus one to fit in 32 bits
pub const TABLE_BLOCKS: usize = 23;
/// Bits of a tagged stack head given over to the index,
/// leaving the rest for the tag that defeats ABA
pub const TAG_SHIFT: u32 = 48;
/// Picks the index back out of a tagged stack head
pub const INDEX_MASK: usize = - 1;
/// One past the highest id the task table can address
///
/// The id a handle gets when there was nowhere to put its task
pub const MAX_TASK_ID: usize = - FIRST_BLOCK;
/// The id a handle gets when there was no runtime running to take
/// its task
///
/// Past every real id, the same as `MAX_TASK_ID`
pub const UNSTARTED_TASK_ID: usize = MAX_TASK_ID + 1;
/// The `kevent` ident the `Reactor` wakes a waiting thread on
///
/// One ident covers every wake, since a thread only waits on
/// one thing at a time
pub const WAKE_IDENT: usize = 0;
/// Task ids one worker can hold in its own ring
///
/// A power of two so the ring indexes with a mask
pub const LOCAL_QUEUE: usize = 256;
/// Picks a ring position out of a head or tail counter
pub const LOCAL_QUEUE_MASK: u32 = as u32;
/// Slots in the pool's static arrays, for workers and sleep
/// threads alike
///
/// The hard bound on either kind of thread
pub const MAX_WORKERS: usize = 4096;
/// Live workers the pool settles around under load, per core
///
/// A target rather than a limit: the pool passes it on overload or
/// real need
pub const WORKER_MULTIPLIER: usize = 4;
/// Sleep threads the pool settles around under load, per core
///
/// A target rather than a limit, the same as `WORKER_MULTIPLIER`
pub const SLEEP_MULTIPLIER: usize = 8;
/// How often the manager wakes to do policy on its own
pub const MANAGER_TICK: Duration = from_millis;
/// How often a shutdown looks to see whether the pool has
/// finished draining, and an `init` whether a shutdown has
pub const SHUTDOWN_POLL: Duration = from_millis;
/// How long a worker sits idle before it is reaped
pub const IDLE_REAP: Duration = from_millis;
/// How long a worker or sleep thread past its target sits idle
/// before it is reaped
pub const IDLE_REAP_OVER: Duration = from_millis;
/// Tasks waiting for each running thread before the pool counts as
/// overloaded past its target
pub const OVERLOAD_RATIO: usize = 64;
/// Threads held back from what the kernel lets a process run, for
/// everything that isn't the pool: the manager, the reactor, and the
/// program's own threads
pub const THREAD_RESERVE: usize = 256;
/// How many runs deep a worker helps while a task it is running
/// waits on another
///
/// Past this it waits instead, and a new worker takes the queued work
pub const HELP_DEPTH: usize = 64;
/// Tasks a worker takes from its LIFO slot in a row before it gives
/// the queue a turn
pub const LIFO_STREAK: u32 = 3;
/// The longest a worker waiting inside a task sleeps before looking
/// for queued work to help with again
///
/// It starts far shorter and backs off to this
pub const HELP_POLL: Duration = from_millis;
/// How long a wait sleeps at a time while the manager is gone, before
/// making sure no dead thread has stranded what it waits on
pub const STRANDED_POLL: Duration = from_secs;
/// Stack reserved for each worker thread
pub const WORKER_STACK: usize = 8 * 1024 * 1024;
/// The smallest stack a worker thread can be given
pub const MIN_WORKER_STACK: usize = 16 * 1024;
/// How much of a file or a pipe one read or write
/// syscall asks for
///
/// Cancels land between chunks, so this is also how long a
/// cancelled file task can keep running
pub const FILE_CHUNK: usize = 64 * 1024;
/// How long a process task waits before looking at
/// a child again, when it has no queue to wait on
pub const PROCESS_POLL: Duration = from_millis;
/// How often a lock another file holds is asked for again
pub const LOCK_POLL: Duration = from_millis;
/// Tasks that may overtake a queued one before it counts
/// as starving
pub const STARVE_AGE: u64 = 1_000_000;
/// Pops a starving queue is served oldest first for, each
/// time the manager finds it starving
pub const STARVE_RELIEF: u32 = 64;
/// Priority bands the injector serves, highest first
pub const PRIORITY_BANDS: usize = 4;
/// Turns a priority class into the band that serves it
pub const PRIORITY_BAND_SHIFT: u32 = 6;
/// Bits of a packed priority given over to the class,
/// leaving the rest for the sequence
pub const PRIORITY_CLASS_SHIFT: u32 = 56;
/// Picks the sequence back out of a packed priority
pub const PRIORITY_SEQUENCE_MASK: u64 = - 1;
/// The class a task gets when the caller doesn't pick one
pub const DEFAULT_PRIORITY: u8 = 128;
/// Slots kept spare above the live count when trimming
pub const TRIM_THRESHOLD: usize = 4096;
/// The most of itself a table may keep when trimming, as a
/// percentage
pub const TRIM_KEEP_PERCENT: usize = 80;
/// Slots a table keeps whatever else happens
pub const TRIM_MINIMUM: usize = 100;
/// Manager ticks between attempts to trim the table
pub const TRIM_INTERVAL: u32 = 500;
/// Stored in a slot's waiting field when its task isn't
/// sitting in a kernel wait
pub const NOT_WAITING: i32 = -1;
/// The `waiting` value for a slot nobody is selecting on
pub const NO_SELECT: i32 = -1;
/// The ident a `join_first` notification arrives under
pub const SELECT_IDENT: usize = 1;
/// How long a `join_first` waits before looking again anyway
pub const SELECT_POLL: Duration = from_millis;
/// How long a signal task waits before looking at its count again
/// anyway
///
/// Bounds the wait for a delivery that landed before the watch
/// went on
pub const SIGNAL_POLL: Duration = from_millis;
/// How long a watch waits before looking at the path again
/// anyway
///
/// Bounds the wait for a change that landed before the watch went
/// on
pub const VNODE_POLL: Duration = from_millis;
/// Stored in a slot's waiting field while a canceller is part
/// way through interrupting it
///
/// Stops a cancel landing on a descriptor that has already
/// been closed and reused
pub const CANCELLING: i32 = i32MIN;
/// Stored in place of a task id when a worker isn't on one
pub const NO_TASK: usize = usizeMAX;
/// The first `kevent` ident a scheduled task may use on the
/// manager's queue
///
/// Keeps task timers clear of `MANAGER_TICK_IDENT`
pub const SCHEDULE_IDENT_BASE: usize = 2;
/// The first `kevent` ident a run's timeout may use on the
/// manager's queue
///
/// Past every schedule ident, so the two never meet
pub const TIMEOUT_IDENT_BASE: usize = SCHEDULE_IDENT_BASE + MAX_TASK_ID + 1;
/// The `kevent` ident the manager's own tick arrives on
pub const MANAGER_TICK_IDENT: usize = 1;
/// Makes a `kevent` registration unique to its user data as
/// well as its ident and filter
///
/// Lets two tasks park on one descriptor without replacing
/// each other's watch
pub const EV_UDATA_SPECIFIC: u16 = 0x0100;
/// The user data a parked task's deadline timer carries, which
/// tells it apart from a delay or a gap at the same ident
pub const PARK_TIMER: usize = 1;
/// Bytes one step of a socket task moves before it parks and
/// lets the rest of the pool have the thread
pub const STEP_BUDGET: usize = 1024 * 1024;
/// How long a blocking socket call waits before looking again,
/// when its thread has no queue to wait on
pub const READY_POLL: Duration = from_millis;