1use std::sync::{Arc, atomic::AtomicUsize, atomic::Ordering};
3use std::task::{Context, Poll};
4use std::{any::Any, fmt, future::Future, panic, pin::Pin, thread, time::Duration};
5
6use crossbeam_channel::{Receiver, Select, Sender, TrySendError, bounded, unbounded};
7
8pub fn spawn_blocking<F, R>(f: F) -> BlockingResult<R>
17where
18 F: FnOnce() -> R + Send + 'static,
19 R: Send + 'static,
20{
21 if let Some(sys) = crate::System::try_current() {
22 sys.spawn_blocking(f)
23 } else {
24 ThreadPool::execute_inplace(f)
25 }
26}
27
28#[derive(Copy, Clone, Debug, PartialEq, Eq)]
32pub struct BlockingError;
33
34impl std::error::Error for BlockingError {}
35
36impl fmt::Display for BlockingError {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 "All threads are busy".fmt(f)
39 }
40}
41
42#[derive(Debug)]
44pub struct BlockingResult<T> {
45 rx: oneshot::AsyncReceiver<Result<T, Box<dyn Any + Send>>>,
46}
47
48impl<T: 'static> BlockingResult<T> {
49 pub fn detach(self) {
51 crate::spawn(async move {
52 let _ = self.await;
53 })
54 .detach();
55 }
56}
57
58type BoxedDispatchable = Box<dyn Dispatchable + Send>;
59
60pub(crate) trait Dispatchable: Send + 'static {
61 fn run(self: Box<Self>);
62}
63
64impl<F> Dispatchable for F
65where
66 F: FnOnce() + Send + 'static,
67{
68 fn run(self: Box<Self>) {
69 (*self)();
70 }
71}
72
73struct CounterGuard(Arc<AtomicUsize>);
74
75impl Drop for CounterGuard {
76 fn drop(&mut self) {
77 self.0.fetch_sub(1, Ordering::AcqRel);
78 }
79}
80
81fn worker(
82 receiver_high_prio: Receiver<BoxedDispatchable>,
83 receiver_low_prio: Receiver<BoxedDispatchable>,
84 counter: Arc<AtomicUsize>,
85 timeout: Duration,
86) -> impl FnOnce() {
87 move || {
88 counter.fetch_add(1, Ordering::AcqRel);
89 let _guard = CounterGuard(counter);
90 let mut sel = Select::new_biased();
91 sel.recv(&receiver_high_prio);
92 sel.recv(&receiver_low_prio);
93 while let Ok(op) = sel.select_timeout(timeout) {
94 match op {
95 op if op.index() == 0 => {
96 if let Ok(f) = op.recv(&receiver_high_prio) {
97 f.run();
98 }
99 }
100 op if op.index() == 1 => {
101 if let Ok(f) = op.recv(&receiver_low_prio) {
102 f.run();
103 }
104 }
105 _ => unreachable!(),
106 }
107 }
108 }
109}
110
111#[derive(Debug, Clone)]
124pub struct ThreadPool {
125 name: String,
126 sender_low_prio: Sender<BoxedDispatchable>,
127 receiver_low_prio: Receiver<BoxedDispatchable>,
128 sender_high_prio: Sender<BoxedDispatchable>,
129 receiver_high_prio: Receiver<BoxedDispatchable>,
130 counter: Arc<AtomicUsize>,
131 thread_limit: usize,
132 recv_timeout: Duration,
133}
134
135impl ThreadPool {
136 pub fn new(name: &str, thread_limit: usize, recv_timeout: Duration) -> Self {
139 let (sender_low_prio, receiver_low_prio) = bounded(0);
140 let (sender_high_prio, receiver_high_prio) = unbounded();
141 Self {
142 sender_low_prio,
143 receiver_low_prio,
144 sender_high_prio,
145 receiver_high_prio,
146 thread_limit,
147 recv_timeout,
148 name: format!("{name}:pool-wrk"),
149 counter: Arc::new(AtomicUsize::new(0)),
150 }
151 }
152
153 pub(crate) fn execute_inplace<F, R>(f: F) -> BlockingResult<R>
154 where
155 F: FnOnce() -> R + Send + 'static,
156 R: Send + 'static,
157 {
158 let (tx, rx) = oneshot::async_channel();
159 let result = panic::catch_unwind(panic::AssertUnwindSafe(f));
160 let _ = tx.send(result);
161 BlockingResult { rx }
162 }
163
164 #[allow(clippy::missing_panics_doc)]
165 pub fn execute<F, R>(&self, f: F) -> BlockingResult<R>
171 where
172 F: FnOnce() -> R + Send + 'static,
173 R: Send + 'static,
174 {
175 let (tx, rx) = oneshot::async_channel();
176 let f = Box::new(move || {
177 if !tx.is_closed() {
179 let result = panic::catch_unwind(panic::AssertUnwindSafe(f));
180 let _ = tx.send(result);
181 }
182 });
183
184 match self.sender_low_prio.try_send(f) {
185 Ok(()) => BlockingResult { rx },
186 Err(e) => match e {
187 TrySendError::Full(f) => {
188 let cnt = self.counter.load(Ordering::Acquire);
189 if cnt >= self.thread_limit {
190 self.sender_high_prio
191 .send(f)
192 .expect("the channel should not be full");
193 BlockingResult { rx }
194 } else {
195 thread::Builder::new()
196 .name(format!("{}:{}", self.name, cnt))
197 .spawn(worker(
198 self.receiver_high_prio.clone(),
199 self.receiver_low_prio.clone(),
200 self.counter.clone(),
201 self.recv_timeout,
202 ))
203 .expect("Cannot construct new thread");
204 self.sender_low_prio
205 .send(f)
206 .expect("the channel should not be full");
207 BlockingResult { rx }
208 }
209 }
210 TrySendError::Disconnected(_) => {
211 unreachable!("receiver should not all disconnected")
212 }
213 },
214 }
215 }
216}
217
218impl<R> Future for BlockingResult<R> {
219 type Output = Result<R, BlockingError>;
220
221 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
222 let this = self.get_mut();
223
224 match Pin::new(&mut this.rx).poll(cx) {
225 Poll::Pending => Poll::Pending,
226 Poll::Ready(result) => Poll::Ready(
227 result
228 .map_err(|_| BlockingError)
229 .and_then(|res| res.map_err(|_| BlockingError)),
230 ),
231 }
232 }
233}