turbopack_trace_utils/
tokio_workers.rs1use std::sync::atomic::{AtomicUsize, Ordering};
2
3static ACTIVE_WORKER_THREADS: AtomicUsize = AtomicUsize::new(0);
4
5pub fn set_worker_threads(worker_threads: usize) {
12 ACTIVE_WORKER_THREADS.store(worker_threads, Ordering::Relaxed);
13}
14
15pub fn park() {
16 ACTIVE_WORKER_THREADS.fetch_sub(1, Ordering::Relaxed);
17}
18
19pub fn unpark() {
20 ACTIVE_WORKER_THREADS.fetch_add(1, Ordering::Relaxed);
21}
22
23pub fn active_worker_threads() -> usize {
24 ACTIVE_WORKER_THREADS.load(Ordering::Relaxed)
25}
26
27pub fn default_worker_threads() -> usize {
29 match std::env::var("TOKIO_WORKER_THREADS") {
30 Ok(value) => {
31 let count: usize = value.parse().expect("TOKIO_WORKER_THREADS must be usize");
32 assert!(count > 0, "TOKIO_WORKER_THREADS cannot be set to 0");
33 count
34 }
35 Err(std::env::VarError::NotPresent) => {
36 std::thread::available_parallelism().map_or(1, |n| n.get())
37 }
38 Err(error) => panic!("TOKIO_WORKER_THREADS must be valid unicode: {error}"),
39 }
40}
41
42#[cfg(test)]
43mod tests {
44 use std::{
45 sync::{Arc, Barrier, mpsc},
46 time::{Duration, Instant},
47 };
48
49 use super::*;
50
51 const TIMEOUT: Duration = Duration::from_secs(10);
54
55 #[track_caller]
59 fn wait_for_active_worker_threads(expected: usize) {
60 let deadline = Instant::now() + TIMEOUT;
61 loop {
62 let active = active_worker_threads();
63 if active == expected {
64 return;
65 }
66 assert!(
67 Instant::now() < deadline,
68 "expected {expected} active worker thread(s), found {active}"
69 );
70 std::thread::sleep(Duration::from_millis(1));
73 }
74 }
75
76 #[test]
80 fn counts_scheduler_workers_but_not_blocking_threads() {
81 set_worker_threads(2);
86 assert_eq!(active_worker_threads(), 2);
87 park();
88 assert_eq!(active_worker_threads(), 1);
89 unpark();
90 assert_eq!(active_worker_threads(), 2);
91
92 let mut builder = tokio::runtime::Builder::new_multi_thread();
93 builder
94 .worker_threads(2)
95 .on_thread_park(park)
96 .on_thread_unpark(unpark);
97 let rt = builder.build().unwrap();
98
99 wait_for_active_worker_threads(0);
101
102 let (blocking_tx, blocking_rx) = mpsc::channel();
105 rt.spawn_blocking(move || blocking_tx.send(active_worker_threads()).unwrap());
106 assert_eq!(blocking_rx.recv_timeout(TIMEOUT).unwrap(), 0);
107
108 let barrier = Arc::new(Barrier::new(2));
112 let worker_barrier = barrier.clone();
113 let (started_tx, started_rx) = mpsc::channel();
114 rt.spawn(async move {
115 started_tx.send(()).unwrap();
116 worker_barrier.wait();
117 });
118 started_rx.recv_timeout(TIMEOUT).unwrap();
119 assert!(active_worker_threads() > 0);
120 barrier.wait();
121
122 wait_for_active_worker_threads(0);
125 }
126}