threadpool: update to std::future (#1219)

An initial pass at updating `tokio-threadpool` to `std::future`. The
codebase and tests both now run using `std::future` but the wake
mechanism is not ideal. Follow up work will be required to improve on
this.

Refs: #1200
This commit is contained in:
Carl Lerche
2019-06-27 22:30:56 -07:00
committed by GitHub
parent e4415d986a
commit e7488d983e
17 changed files with 436 additions and 485 deletions
+6 -5
View File
@@ -1,7 +1,8 @@
use crate::worker::Worker;
use futures::{try_ready, Poll};
use std::error::Error;
use std::fmt;
use std::task::Poll;
/// Error raised by `blocking`.
pub struct BlockingError {
@@ -116,7 +117,7 @@ pub struct BlockingError {
/// pool.shutdown_on_idle().wait().unwrap();
/// }
/// ```
pub fn blocking<F, T>(f: F) -> Poll<T, BlockingError>
pub fn blocking<F, T>(f: F) -> Poll<Result<T, BlockingError>>
where
F: FnOnce() -> T,
{
@@ -124,7 +125,7 @@ where
let worker = match worker {
Some(worker) => worker,
None => {
return Err(BlockingError { _p: () });
return Poll::Ready(Err(BlockingError { _p: () }));
}
};
@@ -135,7 +136,7 @@ where
});
// If the transition cannot happen, exit early
try_ready!(res);
ready!(res)?;
// Currently in blocking mode, so call the inner closure
let ret = f();
@@ -148,7 +149,7 @@ where
});
// Return the result
Ok(ret.into())
Poll::Ready(Ok(ret))
}
impl fmt::Display for BlockingError {
+11 -2
View File
@@ -131,21 +131,30 @@
pub mod park;
macro_rules! ready {
($e:expr) => {
match $e {
::std::task::Poll::Ready(t) => t,
::std::task::Poll::Pending => return ::std::task::Poll::Pending,
}
};
}
mod blocking;
mod builder;
mod callback;
mod config;
mod notifier;
mod pool;
mod sender;
mod shutdown;
mod task;
mod thread_pool;
mod waker;
mod worker;
pub use crate::blocking::{blocking, BlockingError};
pub use crate::builder::Builder;
pub use crate::sender::Sender;
pub use crate::shutdown::Shutdown;
pub use crate::thread_pool::{SpawnHandle, ThreadPool};
pub use crate::thread_pool::ThreadPool;
pub use crate::worker::{Worker, WorkerId};
-91
View File
@@ -1,91 +0,0 @@
use crate::pool::Pool;
use crate::task::Task;
use futures::executor::Notify;
use log::trace;
use std::mem;
use std::ops;
use std::sync::Arc;
/// Implements the future `Notify` API.
///
/// This is how external events are able to signal the task, informing it to try
/// to poll the future again.
#[derive(Debug)]
pub(crate) struct Notifier {
pub pool: Arc<Pool>,
}
/// A guard that ensures that the inner value gets forgotten.
#[derive(Debug)]
struct Forget<T>(Option<T>);
impl Notify for Notifier {
fn notify(&self, id: usize) {
trace!("Notifier::notify; id=0x{:x}", id);
unsafe {
let ptr = id as *const Task;
// We did not actually take ownership of the `Arc` in this function
// so we must ensure that the Arc is forgotten.
let task = Forget::new(Arc::from_raw(ptr));
// TODO: Unify this with Task::notify
if task.schedule() {
// TODO: Check if the pool is still running
//
// Bump the ref count
let task = task.clone();
let _ = self.pool.submit(task, &self.pool);
}
}
}
fn clone_id(&self, id: usize) -> usize {
let ptr = id as *const Task;
// This function doesn't actually get a strong ref to the task here.
// However, the only method we have to convert a raw pointer -> &Arc<T>
// is to call `Arc::from_raw` which returns a strong ref. So, to
// maintain the invariants, `t1` has to be forgotten. This prevents the
// ref count from being decremented.
let t1 = Forget::new(unsafe { Arc::from_raw(ptr) });
// The clone is forgotten so that the fn exits without decrementing the ref
// count. The caller of `clone_id` ensures that `drop_id` is called when
// the ref count needs to be decremented.
let _ = Forget::new(t1.clone());
id
}
fn drop_id(&self, id: usize) {
unsafe {
let ptr = id as *const Task;
let _ = Arc::from_raw(ptr);
}
}
}
// ===== impl Forget =====
impl<T> Forget<T> {
fn new(t: T) -> Self {
Forget(Some(t))
}
}
impl<T> ops::Deref for Forget<T> {
type Target = T;
fn deref(&self) -> &T {
self.0.as_ref().unwrap()
}
}
impl<T> Drop for Forget<T> {
fn drop(&mut self) {
mem::forget(self.0.take());
}
}
+6 -2
View File
@@ -15,7 +15,7 @@ use crate::task::{Blocking, Task};
use crate::worker::{self, Worker, WorkerId};
use crossbeam_deque::Injector;
use crossbeam_utils::CachePadded;
use futures::Poll;
use log::{debug, error, trace};
use rand;
use std::cell::Cell;
@@ -23,6 +23,7 @@ use std::num::Wrapping;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::{AcqRel, Acquire};
use std::sync::{Arc, Weak};
use std::task::Poll;
use std::thread;
#[derive(Debug)]
@@ -219,7 +220,10 @@ impl Pool {
}
}
pub fn poll_blocking_capacity(&self, task: &Arc<Task>) -> Poll<(), crate::BlockingError> {
pub fn poll_blocking_capacity(
&self,
task: &Arc<Task>,
) -> Poll<Result<(), crate::BlockingError>> {
self.blocking.poll_blocking_capacity(task)
}
+11 -28
View File
@@ -1,10 +1,13 @@
use crate::pool::{self, Lifecycle, Pool, MAX_FUTURES};
use crate::task::Task;
use futures::{future, Future};
use tokio_executor::{self, SpawnError};
use log::trace;
use std::future::Future;
use std::pin::Pin;
use std::sync::atomic::Ordering::{AcqRel, Acquire};
use std::sync::Arc;
use tokio_executor::{self, SpawnError};
/// Submit futures to the associated thread pool for execution.
///
@@ -75,10 +78,10 @@ impl Sender {
/// ```
pub fn spawn<F>(&self, future: F) -> Result<(), SpawnError>
where
F: Future<Item = (), Error = ()> + Send + 'static,
F: Future<Output = ()> + Send + 'static,
{
let mut s = self;
tokio_executor::Executor::spawn(&mut s, Box::new(future))
tokio_executor::Executor::spawn(&mut s, Box::pin(future))
}
/// Logic to prepare for spawning
@@ -128,7 +131,7 @@ impl tokio_executor::Executor for Sender {
fn spawn(
&mut self,
future: Box<dyn Future<Item = (), Error = ()> + Send>,
future: Pin<Box<dyn Future<Output = ()> + Send>>,
) -> Result<(), SpawnError> {
let mut s = &*self;
tokio_executor::Executor::spawn(&mut s, future)
@@ -154,7 +157,7 @@ impl<'a> tokio_executor::Executor for &'a Sender {
fn spawn(
&mut self,
future: Box<dyn Future<Item = (), Error = ()> + Send>,
future: Pin<Box<dyn Future<Output = ()> + Send>>,
) -> Result<(), SpawnError> {
self.prepare_for_spawn()?;
@@ -175,34 +178,14 @@ impl<'a> tokio_executor::Executor for &'a Sender {
impl<T> tokio_executor::TypedExecutor<T> for Sender
where
T: Future<Item = (), Error = ()> + Send + 'static,
T: Future<Output = ()> + Send + 'static,
{
fn status(&self) -> Result<(), tokio_executor::SpawnError> {
tokio_executor::Executor::status(self)
}
fn spawn(&mut self, future: T) -> Result<(), SpawnError> {
tokio_executor::Executor::spawn(self, Box::new(future))
}
}
impl<T> future::Executor<T> for Sender
where
T: Future<Item = (), Error = ()> + Send + 'static,
{
fn execute(&self, future: T) -> Result<(), future::ExecuteError<T>> {
if let Err(e) = tokio_executor::Executor::status(self) {
let kind = if e.is_at_capacity() {
future::ExecuteErrorKind::NoCapacity
} else {
future::ExecuteErrorKind::Shutdown
};
return Err(future::ExecuteError::new(kind, future));
}
let _ = self.spawn(future);
Ok(())
tokio_executor::Executor::spawn(self, Box::pin(future))
}
}
+20 -11
View File
@@ -1,9 +1,13 @@
use crate::task::Task;
use crate::worker;
use tokio_sync::task::AtomicWaker;
use crossbeam_deque::Injector;
use futures::task::AtomicTask;
use futures::{Async, Future, Poll};
use std::future::Future;
use std::pin::Pin;
use std::sync::{Arc, Mutex};
use std::task::{Context, Poll};
/// Future that resolves when the thread pool is shutdown.
///
@@ -27,7 +31,7 @@ pub struct Shutdown {
#[derive(Debug)]
struct Inner {
/// The task to notify when the threadpool completes the shutdown process.
task: AtomicTask,
task: AtomicWaker,
/// `true` if the threadpool has been shut down.
completed: bool,
}
@@ -38,20 +42,25 @@ impl Shutdown {
inner: trigger.inner.clone(),
}
}
/// Wait for the shutdown to complete
pub fn wait(self) {
let mut enter = tokio_executor::enter().unwrap();
enter.block_on(self);
}
}
impl Future for Shutdown {
type Item = ();
type Error = ();
type Output = ();
fn poll(&mut self) -> Poll<(), ()> {
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
let inner = self.inner.lock().unwrap();
if !inner.completed {
inner.task.register();
Ok(Async::NotReady)
inner.task.register_by_ref(cx.waker());
Poll::Pending
} else {
Ok(().into())
Poll::Ready(())
}
}
}
@@ -74,7 +83,7 @@ impl ShutdownTrigger {
) -> ShutdownTrigger {
ShutdownTrigger {
inner: Arc::new(Mutex::new(Inner {
task: AtomicTask::new(),
task: AtomicWaker::new(),
completed: false,
})),
workers,
@@ -96,6 +105,6 @@ impl Drop for ShutdownTrigger {
// Notify the task interested in shutdown.
let mut inner = self.inner.lock().unwrap();
inner.completed = true;
inner.task.notify();
inner.task.wake();
}
}
+8 -4
View File
@@ -1,12 +1,13 @@
use crate::pool::Pool;
use crate::task::{BlockingState, Task};
use futures::{Async, Poll};
use std::cell::UnsafeCell;
use std::fmt;
use std::ptr;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release};
use std::sync::Arc;
use std::task::Poll;
use std::thread;
/// Manages the state around entering a blocking section and tasks that are
@@ -109,7 +110,10 @@ impl Blocking {
///
/// The caller must ensure that `task` has not previously been queued to be
/// notified when capacity becomes available.
pub fn poll_blocking_capacity(&self, task: &Arc<Task>) -> Poll<(), crate::BlockingError> {
pub fn poll_blocking_capacity(
&self,
task: &Arc<Task>,
) -> Poll<Result<(), crate::BlockingError>> {
// This requires atomically claiming blocking capacity and if none is
// available, queuing &task.
@@ -193,7 +197,7 @@ impl Blocking {
// The node was queued to be notified once capacity is made
// available.
Ok(Async::NotReady)
Poll::Pending
}
None => {
debug_assert!(curr.remaining_capacity() > 0);
@@ -208,7 +212,7 @@ impl Blocking {
}
// Capacity has been obtained
Ok(().into())
Poll::Ready(Ok(()))
}
}
}
+51 -41
View File
@@ -5,15 +5,17 @@ mod state;
pub(crate) use self::blocking::{Blocking, CanBlock};
use self::blocking_state::BlockingState;
use self::state::State;
use crate::notifier::Notifier;
use crate::pool::Pool;
use futures::executor::{self, Spawn};
use futures::{self, Async, Future};
use crate::waker::Waker;
use log::trace;
use std::cell::{Cell, UnsafeCell};
use std::future::Future;
use std::pin::Pin;
use std::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release};
use std::sync::atomic::{AtomicPtr, AtomicUsize};
use std::sync::Arc;
use std::task::{Context, Poll};
use std::{fmt, panic, ptr};
/// Harness around a future.
@@ -48,7 +50,7 @@ pub(crate) struct Task {
/// Store the future at the head of the struct
///
/// The future is dropped immediately when it transitions to Complete
future: UnsafeCell<Option<Spawn<BoxFuture>>>,
future: UnsafeCell<Option<BoxFuture>>,
}
#[derive(Debug)]
@@ -58,31 +60,27 @@ pub(crate) enum Run {
Complete,
}
type BoxFuture = Box<dyn Future<Item = (), Error = ()> + Send + 'static>;
type BoxFuture = Pin<Box<dyn Future<Output = ()> + Send + 'static>>;
// ===== impl Task =====
impl Task {
/// Create a new `Task` as a harness for `future`.
pub fn new(future: BoxFuture) -> Task {
// Wrap the future with an execution context.
let task_fut = executor::spawn(future);
Task {
state: AtomicUsize::new(State::new().into()),
blocking: AtomicUsize::new(BlockingState::new().into()),
next_blocking: AtomicPtr::new(ptr::null_mut()),
reg_worker: Cell::new(None),
reg_index: Cell::new(0),
future: UnsafeCell::new(Some(task_fut)),
future: UnsafeCell::new(Some(future)),
}
}
/// Create a fake `Task` to be used as part of the intrusive mpsc channel
/// algorithm.
fn stub() -> Task {
let future = Box::new(futures::empty()) as BoxFuture;
let task_fut = executor::spawn(future);
let future = Box::pin(Empty) as BoxFuture;
Task {
state: AtomicUsize::new(State::stub().into()),
@@ -90,18 +88,18 @@ impl Task {
next_blocking: AtomicPtr::new(ptr::null_mut()),
reg_worker: Cell::new(None),
reg_index: Cell::new(0),
future: UnsafeCell::new(Some(task_fut)),
future: UnsafeCell::new(Some(future)),
}
}
/// Execute the task returning `Run::Schedule` if the task needs to be
/// scheduled again.
pub fn run(&self, unpark: &Arc<Notifier>) -> Run {
pub fn run(me: &Arc<Task>, pool: &Arc<Pool>) -> Run {
use self::State::*;
// Transition task to running state. At this point, the task must be
// scheduled.
let actual: State = self
let actual: State = me
.state
.compare_and_swap(Scheduled.into(), Running.into(), AcqRel)
.into();
@@ -111,14 +109,11 @@ impl Task {
_ => panic!("unexpected task state; {:?}", actual),
}
trace!(
"Task::run; state={:?}",
State::from(self.state.load(Relaxed))
);
trace!("Task::run; state={:?}", State::from(me.state.load(Relaxed)));
// The transition to `Running` done above ensures that a lock on the
// future has been obtained.
let fut = unsafe { &mut (*self.future.get()) };
let fut = unsafe { &mut (*me.future.get()) };
// This block deals with the future panicking while being polled.
//
@@ -126,7 +121,7 @@ impl Task {
// `thread::panicking() -> true`. To do this, the future is dropped from
// within the catch_unwind block.
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
struct Guard<'a>(&'a mut Option<Spawn<BoxFuture>>, bool);
struct Guard<'a>(&'a mut Option<BoxFuture>, bool);
impl<'a> Drop for Guard<'a> {
fn drop(&mut self) {
@@ -139,10 +134,14 @@ impl Task {
let mut g = Guard(fut, true);
let ret =
g.0.as_mut()
.unwrap()
.poll_future_notify(unpark, self as *const _ as usize);
let mut waker = arc_waker::waker(Arc::new(Waker {
task: me.clone(),
pool: pool.clone(),
}));
let mut cx = Context::from_waker(&mut waker);
let ret = g.0.as_mut().unwrap().as_mut().poll(&mut cx);
g.1 = false;
@@ -150,7 +149,7 @@ impl Task {
}));
match res {
Ok(Ok(Async::Ready(_))) | Ok(Err(_)) | Err(_) => {
Ok(Poll::Ready(_)) | Err(_) => {
trace!(" -> task complete");
// The future has completed. Drop it immediately to free
@@ -158,20 +157,20 @@ impl Task {
//
// The `Task` harness will stay around longer if it is contained
// by any of the various queues.
self.drop_future();
me.drop_future();
// Transition to the completed state
self.state.store(State::Complete.into(), Release);
me.state.store(State::Complete.into(), Release);
if let Err(panic_err) = res {
if let Some(ref f) = unpark.pool.config.panic_handler {
if let Some(ref f) = pool.config.panic_handler {
f(panic_err);
}
}
Run::Complete
}
Ok(Ok(Async::NotReady)) => {
Ok(Poll::Pending) => {
trace!(" -> not ready");
// Attempt to transition from Running -> Idle, if successful,
@@ -179,7 +178,7 @@ impl Task {
// fails, then the task has been unparked concurrent to running,
// in which case it transitions immediately back to scheduled
// and we return `true`.
let prev: State = self
let prev: State = me
.state
.compare_and_swap(Running.into(), Idle.into(), AcqRel)
.into();
@@ -187,7 +186,7 @@ impl Task {
match prev {
Running => Run::Idle,
Notified => {
self.state.store(Scheduled.into(), Release);
me.state.store(Scheduled.into(), Release);
Run::Schedule
}
_ => unreachable!(),
@@ -231,23 +230,23 @@ impl Task {
}
}
/// Notify the task
pub fn notify(me: Arc<Task>, pool: &Arc<Pool>) {
if me.schedule() {
let _ = pool.submit(me, pool);
}
}
/// Notify the task it has been allocated blocking capacity
pub fn notify_blocking(me: Arc<Task>, pool: &Arc<Pool>) {
BlockingState::notify_blocking(&me.blocking, AcqRel);
Task::notify(me, pool);
Task::schedule(&me, pool);
}
pub fn schedule(me: &Arc<Self>, pool: &Arc<Pool>) {
if me.schedule2() {
let task = me.clone();
let _ = pool.submit(task, &pool);
}
}
/// Transition the task state to scheduled.
///
/// Returns `true` if the caller is permitted to schedule the task.
pub fn schedule(&self) -> bool {
fn schedule2(&self) -> bool {
use self::State::*;
loop {
@@ -300,7 +299,18 @@ impl fmt::Debug for Task {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("Task")
.field("state", &self.state)
.field("future", &"Spawn<BoxFuture>")
.field("future", &"BoxFuture")
.finish()
}
}
struct Empty;
impl Future for Empty {
type Output = ();
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> {
// Never used
unreachable!();
}
}
+15 -4
View File
@@ -2,8 +2,8 @@ use crate::builder::Builder;
use crate::pool::Pool;
use crate::sender::Sender;
use crate::shutdown::{Shutdown, ShutdownTrigger};
use futures::sync::oneshot;
use futures::{Future, Poll};
use std::future::Future;
use std::sync::Arc;
/// Work-stealing based thread pool for executing futures.
@@ -72,11 +72,14 @@ impl ThreadPool {
/// version that returns a `Result` instead of panicking.
pub fn spawn<F>(&self, future: F)
where
F: Future<Item = (), Error = ()> + Send + 'static,
F: Future<Output = ()> + Send + 'static,
{
self.sender().spawn(future).unwrap();
}
/*
* TODO: Bring back
/// Spawn a future on to the thread pool, return a future representing
/// the produced value.
///
@@ -114,6 +117,8 @@ impl ThreadPool {
SpawnHandle(oneshot::spawn(future, self.sender()))
}
*/
/// Return a reference to the sender handle
///
/// The handle is used to spawn futures onto the thread pool. It also
@@ -183,11 +188,15 @@ impl Drop for ThreadPool {
drop(inner);
// Wait until all worker threads terminate and the threadpool's resources clean up.
let _ = shutdown.wait();
let mut enter = tokio_executor::enter().unwrap();
enter.block_on(shutdown);
}
}
}
/*
* TODO: Bring back
/// Handle returned from ThreadPool::spawn_handle.
///
/// This handle is a future representing the completion of a different future
@@ -205,3 +214,5 @@ impl<T, E> Future for SpawnHandle<T, E> {
self.0.poll()
}
}
*/
+24
View File
@@ -0,0 +1,24 @@
use crate::pool::Pool;
use crate::task::Task;
use arc_waker::Wake;
use std::sync::Arc;
/// Implements the future `Waker` API.
///
/// This is how external events are able to signal the task, informing it to try
/// to poll the future again.
#[derive(Debug)]
pub(crate) struct Waker {
pub pool: Arc<Pool>,
pub task: Arc<Task>,
}
unsafe impl Send for Waker {}
unsafe impl Sync for Waker {}
impl Wake for Waker {
fn wake_by_ref(me: &Arc<Self>) {
Task::schedule(&me.task, &me.pool);
}
}
+23 -27
View File
@@ -6,21 +6,22 @@ pub(crate) use self::entry::WorkerEntry as Entry;
pub(crate) use self::stack::Stack;
pub(crate) use self::state::{Lifecycle, State};
use crate::notifier::Notifier;
use crate::pool::{self, BackupId, Pool};
use crate::sender::Sender;
use crate::shutdown::ShutdownTrigger;
use crate::task::{self, CanBlock, Task};
use futures::{Async, Poll};
use tokio_executor;
use log::trace;
use std::cell::Cell;
use std::marker::PhantomData;
use std::rc::Rc;
use std::sync::atomic::Ordering::{AcqRel, Acquire};
use std::sync::Arc;
use std::task::Poll;
use std::thread;
use std::time::Duration;
use tokio_executor;
/// Thread worker
///
@@ -148,7 +149,7 @@ impl Worker {
}
/// Transition the current worker to a blocking worker
pub(crate) fn transition_to_blocking(&self) -> Poll<(), crate::BlockingError> {
pub(crate) fn transition_to_blocking(&self) -> Poll<Result<(), crate::BlockingError>> {
use self::CanBlock::*;
// If we get this far, then `current_task` has been set.
@@ -161,7 +162,7 @@ impl Worker {
// The task has already requested capacity to block, but there is
// none yet available.
NoCapacity => return Ok(Async::NotReady),
NoCapacity => return Poll::Pending,
// The task has yet to ask for capacity
CanRequest => {
@@ -169,12 +170,12 @@ impl Worker {
// is available, register the task to be notified once capacity
// becomes available.
match self.pool.poll_blocking_capacity(task_ref)? {
Async::Ready(()) => {
Poll::Ready(()) => {
self.current_task.set_can_block(Allocated);
}
Async::NotReady => {
Poll::Pending => {
self.current_task.set_can_block(NoCapacity);
return Ok(Async::NotReady);
return Poll::Pending;
}
}
}
@@ -187,7 +188,7 @@ impl Worker {
if self.is_blocking.get() {
// The thread is already in blocking mode, so there is nothing else
// to do. Return `Ready` and allow the caller to block the thread.
return Ok(().into());
return Poll::Ready(Ok(()));
}
trace!("transition to blocking state");
@@ -200,7 +201,7 @@ impl Worker {
// Track that the thread has now fully entered the blocking state.
self.is_blocking.set(true);
Ok(().into())
Poll::Ready(Ok(()))
}
/// Transition from blocking
@@ -223,11 +224,6 @@ impl Worker {
const MAX_SPINS: usize = 3;
const LIGHT_SLEEP_INTERVAL: usize = 32;
// Get the notifier.
let notify = Arc::new(Notifier {
pool: self.pool.clone(),
});
let mut first = true;
let mut spin_cnt = 0;
let mut tick = 0;
@@ -236,7 +232,7 @@ impl Worker {
first = false;
// Run the next available task
if self.try_run_task(&notify) {
if self.try_run_task(&self.pool) {
if self.is_blocking.get() {
// Exit out of the run state
return;
@@ -291,12 +287,12 @@ impl Worker {
///
/// Returns `true` if work was found.
#[inline]
fn try_run_task(&self, notify: &Arc<Notifier>) -> bool {
if self.try_run_owned_task(notify) {
fn try_run_task(&self, pool: &Arc<Pool>) -> bool {
if self.try_run_owned_task(pool) {
return true;
}
self.try_steal_task(notify)
self.try_steal_task(pool)
}
/// Checks the worker's current state, updating it as needed.
@@ -381,11 +377,11 @@ impl Worker {
/// Runs the next task on this worker's queue.
///
/// Returns `true` if work was found.
fn try_run_owned_task(&self, notify: &Arc<Notifier>) -> bool {
fn try_run_owned_task(&self, pool: &Arc<Pool>) -> bool {
// Poll the internal queue for a task to run
match self.entry().pop_task() {
Some(task) => {
self.run_task(task, notify);
self.run_task(task, pool);
true
}
None => false,
@@ -395,7 +391,7 @@ impl Worker {
/// Tries to steal a task from another worker.
///
/// Returns `true` if work was found
fn try_steal_task(&self, notify: &Arc<Notifier>) -> bool {
fn try_steal_task(&self, pool: &Arc<Pool>) -> bool {
use crossbeam_deque::Steal;
debug_assert!(!self.is_blocking.get());
@@ -411,7 +407,7 @@ impl Worker {
Steal::Success(task) => {
trace!("stole task from another worker");
self.run_task(task, notify);
self.run_task(task, pool);
trace!(
"try_steal_task -- signal_work; self={}; from={}",
@@ -444,7 +440,7 @@ impl Worker {
found_work
}
fn run_task(&self, task: Arc<Task>, notify: &Arc<Notifier>) {
fn run_task(&self, task: Arc<Task>, pool: &Arc<Pool>) {
use crate::task::Run::*;
// If this is the first time this task is being polled, register it so that we can keep
@@ -454,7 +450,7 @@ impl Worker {
self.entry().register_task(&task);
}
let run = self.run_task2(&task, notify);
let run = self.run_task2(&task, pool);
// TODO: Try to claim back the worker state in case the backup thread
// did not start up fast enough. This is a performance optimization.
@@ -528,7 +524,7 @@ impl Worker {
///
/// Great care is needed to ensure that `current_task` is unset in this
/// function.
fn run_task2(&self, task: &Arc<Task>, notify: &Arc<Notifier>) -> task::Run {
fn run_task2(&self, task: &Arc<Task>, pool: &Arc<Pool>) -> task::Run {
struct Guard<'a> {
worker: &'a Worker,
}
@@ -562,7 +558,7 @@ impl Worker {
// function returns, even if the return is caused by a panic.
let _g = Guard { worker: self };
task.run(notify)
Task::run(task, pool)
}
/// Put the worker to sleep