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