mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-07 00:00:08 +02:00
threadpool: update crossbeam dependencies (#874)
This commit is contained in:
committed by
Carl Lerche
parent
11e2af66a8
commit
e1a07ce50c
@@ -22,10 +22,9 @@ categories = ["concurrency", "asynchronous"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
tokio-executor = { version = "0.1.2", path = "../tokio-executor" }
|
tokio-executor = { version = "0.1.2", path = "../tokio-executor" }
|
||||||
futures = "0.1.19"
|
futures = "0.1.19"
|
||||||
crossbeam = "0.6.0"
|
crossbeam-deque = "0.7.0"
|
||||||
crossbeam-channel = "0.3.3"
|
crossbeam-queue = "0.1.0"
|
||||||
crossbeam-deque = "0.6.1"
|
crossbeam-utils = "0.6.4"
|
||||||
crossbeam-utils = "0.6.2"
|
|
||||||
num_cpus = "1.2"
|
num_cpus = "1.2"
|
||||||
rand = "0.6"
|
rand = "0.6"
|
||||||
slab = "0.4.1"
|
slab = "0.4.1"
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ use config::{Config, MAX_WORKERS};
|
|||||||
use park::{BoxPark, BoxedPark, DefaultPark};
|
use park::{BoxPark, BoxedPark, DefaultPark};
|
||||||
use shutdown::ShutdownTrigger;
|
use shutdown::ShutdownTrigger;
|
||||||
use pool::{Pool, MAX_BACKUP};
|
use pool::{Pool, MAX_BACKUP};
|
||||||
use task::Queue;
|
|
||||||
use thread_pool::ThreadPool;
|
use thread_pool::ThreadPool;
|
||||||
use worker::{self, Worker, WorkerId};
|
use worker::{self, Worker, WorkerId};
|
||||||
|
|
||||||
@@ -13,6 +12,7 @@ use std::sync::Arc;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
|
|
||||||
|
use crossbeam_deque::Injector;
|
||||||
use num_cpus;
|
use num_cpus;
|
||||||
use tokio_executor::Enter;
|
use tokio_executor::Enter;
|
||||||
use tokio_executor::park::Park;
|
use tokio_executor::park::Park;
|
||||||
@@ -414,7 +414,7 @@ impl Builder {
|
|||||||
workers.into()
|
workers.into()
|
||||||
};
|
};
|
||||||
|
|
||||||
let queue = Arc::new(Queue::new());
|
let queue = Arc::new(Injector::new());
|
||||||
|
|
||||||
// Create a trigger that will clean up resources on shutdown.
|
// Create a trigger that will clean up resources on shutdown.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -79,9 +79,8 @@
|
|||||||
|
|
||||||
extern crate tokio_executor;
|
extern crate tokio_executor;
|
||||||
|
|
||||||
extern crate crossbeam;
|
extern crate crossbeam_deque;
|
||||||
extern crate crossbeam_channel;
|
extern crate crossbeam_queue;
|
||||||
extern crate crossbeam_deque as deque;
|
|
||||||
extern crate crossbeam_utils;
|
extern crate crossbeam_utils;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate futures;
|
extern crate futures;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ use self::backup_stack::BackupStack;
|
|||||||
|
|
||||||
use config::Config;
|
use config::Config;
|
||||||
use shutdown::ShutdownTrigger;
|
use shutdown::ShutdownTrigger;
|
||||||
use task::{Blocking, Queue, Task};
|
use task::{Blocking, Task};
|
||||||
use worker::{self, Worker, WorkerId};
|
use worker::{self, Worker, WorkerId};
|
||||||
|
|
||||||
use futures::Poll;
|
use futures::Poll;
|
||||||
@@ -27,6 +27,7 @@ use std::sync::atomic::AtomicUsize;
|
|||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
|
use crossbeam_deque::Injector;
|
||||||
use crossbeam_utils::CachePadded;
|
use crossbeam_utils::CachePadded;
|
||||||
use rand;
|
use rand;
|
||||||
|
|
||||||
@@ -57,7 +58,7 @@ pub(crate) struct Pool {
|
|||||||
//
|
//
|
||||||
// Spawned tasks are pushed into this queue. Although worker threads have their own dedicated
|
// Spawned tasks are pushed into this queue. Although worker threads have their own dedicated
|
||||||
// task queues, they periodically steal tasks from this global queue, too.
|
// task queues, they periodically steal tasks from this global queue, too.
|
||||||
pub queue: Arc<Queue>,
|
pub queue: Arc<Injector<Arc<Task>>>,
|
||||||
|
|
||||||
// Completes the shutdown process when the `ThreadPool` and all `Worker`s get dropped.
|
// Completes the shutdown process when the `ThreadPool` and all `Worker`s get dropped.
|
||||||
//
|
//
|
||||||
@@ -90,7 +91,7 @@ impl Pool {
|
|||||||
trigger: Weak<ShutdownTrigger>,
|
trigger: Weak<ShutdownTrigger>,
|
||||||
max_blocking: usize,
|
max_blocking: usize,
|
||||||
config: Config,
|
config: Config,
|
||||||
queue: Arc<Queue>,
|
queue: Arc<Injector<Arc<Task>>>,
|
||||||
) -> Pool {
|
) -> Pool {
|
||||||
let pool_size = workers.len();
|
let pool_size = workers.len();
|
||||||
let total_size = max_blocking + pool_size;
|
let total_size = max_blocking + pool_size;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use task::Queue;
|
use task::Task;
|
||||||
use worker;
|
use worker;
|
||||||
|
|
||||||
|
use crossbeam_deque::Injector;
|
||||||
use futures::{Future, Poll, Async};
|
use futures::{Future, Poll, Async};
|
||||||
use futures::task::AtomicTask;
|
use futures::task::AtomicTask;
|
||||||
|
|
||||||
@@ -62,14 +63,17 @@ impl Future for Shutdown {
|
|||||||
pub(crate) struct ShutdownTrigger {
|
pub(crate) struct ShutdownTrigger {
|
||||||
inner: Arc<Mutex<Inner>>,
|
inner: Arc<Mutex<Inner>>,
|
||||||
workers: Arc<[worker::Entry]>,
|
workers: Arc<[worker::Entry]>,
|
||||||
queue: Arc<Queue>,
|
queue: Arc<Injector<Arc<Task>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for ShutdownTrigger {}
|
unsafe impl Send for ShutdownTrigger {}
|
||||||
unsafe impl Sync for ShutdownTrigger {}
|
unsafe impl Sync for ShutdownTrigger {}
|
||||||
|
|
||||||
impl ShutdownTrigger {
|
impl ShutdownTrigger {
|
||||||
pub(crate) fn new(workers: Arc<[worker::Entry]>, queue: Arc<Queue>) -> ShutdownTrigger {
|
pub(crate) fn new(
|
||||||
|
workers: Arc<[worker::Entry]>,
|
||||||
|
queue: Arc<Injector<Arc<Task>>>,
|
||||||
|
) -> ShutdownTrigger {
|
||||||
ShutdownTrigger {
|
ShutdownTrigger {
|
||||||
inner: Arc::new(Mutex::new(Inner {
|
inner: Arc::new(Mutex::new(Inner {
|
||||||
task: AtomicTask::new(),
|
task: AtomicTask::new(),
|
||||||
@@ -84,7 +88,7 @@ impl ShutdownTrigger {
|
|||||||
impl Drop for ShutdownTrigger {
|
impl Drop for ShutdownTrigger {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// Drain the global task queue.
|
// Drain the global task queue.
|
||||||
while self.queue.pop().is_some() {}
|
while !self.queue.steal().is_empty() {}
|
||||||
|
|
||||||
// Drop the remaining incomplete tasks and parkers assosicated with workers.
|
// Drop the remaining incomplete tasks and parkers assosicated with workers.
|
||||||
for worker in self.workers.iter() {
|
for worker in self.workers.iter() {
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
mod blocking;
|
mod blocking;
|
||||||
mod blocking_state;
|
mod blocking_state;
|
||||||
mod queue;
|
|
||||||
mod state;
|
mod state;
|
||||||
|
|
||||||
pub(crate) use self::blocking::{Blocking, CanBlock};
|
pub(crate) use self::blocking::{Blocking, CanBlock};
|
||||||
pub(crate) use self::queue::Queue;
|
|
||||||
use self::blocking_state::BlockingState;
|
use self::blocking_state::BlockingState;
|
||||||
use self::state::State;
|
use self::state::State;
|
||||||
|
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
use task::Task;
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use crossbeam_channel::{unbounded, Receiver, Sender};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) struct Queue {
|
|
||||||
// TODO(stjepang): Use a custom, faster MPMC queue implementation that supports `steal_many()`.
|
|
||||||
chan: (Sender<Arc<Task>>, Receiver<Arc<Task>>),
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===== impl Queue =====
|
|
||||||
|
|
||||||
impl Queue {
|
|
||||||
/// Create a new, empty, `Queue`.
|
|
||||||
pub fn new() -> Queue {
|
|
||||||
Queue {
|
|
||||||
chan: unbounded(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Push a task onto the queue.
|
|
||||||
#[inline]
|
|
||||||
pub fn push(&self, task: Arc<Task>) {
|
|
||||||
self.chan.0.send(task).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pop a task from the queue.
|
|
||||||
#[inline]
|
|
||||||
pub fn pop(&self) -> Option<Arc<Task>> {
|
|
||||||
self.chan.1.try_recv().ok()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,9 +9,9 @@ use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
|||||||
use std::sync::atomic::Ordering::{Acquire, AcqRel, Relaxed, Release};
|
use std::sync::atomic::Ordering::{Acquire, AcqRel, Relaxed, Release};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use crossbeam::queue::SegQueue;
|
use crossbeam_deque::{Steal, Stealer, Worker};
|
||||||
|
use crossbeam_queue::SegQueue;
|
||||||
use crossbeam_utils::CachePadded;
|
use crossbeam_utils::CachePadded;
|
||||||
use deque;
|
|
||||||
use slab::Slab;
|
use slab::Slab;
|
||||||
|
|
||||||
// TODO: None of the fields should be public
|
// TODO: None of the fields should be public
|
||||||
@@ -29,10 +29,10 @@ pub(crate) struct WorkerEntry {
|
|||||||
next_sleeper: UnsafeCell<usize>,
|
next_sleeper: UnsafeCell<usize>,
|
||||||
|
|
||||||
// Worker half of deque
|
// Worker half of deque
|
||||||
worker: deque::Worker<Arc<Task>>,
|
pub worker: Worker<Arc<Task>>,
|
||||||
|
|
||||||
// Stealer half of deque
|
// Stealer half of deque
|
||||||
stealer: deque::Stealer<Arc<Task>>,
|
stealer: Stealer<Arc<Task>>,
|
||||||
|
|
||||||
// Thread parker
|
// Thread parker
|
||||||
park: UnsafeCell<Option<BoxPark>>,
|
park: UnsafeCell<Option<BoxPark>>,
|
||||||
@@ -53,7 +53,8 @@ pub(crate) struct WorkerEntry {
|
|||||||
|
|
||||||
impl WorkerEntry {
|
impl WorkerEntry {
|
||||||
pub fn new(park: BoxPark, unpark: BoxUnpark) -> Self {
|
pub fn new(park: BoxPark, unpark: BoxUnpark) -> Self {
|
||||||
let (w, s) = deque::fifo();
|
let w = Worker::new_fifo();
|
||||||
|
let s = w.stealer();
|
||||||
|
|
||||||
WorkerEntry {
|
WorkerEntry {
|
||||||
state: CachePadded::new(AtomicUsize::new(State::default().into())),
|
state: CachePadded::new(AtomicUsize::new(State::default().into())),
|
||||||
@@ -187,7 +188,7 @@ impl WorkerEntry {
|
|||||||
/// This **must** only be called by the thread that owns the worker entry.
|
/// This **must** only be called by the thread that owns the worker entry.
|
||||||
/// This function is not `Sync`.
|
/// This function is not `Sync`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn pop_task(&self) -> deque::Pop<Arc<Task>> {
|
pub fn pop_task(&self) -> Option<Arc<Task>> {
|
||||||
self.worker.pop()
|
self.worker.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,23 +200,15 @@ impl WorkerEntry {
|
|||||||
/// At the same time, this method steals some additional tasks and moves
|
/// At the same time, this method steals some additional tasks and moves
|
||||||
/// them into `dest` in order to balance the work distribution among
|
/// them into `dest` in order to balance the work distribution among
|
||||||
/// workers.
|
/// workers.
|
||||||
pub fn steal_tasks(&self, dest: &Self) -> deque::Steal<Arc<Task>> {
|
pub fn steal_tasks(&self, dest: &Self) -> Steal<Arc<Task>> {
|
||||||
self.stealer.steal_many(&dest.worker)
|
self.stealer.steal_batch_and_pop(&dest.worker)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Drain (and drop) all tasks that are queued for work.
|
/// Drain (and drop) all tasks that are queued for work.
|
||||||
///
|
///
|
||||||
/// This is called when the pool is shutting down.
|
/// This is called when the pool is shutting down.
|
||||||
pub fn drain_tasks(&self) {
|
pub fn drain_tasks(&self) {
|
||||||
use deque::Pop::*;
|
while self.worker.pop().is_some() {}
|
||||||
|
|
||||||
loop {
|
|
||||||
match self.worker.pop() {
|
|
||||||
Data(_) => {}
|
|
||||||
Empty => break,
|
|
||||||
Retry => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parks the worker thread.
|
/// Parks the worker thread.
|
||||||
@@ -284,7 +277,6 @@ impl WorkerEntry {
|
|||||||
}
|
}
|
||||||
running_tasks.clear();
|
running_tasks.clear();
|
||||||
|
|
||||||
// Drop the parker.
|
|
||||||
unsafe {
|
unsafe {
|
||||||
*self.park.get() = None;
|
*self.park.get() = None;
|
||||||
*self.unpark.get() = None;
|
*self.unpark.get() = None;
|
||||||
@@ -297,7 +289,7 @@ impl WorkerEntry {
|
|||||||
if self.needs_drain.compare_and_swap(true, false, Acquire) {
|
if self.needs_drain.compare_and_swap(true, false, Acquire) {
|
||||||
let running_tasks = unsafe { &mut *self.running_tasks.get() };
|
let running_tasks = unsafe { &mut *self.running_tasks.get() };
|
||||||
|
|
||||||
while let Some(task) = self.remotely_completed_tasks.try_pop() {
|
while let Ok(task) = self.remotely_completed_tasks.pop() {
|
||||||
running_tasks.remove(task.reg_index.get());
|
running_tasks.remove(task.reg_index.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -386,16 +386,13 @@ impl Worker {
|
|||||||
///
|
///
|
||||||
/// Returns `true` if work was found.
|
/// Returns `true` if work was found.
|
||||||
fn try_run_owned_task(&self, notify: &Arc<Notifier>) -> bool {
|
fn try_run_owned_task(&self, notify: &Arc<Notifier>) -> bool {
|
||||||
use deque::Pop;
|
|
||||||
|
|
||||||
// Poll the internal queue for a task to run
|
// Poll the internal queue for a task to run
|
||||||
match self.entry().pop_task() {
|
match self.entry().pop_task() {
|
||||||
Pop::Data(task) => {
|
Some(task) => {
|
||||||
self.run_task(task, notify);
|
self.run_task(task, notify);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Pop::Empty => false,
|
None => false,
|
||||||
Pop::Retry => true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -403,7 +400,7 @@ impl Worker {
|
|||||||
///
|
///
|
||||||
/// Returns `true` if work was found
|
/// Returns `true` if work was found
|
||||||
fn try_steal_task(&self, notify: &Arc<Notifier>) -> bool {
|
fn try_steal_task(&self, notify: &Arc<Notifier>) -> bool {
|
||||||
use deque::Steal;
|
use crossbeam_deque::Steal;
|
||||||
|
|
||||||
debug_assert!(!self.is_blocking.get());
|
debug_assert!(!self.is_blocking.get());
|
||||||
|
|
||||||
@@ -415,7 +412,7 @@ impl Worker {
|
|||||||
loop {
|
loop {
|
||||||
if idx < len {
|
if idx < len {
|
||||||
match self.pool.workers[idx].steal_tasks(self.entry()) {
|
match self.pool.workers[idx].steal_tasks(self.entry()) {
|
||||||
Steal::Data(task) => {
|
Steal::Success(task) => {
|
||||||
trace!("stole task from another worker");
|
trace!("stole task from another worker");
|
||||||
|
|
||||||
self.run_task(task, notify);
|
self.run_task(task, notify);
|
||||||
@@ -701,15 +698,17 @@ impl Worker {
|
|||||||
///
|
///
|
||||||
/// Returns `true` if this worker has tasks in its queue.
|
/// Returns `true` if this worker has tasks in its queue.
|
||||||
fn sleep_light(&self) {
|
fn sleep_light(&self) {
|
||||||
const STEAL_COUNT: usize = 32;
|
|
||||||
|
|
||||||
self.entry().park_timeout(Duration::from_millis(0));
|
self.entry().park_timeout(Duration::from_millis(0));
|
||||||
|
|
||||||
for _ in 0..STEAL_COUNT {
|
use crossbeam_deque::Steal;
|
||||||
if let Some(task) = self.pool.queue.pop() {
|
loop {
|
||||||
self.pool.submit(task, &self.pool);
|
match self.pool.queue.steal_batch(&self.entry().worker) {
|
||||||
} else {
|
Steal::Success(()) => {
|
||||||
break;
|
self.pool.signal_work(&self.pool);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Steal::Empty => break,
|
||||||
|
Steal::Retry => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user