forked from mirror/async-std
Restore task::spawn_blocking
This commit is contained in:
parent
84e5c5f351
commit
98cbf7f8eb
1 changed files with 85 additions and 2 deletions
|
@ -1,4 +1,12 @@
|
|||
use crate::task::{spawn, JoinHandle};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use crossbeam_channel::{unbounded, Receiver, Sender};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::task::{JoinHandle, Task};
|
||||
use crate::utils::abort_on_panic;
|
||||
|
||||
/// Spawns a blocking task.
|
||||
///
|
||||
|
@ -35,5 +43,80 @@ where
|
|||
F: FnOnce() -> T + Send + 'static,
|
||||
T: Send + 'static,
|
||||
{
|
||||
spawn(async { f() })
|
||||
let schedule = |task| POOL.sender.send(task).unwrap();
|
||||
let (task, handle) = async_task::spawn(async { f() }, schedule, Task::new(None));
|
||||
task.schedule();
|
||||
JoinHandle::new(handle)
|
||||
}
|
||||
|
||||
type Runnable = async_task::Task<Task>;
|
||||
|
||||
struct Pool {
|
||||
sender: Sender<Runnable>,
|
||||
receiver: Receiver<Runnable>,
|
||||
}
|
||||
|
||||
/// The number of sleeping worker threads.
|
||||
static SLEEPING: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
static POOL: Lazy<Pool> = Lazy::new(|| {
|
||||
// Start a single worker thread waiting for the first task.
|
||||
start_thread();
|
||||
|
||||
let (sender, receiver) = unbounded();
|
||||
Pool { sender, receiver }
|
||||
});
|
||||
|
||||
fn start_thread() {
|
||||
SLEEPING.fetch_add(1, Ordering::SeqCst);
|
||||
let timeout = Duration::from_secs(1);
|
||||
|
||||
thread::Builder::new()
|
||||
.name("async-std/blocking".to_string())
|
||||
.spawn(move || {
|
||||
loop {
|
||||
let mut task = match POOL.receiver.recv_timeout(timeout) {
|
||||
Ok(task) => task,
|
||||
Err(_) => {
|
||||
// Check whether this is the last sleeping thread.
|
||||
if SLEEPING.fetch_sub(1, Ordering::SeqCst) == 1 {
|
||||
// If so, then restart the thread to make sure there is always at least
|
||||
// one sleeping thread.
|
||||
if SLEEPING.compare_and_swap(0, 1, Ordering::SeqCst) == 0 {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Stop the thread.
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// If there are no sleeping threads, then start one to make sure there is always at
|
||||
// least one sleeping thread.
|
||||
if SLEEPING.fetch_sub(1, Ordering::SeqCst) == 1 {
|
||||
start_thread();
|
||||
}
|
||||
|
||||
loop {
|
||||
// Run the task.
|
||||
abort_on_panic(|| task.run());
|
||||
|
||||
// Try taking another task if there are any available.
|
||||
task = match POOL.receiver.try_recv() {
|
||||
Ok(task) => task,
|
||||
Err(_) => break,
|
||||
};
|
||||
}
|
||||
|
||||
// If there is at least one sleeping thread, stop this thread instead of putting it
|
||||
// to sleep.
|
||||
if SLEEPING.load(Ordering::SeqCst) > 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
SLEEPING.fetch_add(1, Ordering::SeqCst);
|
||||
}
|
||||
})
|
||||
.expect("cannot start a blocking thread");
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue