mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-08-17 00:00:11 +02:00
threadpool: introduce a global task queue (#798)
This commit is contained in:
committed by
Toby Lawrence
parent
201b6ce53a
commit
fdf4aba621
@@ -1,33 +1,13 @@
|
||||
use task::Task;
|
||||
|
||||
use std::cell::UnsafeCell;
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::AtomicPtr;
|
||||
use std::sync::atomic::Ordering::{Acquire, Release, AcqRel, Relaxed};
|
||||
|
||||
use crossbeam_utils::CachePadded;
|
||||
use crossbeam_channel::{unbounded, Receiver, Sender};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Queue {
|
||||
/// Queue head.
|
||||
///
|
||||
/// This is a strong reference to `Task` (i.e, `Arc<Task>`)
|
||||
head: CachePadded<AtomicPtr<Task>>,
|
||||
|
||||
/// Tail pointer. This is `Arc<Task>` unless it points to `stub`.
|
||||
tail: UnsafeCell<*mut Task>,
|
||||
|
||||
/// Stub pointer, used as part of the intrusive mpsc channel algorithm
|
||||
/// described by 1024cores.
|
||||
stub: Box<Task>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum Poll {
|
||||
Empty,
|
||||
Inconsistent,
|
||||
Data(Arc<Task>),
|
||||
// TODO(stjepang): Use a custom, faster MPMC queue implementation that supports `steal_many()`.
|
||||
chan: (Sender<Arc<Task>>, Receiver<Arc<Task>>),
|
||||
}
|
||||
|
||||
// ===== impl Queue =====
|
||||
@@ -35,93 +15,20 @@ pub(crate) enum Poll {
|
||||
impl Queue {
|
||||
/// Create a new, empty, `Queue`.
|
||||
pub fn new() -> Queue {
|
||||
let stub = Box::new(Task::stub());
|
||||
let ptr = &*stub as *const _ as *mut _;
|
||||
|
||||
Queue {
|
||||
head: CachePadded::new(AtomicPtr::new(ptr)),
|
||||
tail: UnsafeCell::new(ptr),
|
||||
stub: stub,
|
||||
chan: unbounded(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Push a task onto the queue.
|
||||
///
|
||||
/// This function is `Sync`.
|
||||
#[inline]
|
||||
pub fn push(&self, task: Arc<Task>) {
|
||||
unsafe {
|
||||
self.push2(Arc::into_raw(task));
|
||||
}
|
||||
self.chan.0.send(task).unwrap();
|
||||
}
|
||||
|
||||
unsafe fn push2(&self, task: *const Task) {
|
||||
let task = task as *mut Task;
|
||||
|
||||
// Set the next pointer. This does not require an atomic operation as
|
||||
// this node is not accessible. The write will be flushed with the next
|
||||
// operation
|
||||
(*task).next.store(ptr::null_mut(), Relaxed);
|
||||
|
||||
// Update the head to point to the new node. We need to see the previous
|
||||
// node in order to update the next pointer as well as release `task`
|
||||
// to any other threads calling `push`.
|
||||
let prev = self.head.swap(task, AcqRel);
|
||||
|
||||
// Release `task` to the consume end.
|
||||
(*prev).next.store(task, Release);
|
||||
}
|
||||
|
||||
/// Poll a task from the queue.
|
||||
///
|
||||
/// This function is **not** `Sync` and requires coordination by the caller.
|
||||
pub unsafe fn poll(&self) -> Poll {
|
||||
let mut tail = *self.tail.get();
|
||||
let mut next = (*tail).next.load(Acquire);
|
||||
|
||||
let stub = &*self.stub as *const _ as *mut _;
|
||||
|
||||
if tail == stub {
|
||||
if next.is_null() {
|
||||
return Poll::Empty;
|
||||
}
|
||||
|
||||
*self.tail.get() = next;
|
||||
tail = next;
|
||||
next = (*next).next.load(Acquire);
|
||||
}
|
||||
|
||||
if !next.is_null() {
|
||||
*self.tail.get() = next;
|
||||
|
||||
// No ref_count inc is necessary here as this poll is paired
|
||||
// with a `push` which "forgets" the handle.
|
||||
return Poll::Data(Arc::from_raw(tail));
|
||||
}
|
||||
|
||||
if self.head.load(Acquire) != tail {
|
||||
return Poll::Inconsistent;
|
||||
}
|
||||
|
||||
self.push2(stub);
|
||||
|
||||
next = (*tail).next.load(Acquire);
|
||||
|
||||
if !next.is_null() {
|
||||
*self.tail.get() = next;
|
||||
|
||||
return Poll::Data(Arc::from_raw(tail));
|
||||
}
|
||||
|
||||
Poll::Inconsistent
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Queue {
|
||||
fn drop(&mut self) {
|
||||
loop {
|
||||
if let Poll::Empty = unsafe { self.poll() } {
|
||||
break
|
||||
}
|
||||
}
|
||||
/// Pop a task from the queue.
|
||||
#[inline]
|
||||
pub fn pop(&self) -> Option<Arc<Task>> {
|
||||
self.chan.1.try_recv().ok()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user