2018-04-03 22:35:59 -07:00
|
|
|
mod state;
|
2018-04-04 13:30:54 -07:00
|
|
|
mod stack;
|
2018-04-03 22:35:59 -07:00
|
|
|
|
|
|
|
|
pub(crate) use self::state::{
|
2018-04-04 13:30:54 -07:00
|
|
|
State,
|
|
|
|
|
Lifecycle,
|
2018-04-03 22:35:59 -07:00
|
|
|
MAX_FUTURES,
|
|
|
|
|
};
|
2018-04-04 13:30:54 -07:00
|
|
|
use self::stack::SleepStack;
|
2018-04-03 22:35:59 -07:00
|
|
|
|
2018-04-04 13:30:54 -07:00
|
|
|
use config::Config;
|
2018-03-28 01:56:21 +03:00
|
|
|
use shutdown_task::ShutdownTask;
|
|
|
|
|
use task::Task;
|
2018-04-04 13:30:54 -07:00
|
|
|
use worker::{self, Worker, WorkerId};
|
2018-04-03 22:35:59 -07:00
|
|
|
|
|
|
|
|
use futures::task::AtomicTask;
|
2018-03-28 01:56:21 +03:00
|
|
|
|
|
|
|
|
use std::cell::UnsafeCell;
|
2018-04-04 13:30:54 -07:00
|
|
|
use std::sync::atomic::Ordering::{Acquire, AcqRel, Relaxed};
|
2018-03-28 01:56:21 +03:00
|
|
|
use std::sync::atomic::AtomicUsize;
|
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
|
|
|
|
use rand::{Rng, SeedableRng, XorShiftRng};
|
|
|
|
|
|
2018-04-03 22:35:59 -07:00
|
|
|
// TODO: Rename this
|
2018-03-28 01:56:21 +03:00
|
|
|
#[derive(Debug)]
|
2018-04-04 13:30:54 -07:00
|
|
|
pub(crate) struct Pool {
|
2018-03-28 01:56:21 +03:00
|
|
|
// ThreadPool state
|
|
|
|
|
pub state: AtomicUsize,
|
|
|
|
|
|
|
|
|
|
// Stack tracking sleeping workers.
|
2018-04-04 13:30:54 -07:00
|
|
|
sleep_stack: SleepStack,
|
2018-03-28 01:56:21 +03:00
|
|
|
|
|
|
|
|
// Number of workers who haven't reached the final state of shutdown
|
|
|
|
|
//
|
|
|
|
|
// This is only used to know when to single `shutdown_task` once the
|
|
|
|
|
// shutdown process has completed.
|
|
|
|
|
pub num_workers: AtomicUsize,
|
|
|
|
|
|
|
|
|
|
// Used to generate a thread local RNG seed
|
|
|
|
|
pub next_thread_id: AtomicUsize,
|
|
|
|
|
|
|
|
|
|
// Storage for workers
|
|
|
|
|
//
|
|
|
|
|
// This will *usually* be a small number
|
2018-04-03 22:35:59 -07:00
|
|
|
pub workers: Box<[worker::Entry]>,
|
2018-03-28 01:56:21 +03:00
|
|
|
|
|
|
|
|
// Task notified when the worker shuts down
|
|
|
|
|
pub shutdown_task: ShutdownTask,
|
|
|
|
|
|
|
|
|
|
// Configuration
|
|
|
|
|
pub config: Config,
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-04 13:30:54 -07:00
|
|
|
impl Pool {
|
|
|
|
|
/// Create a new `Pool`
|
|
|
|
|
pub fn new(workers: Box<[worker::Entry]>, config: Config) -> Pool {
|
2018-04-03 22:35:59 -07:00
|
|
|
let pool_size = workers.len();
|
|
|
|
|
|
2018-04-04 13:30:54 -07:00
|
|
|
let ret = Pool {
|
|
|
|
|
state: AtomicUsize::new(State::new().into()),
|
|
|
|
|
sleep_stack: SleepStack::new(),
|
2018-04-03 22:35:59 -07:00
|
|
|
num_workers: AtomicUsize::new(pool_size),
|
|
|
|
|
next_thread_id: AtomicUsize::new(0),
|
|
|
|
|
workers,
|
|
|
|
|
shutdown_task: ShutdownTask {
|
|
|
|
|
task1: AtomicTask::new(),
|
|
|
|
|
#[cfg(feature = "unstable-futures")]
|
|
|
|
|
task2: futures2::task::AtomicWaker::new(),
|
|
|
|
|
},
|
|
|
|
|
config,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Now, we prime the sleeper stack
|
|
|
|
|
for i in 0..pool_size {
|
2018-04-04 13:30:54 -07:00
|
|
|
ret.sleep_stack.push(&ret.workers, i).unwrap();
|
2018-04-03 22:35:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-28 01:56:21 +03:00
|
|
|
/// Start shutting down the pool. This means that no new futures will be
|
|
|
|
|
/// accepted.
|
|
|
|
|
pub fn shutdown(&self, now: bool, purge_queue: bool) {
|
2018-04-04 13:30:54 -07:00
|
|
|
let mut state: State = self.state.load(Acquire).into();
|
2018-03-28 01:56:21 +03:00
|
|
|
|
|
|
|
|
trace!("shutdown; state={:?}", state);
|
|
|
|
|
|
|
|
|
|
// For now, this must be true
|
|
|
|
|
debug_assert!(!purge_queue || now);
|
|
|
|
|
|
2018-04-04 13:30:54 -07:00
|
|
|
// Start by setting the shutdown flag
|
2018-03-28 01:56:21 +03:00
|
|
|
loop {
|
|
|
|
|
let mut next = state;
|
|
|
|
|
|
|
|
|
|
let num_futures = next.num_futures();
|
|
|
|
|
|
2018-04-04 13:30:54 -07:00
|
|
|
if next.lifecycle() == Lifecycle::ShutdownNow {
|
2018-03-28 01:56:21 +03:00
|
|
|
// Already transitioned to shutting down state
|
|
|
|
|
|
|
|
|
|
if !purge_queue || num_futures == 0 {
|
|
|
|
|
// Nothing more to do
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The queue must be purged
|
|
|
|
|
debug_assert!(purge_queue);
|
|
|
|
|
next.clear_num_futures();
|
|
|
|
|
} else {
|
|
|
|
|
next.set_lifecycle(if now || num_futures == 0 {
|
|
|
|
|
// If already idle, always transition to shutdown now.
|
2018-04-04 13:30:54 -07:00
|
|
|
Lifecycle::ShutdownNow
|
2018-03-28 01:56:21 +03:00
|
|
|
} else {
|
2018-04-04 13:30:54 -07:00
|
|
|
Lifecycle::ShutdownOnIdle
|
2018-03-28 01:56:21 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if purge_queue {
|
|
|
|
|
next.clear_num_futures();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let actual = self.state.compare_and_swap(
|
|
|
|
|
state.into(), next.into(), AcqRel).into();
|
|
|
|
|
|
|
|
|
|
if state == actual {
|
|
|
|
|
state = next;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
state = actual;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
trace!(" -> transitioned to shutdown");
|
|
|
|
|
|
|
|
|
|
// Only transition to terminate if there are no futures currently on the
|
|
|
|
|
// pool
|
|
|
|
|
if state.num_futures() != 0 {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.terminate_sleeping_workers();
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-04 13:30:54 -07:00
|
|
|
/// Called by `Worker` as it tries to enter a sleeping state. Before it
|
|
|
|
|
/// sleeps, it must push itself onto the sleep stack. This enables other
|
|
|
|
|
/// threads to see it when signaling work.
|
|
|
|
|
pub fn push_sleeper(&self, idx: usize) -> Result<(), ()> {
|
|
|
|
|
self.sleep_stack.push(&self.workers, idx)
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-28 01:56:21 +03:00
|
|
|
pub fn terminate_sleeping_workers(&self) {
|
2018-04-03 22:35:59 -07:00
|
|
|
use worker::Lifecycle::Signaled;
|
|
|
|
|
|
2018-03-28 01:56:21 +03:00
|
|
|
trace!(" -> shutting down workers");
|
|
|
|
|
// Wakeup all sleeping workers. They will wake up, see the state
|
|
|
|
|
// transition, and terminate.
|
2018-04-04 13:30:54 -07:00
|
|
|
while let Some((idx, worker_state)) = self.sleep_stack.pop(&self.workers, Signaled, true) {
|
2018-03-28 01:56:21 +03:00
|
|
|
trace!(" -> shutdown worker; idx={:?}; state={:?}", idx, worker_state);
|
|
|
|
|
|
2018-04-04 13:30:54 -07:00
|
|
|
if self.workers[idx].signal_stop(worker_state).is_err() {
|
|
|
|
|
// The worker is already in the shutdown state, immediately
|
|
|
|
|
// track that it has terminated as the worker will never work
|
|
|
|
|
// again.
|
|
|
|
|
self.worker_terminated();
|
2018-03-28 01:56:21 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn worker_terminated(&self) {
|
|
|
|
|
let prev = self.num_workers.fetch_sub(1, AcqRel);
|
|
|
|
|
|
|
|
|
|
trace!("worker_terminated; num_workers={}", prev - 1);
|
|
|
|
|
|
|
|
|
|
if 1 == prev {
|
|
|
|
|
trace!("notifying shutdown task");
|
|
|
|
|
self.shutdown_task.notify();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Submit a task to the scheduler.
|
|
|
|
|
///
|
|
|
|
|
/// Called from either inside or outside of the scheduler. If currently on
|
|
|
|
|
/// the scheduler, then a fast path is taken.
|
2018-04-05 10:57:05 -07:00
|
|
|
pub fn submit(&self, task: Arc<Task>, inner: &Arc<Pool>) {
|
2018-03-28 01:56:21 +03:00
|
|
|
Worker::with_current(|worker| {
|
|
|
|
|
match worker {
|
|
|
|
|
Some(worker) => {
|
2018-03-30 11:50:02 -07:00
|
|
|
let idx = worker.id.idx;
|
2018-03-28 01:56:21 +03:00
|
|
|
|
|
|
|
|
trace!(" -> submit internal; idx={}", idx);
|
|
|
|
|
|
|
|
|
|
worker.inner.workers[idx].submit_internal(task);
|
|
|
|
|
worker.inner.signal_work(inner);
|
|
|
|
|
}
|
|
|
|
|
None => {
|
|
|
|
|
self.submit_external(task, inner);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Submit a task to the scheduler from off worker
|
|
|
|
|
///
|
|
|
|
|
/// Called from outside of the scheduler, this function is how new tasks
|
|
|
|
|
/// enter the system.
|
2018-04-05 10:57:05 -07:00
|
|
|
fn submit_external(&self, task: Arc<Task>, inner: &Arc<Pool>) {
|
2018-04-03 22:35:59 -07:00
|
|
|
use worker::Lifecycle::Notified;
|
|
|
|
|
|
2018-03-28 01:56:21 +03:00
|
|
|
// First try to get a handle to a sleeping worker. This ensures that
|
|
|
|
|
// sleeping tasks get woken up
|
2018-04-04 13:30:54 -07:00
|
|
|
if let Some((idx, worker_state)) = self.sleep_stack.pop(&self.workers, Notified, false) {
|
|
|
|
|
trace!("submit to existing worker; idx={}; state={:?}", idx, worker_state);
|
|
|
|
|
self.submit_to_external(idx, task, worker_state, inner);
|
2018-03-28 01:56:21 +03:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// All workers are active, so pick a random worker and submit the
|
|
|
|
|
// task to it.
|
|
|
|
|
let len = self.workers.len();
|
|
|
|
|
let idx = self.rand_usize() % len;
|
|
|
|
|
|
|
|
|
|
trace!(" -> submitting to random; idx={}", idx);
|
|
|
|
|
|
2018-04-04 13:30:54 -07:00
|
|
|
let state = self.workers[idx].load_state();
|
2018-03-28 01:56:21 +03:00
|
|
|
self.submit_to_external(idx, task, state, inner);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn submit_to_external(&self,
|
|
|
|
|
idx: usize,
|
2018-04-05 10:57:05 -07:00
|
|
|
task: Arc<Task>,
|
2018-04-04 13:30:54 -07:00
|
|
|
state: worker::State,
|
|
|
|
|
inner: &Arc<Pool>)
|
2018-03-28 01:56:21 +03:00
|
|
|
{
|
|
|
|
|
let entry = &self.workers[idx];
|
|
|
|
|
|
|
|
|
|
if !entry.submit_external(task, state) {
|
2018-04-03 22:35:59 -07:00
|
|
|
self.spawn_worker(idx, inner);
|
2018-03-28 01:56:21 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-04 13:30:54 -07:00
|
|
|
fn spawn_worker(&self, idx: usize, inner: &Arc<Pool>) {
|
2018-04-03 22:35:59 -07:00
|
|
|
Worker::spawn(WorkerId::new(idx), inner);
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-28 01:56:21 +03:00
|
|
|
/// If there are any other workers currently relaxing, signal them that work
|
|
|
|
|
/// is available so that they can try to find more work to process.
|
2018-04-04 13:30:54 -07:00
|
|
|
pub fn signal_work(&self, inner: &Arc<Pool>) {
|
2018-04-03 22:35:59 -07:00
|
|
|
use worker::Lifecycle::*;
|
|
|
|
|
|
2018-04-04 13:30:54 -07:00
|
|
|
if let Some((idx, mut worker_state)) = self.sleep_stack.pop(&self.workers, Signaled, false) {
|
2018-03-28 01:56:21 +03:00
|
|
|
let entry = &self.workers[idx];
|
|
|
|
|
|
2018-04-04 13:30:54 -07:00
|
|
|
debug_assert!(worker_state.lifecycle() != Signaled, "actual={:?}", worker_state.lifecycle());
|
2018-04-03 22:35:59 -07:00
|
|
|
|
2018-03-28 01:56:21 +03:00
|
|
|
// Transition the worker state to signaled
|
|
|
|
|
loop {
|
2018-04-04 13:30:54 -07:00
|
|
|
let mut next = worker_state;
|
2018-03-28 01:56:21 +03:00
|
|
|
|
2018-04-03 22:35:59 -07:00
|
|
|
next.set_lifecycle(Signaled);
|
2018-03-28 01:56:21 +03:00
|
|
|
|
|
|
|
|
let actual = entry.state.compare_and_swap(
|
2018-04-04 13:30:54 -07:00
|
|
|
worker_state.into(), next.into(), AcqRel).into();
|
2018-03-28 01:56:21 +03:00
|
|
|
|
2018-04-04 13:30:54 -07:00
|
|
|
if actual == worker_state {
|
2018-03-28 01:56:21 +03:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-04 13:30:54 -07:00
|
|
|
worker_state = actual;
|
2018-03-28 01:56:21 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The state has been transitioned to signal, now we need to wake up
|
|
|
|
|
// the worker if necessary.
|
2018-04-04 13:30:54 -07:00
|
|
|
match worker_state.lifecycle() {
|
2018-04-03 22:35:59 -07:00
|
|
|
Sleeping => {
|
2018-03-28 01:56:21 +03:00
|
|
|
trace!("signal_work -- wakeup; idx={}", idx);
|
|
|
|
|
self.workers[idx].wakeup();
|
|
|
|
|
}
|
2018-04-03 22:35:59 -07:00
|
|
|
Shutdown => {
|
2018-03-28 01:56:21 +03:00
|
|
|
trace!("signal_work -- spawn; idx={}", idx);
|
2018-03-30 11:50:02 -07:00
|
|
|
Worker::spawn(WorkerId::new(idx), inner);
|
2018-03-28 01:56:21 +03:00
|
|
|
}
|
2018-04-03 22:35:59 -07:00
|
|
|
Running | Notified | Signaled => {
|
|
|
|
|
// The workers are already active. No need to wake them up.
|
|
|
|
|
}
|
2018-03-28 01:56:21 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Generates a random number
|
|
|
|
|
///
|
|
|
|
|
/// Uses a thread-local seeded XorShift.
|
|
|
|
|
pub fn rand_usize(&self) -> usize {
|
|
|
|
|
// Use a thread-local random number generator. If the thread does not
|
|
|
|
|
// have one yet, then seed a new one
|
|
|
|
|
thread_local!(static THREAD_RNG_KEY: UnsafeCell<Option<XorShiftRng>> = UnsafeCell::new(None));
|
|
|
|
|
|
|
|
|
|
THREAD_RNG_KEY.with(|t| {
|
|
|
|
|
#[cfg(target_pointer_width = "32")]
|
|
|
|
|
fn new_rng(thread_id: usize) -> XorShiftRng {
|
|
|
|
|
XorShiftRng::from_seed([
|
|
|
|
|
thread_id as u32,
|
|
|
|
|
0x00000000,
|
|
|
|
|
0xa8a7d469,
|
|
|
|
|
0x97830e05])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(target_pointer_width = "64")]
|
|
|
|
|
fn new_rng(thread_id: usize) -> XorShiftRng {
|
|
|
|
|
XorShiftRng::from_seed([
|
|
|
|
|
thread_id as u32,
|
|
|
|
|
(thread_id >> 32) as u32,
|
|
|
|
|
0xa8a7d469,
|
|
|
|
|
0x97830e05])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let thread_id = self.next_thread_id.fetch_add(1, Relaxed);
|
|
|
|
|
let rng = unsafe { &mut *t.get() };
|
|
|
|
|
|
|
|
|
|
if rng.is_none() {
|
|
|
|
|
*rng = Some(new_rng(thread_id));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rng.as_mut().unwrap().next_u32() as usize
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-04 13:30:54 -07:00
|
|
|
unsafe impl Send for Pool {}
|
|
|
|
|
unsafe impl Sync for Pool {}
|