mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-08-22 00:00:11 +02:00
* Builder -> src/builder.rs * Callback -> src/callback.rs * Config -> src/config.rs * Futures2Wake -> src/futures2_wake.rs * Inner -> src/inner.rs * Notifier-> src/notifier.rs * Sender -> src/sender.rs * Shutdown -> src/shutdown.rs * ShutdownTask -> src/shutdown_task.rs * SleepStack -> src/sleep_stack.rs * State -> src/state.rs * ThreadPool -> src/thread_pool.rs * Worker -> src/worker.rs * WorkerEntry -> src/worker_entry.rs * WorkerState -> src/worker_state.rs
121 lines
2.9 KiB
Rust
121 lines
2.9 KiB
Rust
use task::{Task, Queue};
|
|
use worker_state::{
|
|
WorkerState,
|
|
WORKER_SHUTDOWN,
|
|
WORKER_SLEEPING,
|
|
};
|
|
|
|
use std::cell::UnsafeCell;
|
|
use std::sync::atomic::Ordering::{AcqRel};
|
|
use std::sync::atomic::AtomicUsize;
|
|
use std::sync::{Mutex, Condvar};
|
|
|
|
use deque;
|
|
|
|
#[derive(Debug)]
|
|
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
|
|
pub deque: deque::Deque<Task>,
|
|
|
|
// Stealer half of deque
|
|
pub steal: deque::Stealer<Task>,
|
|
|
|
// Park mutex
|
|
pub park_mutex: Mutex<()>,
|
|
|
|
// Park condvar
|
|
pub park_condvar: Condvar,
|
|
|
|
// MPSC queue of jobs submitted to the worker from an external source.
|
|
pub inbound: Queue,
|
|
}
|
|
|
|
impl WorkerEntry {
|
|
pub fn new() -> Self {
|
|
let w = deque::Deque::new();
|
|
let s = w.stealer();
|
|
|
|
WorkerEntry {
|
|
state: AtomicUsize::new(WorkerState::default().into()),
|
|
next_sleeper: UnsafeCell::new(0),
|
|
deque: w,
|
|
steal: s,
|
|
inbound: Queue::new(),
|
|
park_mutex: Mutex::new(()),
|
|
park_condvar: Condvar::new(),
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
pub fn submit_internal(&self, task: Task) {
|
|
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.
|
|
pub fn submit_external(&self, task: Task, mut state: WorkerState) -> bool {
|
|
// 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() {
|
|
WORKER_SLEEPING => {
|
|
// The worker is currently sleeping, the condition variable must
|
|
// be signaled
|
|
self.wakeup();
|
|
true
|
|
}
|
|
WORKER_SHUTDOWN => false,
|
|
_ => true,
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn push_external(&self, task: Task) {
|
|
self.inbound.push(task);
|
|
}
|
|
|
|
#[inline]
|
|
pub fn push_internal(&self, task: Task) {
|
|
self.deque.push(task);
|
|
}
|
|
|
|
#[inline]
|
|
pub fn wakeup(&self) {
|
|
let _lock = self.park_mutex.lock().unwrap();
|
|
self.park_condvar.notify_one();
|
|
}
|
|
|
|
#[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; }
|
|
}
|
|
}
|