2018-03-29 13:47:08 -07:00
|
|
|
use park::{BoxPark, BoxUnpark};
|
2018-03-28 01:56:21 +03:00
|
|
|
use task::{Task, Queue};
|
2018-04-04 13:30:54 -07:00
|
|
|
use worker::state::{State, PUSHED_MASK};
|
2018-03-28 01:56:21 +03:00
|
|
|
|
|
|
|
|
use std::cell::UnsafeCell;
|
2018-03-29 13:47:08 -07:00
|
|
|
use std::fmt;
|
2018-04-05 10:57:05 -07:00
|
|
|
use std::sync::Arc;
|
2018-04-04 13:30:54 -07:00
|
|
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
|
|
|
use std::sync::atomic::Ordering::{Acquire, AcqRel, Relaxed};
|
2018-03-28 01:56:21 +03:00
|
|
|
|
|
|
|
|
use deque;
|
|
|
|
|
|
2018-04-03 22:35:59 -07:00
|
|
|
// TODO: None of the fields should be public
|
2018-04-04 13:30:54 -07:00
|
|
|
//
|
|
|
|
|
// It would also be helpful to split up the state across what fields /
|
|
|
|
|
// operations are thread-safe vs. which ones require ownership of the worker.
|
2018-03-28 01:56:21 +03:00
|
|
|
pub(crate) struct WorkerEntry {
|
|
|
|
|
// Worker state. This is mutated when notifying the worker.
|
|
|
|
|
pub state: AtomicUsize,
|
|
|
|
|
|
|
|
|
|
// Next entry in the parked Trieber stack
|
|
|
|
|
next_sleeper: UnsafeCell<usize>,
|
|
|
|
|
|
|
|
|
|
// Worker half of deque
|
2018-04-05 10:57:05 -07:00
|
|
|
deque: deque::Deque<Arc<Task>>,
|
2018-03-28 01:56:21 +03:00
|
|
|
|
|
|
|
|
// Stealer half of deque
|
2018-04-05 10:57:05 -07:00
|
|
|
steal: deque::Stealer<Arc<Task>>,
|
2018-03-28 01:56:21 +03:00
|
|
|
|
2018-03-29 13:47:08 -07:00
|
|
|
// Thread parker
|
|
|
|
|
pub park: UnsafeCell<BoxPark>,
|
2018-03-28 01:56:21 +03:00
|
|
|
|
2018-03-29 13:47:08 -07:00
|
|
|
// Thread unparker
|
|
|
|
|
pub unpark: BoxUnpark,
|
2018-03-28 01:56:21 +03:00
|
|
|
|
|
|
|
|
// MPSC queue of jobs submitted to the worker from an external source.
|
|
|
|
|
pub inbound: Queue,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl WorkerEntry {
|
2018-03-29 13:47:08 -07:00
|
|
|
pub fn new(park: BoxPark, unpark: BoxUnpark) -> Self {
|
2018-03-28 01:56:21 +03:00
|
|
|
let w = deque::Deque::new();
|
|
|
|
|
let s = w.stealer();
|
|
|
|
|
|
|
|
|
|
WorkerEntry {
|
2018-04-04 13:30:54 -07:00
|
|
|
state: AtomicUsize::new(State::default().into()),
|
2018-03-28 01:56:21 +03:00
|
|
|
next_sleeper: UnsafeCell::new(0),
|
|
|
|
|
deque: w,
|
|
|
|
|
steal: s,
|
|
|
|
|
inbound: Queue::new(),
|
2018-03-29 13:47:08 -07:00
|
|
|
park: UnsafeCell::new(park),
|
|
|
|
|
unpark,
|
2018-03-28 01:56:21 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-04 13:30:54 -07:00
|
|
|
/// Atomically load the worker's state
|
|
|
|
|
///
|
|
|
|
|
/// # Ordering
|
|
|
|
|
///
|
|
|
|
|
/// An `Acquire` ordering is established on the entry's state variable.
|
|
|
|
|
pub fn load_state(&self) -> State {
|
|
|
|
|
self.state.load(Acquire).into()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Atomically unset the pushed flag.
|
|
|
|
|
///
|
|
|
|
|
/// # Return
|
|
|
|
|
///
|
|
|
|
|
/// The state *before* the push flag is unset.
|
|
|
|
|
///
|
|
|
|
|
/// # Ordering
|
|
|
|
|
///
|
|
|
|
|
/// The specified ordering is established on the entry's state variable.
|
|
|
|
|
pub fn fetch_unset_pushed(&self, ordering: Ordering) -> State {
|
|
|
|
|
self.state.fetch_and(!PUSHED_MASK, ordering).into()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Submit a task to this worker while currently on the same thread that is
|
|
|
|
|
/// running the worker.
|
2018-03-28 01:56:21 +03:00
|
|
|
#[inline]
|
2018-04-05 10:57:05 -07:00
|
|
|
pub fn submit_internal(&self, task: Arc<Task>) {
|
2018-03-28 01:56:21 +03:00
|
|
|
self.push_internal(task);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Submits a task to the worker. This assumes that the caller is external
|
|
|
|
|
/// to the worker. Internal submissions go through another path.
|
|
|
|
|
///
|
|
|
|
|
/// Returns `false` if the worker needs to be spawned.
|
2018-04-04 13:30:54 -07:00
|
|
|
///
|
|
|
|
|
/// # Ordering
|
|
|
|
|
///
|
|
|
|
|
/// The `state` must have been obtained with an `Acquire` ordering.
|
2018-04-05 10:57:05 -07:00
|
|
|
pub fn submit_external(&self, task: Arc<Task>, mut state: State) -> bool {
|
2018-04-03 22:35:59 -07:00
|
|
|
use worker::Lifecycle::*;
|
|
|
|
|
|
2018-03-28 01:56:21 +03:00
|
|
|
// Push the task onto the external queue
|
|
|
|
|
self.push_external(task);
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
let mut next = state;
|
|
|
|
|
next.notify();
|
|
|
|
|
|
|
|
|
|
let actual = self.state.compare_and_swap(
|
|
|
|
|
state.into(), next.into(),
|
|
|
|
|
AcqRel).into();
|
|
|
|
|
|
|
|
|
|
if state == actual {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
state = actual;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
match state.lifecycle() {
|
2018-04-03 22:35:59 -07:00
|
|
|
Sleeping => {
|
2018-03-28 01:56:21 +03:00
|
|
|
// The worker is currently sleeping, the condition variable must
|
|
|
|
|
// be signaled
|
|
|
|
|
self.wakeup();
|
|
|
|
|
true
|
|
|
|
|
}
|
2018-04-03 22:35:59 -07:00
|
|
|
Shutdown => false,
|
|
|
|
|
Running | Notified | Signaled => {
|
|
|
|
|
// In these states, the worker is active and will eventually see
|
|
|
|
|
// the task that was just submitted.
|
|
|
|
|
true
|
|
|
|
|
}
|
2018-03-28 01:56:21 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-04 13:30:54 -07:00
|
|
|
/// Signals to the worker that it should stop
|
|
|
|
|
///
|
|
|
|
|
/// `state` is the last observed state for the worker. This allows skipping
|
|
|
|
|
/// the initial load from the state atomic.
|
|
|
|
|
///
|
|
|
|
|
/// # Return
|
|
|
|
|
///
|
|
|
|
|
/// Returns `Ok` when the worker was successfully signaled.
|
|
|
|
|
///
|
|
|
|
|
/// Returns `Err` if the worker has already terminated.
|
2018-04-15 12:29:22 -07:00
|
|
|
pub fn signal_stop(&self, mut state: State) {
|
2018-04-04 13:30:54 -07:00
|
|
|
use worker::Lifecycle::*;
|
|
|
|
|
|
|
|
|
|
// Transition the worker state to signaled
|
|
|
|
|
loop {
|
|
|
|
|
let mut next = state;
|
|
|
|
|
|
|
|
|
|
match state.lifecycle() {
|
|
|
|
|
Shutdown => {
|
2018-04-15 12:29:22 -07:00
|
|
|
return;
|
2018-04-04 13:30:54 -07:00
|
|
|
}
|
|
|
|
|
Running | Sleeping => {}
|
|
|
|
|
Notified | Signaled => {
|
|
|
|
|
// These two states imply that the worker is active, thus it
|
|
|
|
|
// will eventually see the shutdown signal, so we don't need
|
|
|
|
|
// to do anything.
|
|
|
|
|
//
|
|
|
|
|
// The worker is forced to see the shutdown signal
|
|
|
|
|
// eventually as:
|
|
|
|
|
//
|
|
|
|
|
// a) No more work will arrive
|
|
|
|
|
// b) The shutdown signal is stored as the head of the
|
|
|
|
|
// sleep, stack which will prevent the worker from going to
|
|
|
|
|
// sleep again.
|
2018-04-15 12:29:22 -07:00
|
|
|
return;
|
2018-04-04 13:30:54 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
next.set_lifecycle(Signaled);
|
|
|
|
|
|
|
|
|
|
let actual = self.state.compare_and_swap(
|
|
|
|
|
state.into(), next.into(), AcqRel).into();
|
|
|
|
|
|
|
|
|
|
if actual == state {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
state = actual;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Wakeup the worker
|
|
|
|
|
self.wakeup();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Pop a task
|
|
|
|
|
///
|
|
|
|
|
/// This **must** only be called by the thread that owns the worker entry.
|
|
|
|
|
/// This function is not `Sync`.
|
2018-04-05 10:57:05 -07:00
|
|
|
pub fn pop_task(&self) -> deque::Steal<Arc<Task>> {
|
2018-04-04 13:30:54 -07:00
|
|
|
self.deque.steal()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Steal a task
|
|
|
|
|
///
|
|
|
|
|
/// This is called by *other* workers to steal a task for processing. This
|
|
|
|
|
/// function is `Sync`.
|
2018-04-05 10:57:05 -07:00
|
|
|
pub fn steal_task(&self) -> deque::Steal<Arc<Task>> {
|
2018-04-04 13:30:54 -07:00
|
|
|
self.steal.steal()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Drain (and drop) all tasks that are queued for work.
|
|
|
|
|
///
|
|
|
|
|
/// This is called when the pool is shutting down.
|
|
|
|
|
pub fn drain_tasks(&self) {
|
|
|
|
|
while let Some(_) = self.deque.pop() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-28 01:56:21 +03:00
|
|
|
#[inline]
|
2018-04-05 10:57:05 -07:00
|
|
|
fn push_external(&self, task: Arc<Task>) {
|
2018-03-28 01:56:21 +03:00
|
|
|
self.inbound.push(task);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
2018-04-05 10:57:05 -07:00
|
|
|
pub fn push_internal(&self, task: Arc<Task>) {
|
2018-03-28 01:56:21 +03:00
|
|
|
self.deque.push(task);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn wakeup(&self) {
|
2018-03-29 13:47:08 -07:00
|
|
|
self.unpark.unpark();
|
2018-03-28 01:56:21 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn next_sleeper(&self) -> usize {
|
|
|
|
|
unsafe { *self.next_sleeper.get() }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn set_next_sleeper(&self, val: usize) {
|
|
|
|
|
unsafe { *self.next_sleeper.get() = val; }
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-03-29 13:47:08 -07:00
|
|
|
|
|
|
|
|
impl fmt::Debug for WorkerEntry {
|
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
fmt.debug_struct("WorkerEntry")
|
|
|
|
|
.field("state", &self.state.load(Relaxed))
|
|
|
|
|
.field("next_sleeper", &"UnsafeCell<usize>")
|
|
|
|
|
.field("deque", &self.deque)
|
|
|
|
|
.field("steal", &self.steal)
|
|
|
|
|
.field("park", &"UnsafeCell<BoxPark>")
|
|
|
|
|
.field("unpark", &"BoxUnpark")
|
|
|
|
|
.field("inbound", &self.inbound)
|
|
|
|
|
.finish()
|
|
|
|
|
}
|
|
|
|
|
}
|