Make blocking pool non-static and use for thread pool (#1678)

Previously, support for `blocking` was done through a static `POOL` that
would spawn threads on demand. While this made the pool accessible at
all times, it made it hard to configure, and it was impossible to keep
multiple blocking pools.

This patch changes `blocking` to instead use a "default" global like the
ones used for timers, executors, and the like. There is now
`blocking::with_pool`, which is used by both thread-pool workers and the
current-thread runtime to ensure that a pool is available to tasks.

This patch also changes `ThreadPool` to spawn its worker threads on the
blocking pool rather than as free-standing threads. This is in
preparation for the coming in-place blocking work.

One downside of this change is that thread names are no longer
"semantic". All threads are named by the pool name, and individual
threads are not (currently) given names with numerical suffixes like
before.
This commit is contained in:
Jon Gjengset
2019-10-24 14:17:47 -07:00
committed by Carl Lerche
parent 99940aeeb4
commit 03a9378297
18 changed files with 646 additions and 327 deletions
+62 -41
View File
@@ -16,23 +16,19 @@ pub(crate) struct Worker<P: Park + 'static> {
park: P,
}
struct Entry<P: 'static> {
pool: Arc<Set<P>>,
index: usize,
}
pub(crate) fn create_set<F, P>(
pool_size: usize,
mk_park: F,
blocking: Arc<crate::blocking::Pool>,
) -> (Arc<Set<P::Unpark>>, Vec<Worker<P>>)
where
P: Park,
P: Send + Park,
F: FnMut(usize) -> P,
{
// Create the parks...
let parks: Vec<_> = (0..pool_size).map(mk_park).collect();
let mut pool = Arc::new(Set::new(pool_size, |i| parks[i].unpark()));
let mut pool = Arc::new(Set::new(pool_size, |i| parks[i].unpark(), blocking));
// Establish the circular link between the individual worker state
// structure and the container.
@@ -42,7 +38,10 @@ where
let workers = parks
.into_iter()
.enumerate()
.map(|(index, park)| Worker::new(pool.clone(), index, park))
.map(|(index, park)| {
// unsafe is safe because we call Worker::new only once with each index in the pool
unsafe { Worker::new(pool.clone(), index, park) }
})
.collect();
(pool, workers)
@@ -56,28 +55,39 @@ const GLOBAL_POLL_INTERVAL: u16 = 61;
impl<P> Worker<P>
where
P: Park + 'static,
P: Send + Park,
{
pub(super) fn new(pool: Arc<Set<P::Unpark>>, index: usize, park: P) -> Self {
// unsafe because new may only be called once for each index in pool's set
pub(super) unsafe fn new(pool: Arc<Set<P::Unpark>>, index: usize, park: P) -> Self {
Worker {
entry: Entry { pool, index },
entry: Entry::new(pool, index),
park,
}
}
pub(super) fn run(&mut self) {
let mut executor = &*self.entry.pool;
let entry = &self.entry;
pub(super) fn run(mut self) {
let pool = Arc::clone(&self.entry.pool);
let pool = &pool;
let index = self.entry.index;
let mut executor = &**pool;
let entry = &mut self.entry;
let park = &mut self.park;
let blocking = &executor.blocking;
// Track the current worker
current::set(&entry.pool, entry.index, || {
current::set(&pool, index, || {
let _enter = crate::enter().expect("executor already running on thread");
crate::with_default(&mut executor, || {
entry.run(park);
crate::blocking::with_pool(blocking, || entry.run(park))
})
})
});
}
pub(super) fn id(&self) -> usize {
self.entry.index
}
#[cfg(test)]
@@ -96,11 +106,21 @@ where
}
}
struct Entry<P: 'static> {
pool: Arc<Set<P>>,
index: usize,
}
impl<P> Entry<P>
where
P: Unpark,
{
fn run(&self, park: &mut impl Park<Unpark = P>) {
// unsafe because Entry::owned assumes there is only one instance of the Entry
unsafe fn new(pool: Arc<Set<P>>, index: usize) -> Self {
Entry { pool, index }
}
fn run(&mut self, park: &mut impl Park<Unpark = P>) {
while self.is_running() {
if self.tick(park) {
self.park(park);
@@ -110,12 +130,12 @@ where
self.shutdown(park);
}
fn is_running(&self) -> bool {
fn is_running(&mut self) -> bool {
self.owned().is_running.get()
}
/// Returns `true` if the worker needs to park
fn tick(&self, park: &mut impl Park<Unpark = P>) -> bool {
fn tick(&mut self, park: &mut impl Park<Unpark = P>) -> bool {
// Process all pending tasks in the local queue.
if !self.process_local_queue(park) {
return false;
@@ -140,7 +160,7 @@ where
///
/// Returns `false` if processing was interrupted due to the pool shutting
/// down.
fn process_local_queue(&self, park: &mut impl Park<Unpark = P>) -> bool {
fn process_local_queue(&mut self, park: &mut impl Park<Unpark = P>) -> bool {
debug_assert!(self.is_running());
loop {
@@ -171,7 +191,7 @@ where
}
}
fn steal_work(&self) -> Option<Task<Shared<P>>> {
fn steal_work(&mut self) -> Option<Task<Shared<P>>> {
let num_workers = self.pool.len();
let start = self.owned().rand.fastrand_n(num_workers as u32);
@@ -185,17 +205,16 @@ where
/// Runs maintenance work such as free pending tasks and check the pool's
/// state.
fn maintenance(&self) {
fn maintenance(&mut self) {
// Free any completed tasks
self.drain_tasks_pending_drop();
// Update the pool state cache
self.owned()
.is_running
.set(!self.owned().work_queue.is_closed());
let closed = self.owned().work_queue.is_closed();
self.owned().is_running.set(!closed)
}
fn search_for_work(&self) -> bool {
fn search_for_work(&mut self) -> bool {
debug_assert!(self.is_searching());
if let Some(task) = self.steal_work() {
@@ -208,7 +227,7 @@ where
}
}
fn transition_to_searching(&self) -> bool {
fn transition_to_searching(&mut self) -> bool {
if self.is_searching() {
return true;
}
@@ -218,7 +237,7 @@ where
ret
}
fn transition_from_searching(&self) {
fn transition_from_searching(&mut self) {
debug_assert!(self.is_searching());
self.owned().is_searching.set(false);
@@ -231,11 +250,13 @@ where
}
/// Returns `true` if the worker must check for any work.
fn transition_to_parked(&self) -> bool {
fn transition_to_parked(&mut self) -> bool {
let idx = self.index;
let is_searching = self.is_searching();
let ret = self
.set()
.idle()
.transition_worker_to_parked(self.index, self.is_searching());
.transition_worker_to_parked(idx, is_searching);
// The worker is no longer searching. Setting this is the local cache
// only.
@@ -249,7 +270,7 @@ where
}
/// Returns `true` if the transition happened.
fn transition_from_parked(&self) -> bool {
fn transition_from_parked(&mut self) -> bool {
if self.owned().did_submit_task.get() || !self.is_running() {
// Remove the worker from the sleep set.
self.set().idle().unpark_worker_by_id(self.index);
@@ -270,7 +291,7 @@ where
}
}
fn run_task(&self, task: Task<Shared<P>>) {
fn run_task(&mut self, task: Task<Shared<P>>) {
if self.is_searching() {
self.transition_from_searching();
}
@@ -281,13 +302,13 @@ where
}
}
fn final_work_sweep(&self) {
fn final_work_sweep(&mut self) {
if !self.owned().work_queue.is_empty() {
self.set().notify_work();
}
}
fn park(&self, park: &mut impl Park<Unpark = P>) {
fn park(&mut self, park: &mut impl Park<Unpark = P>) {
if self.transition_to_parked() {
// We are the final searching worker, check if any work arrived
// before parking
@@ -309,7 +330,7 @@ where
}
}
fn park_light(&self, park: &mut impl Park<Unpark = P>) {
fn park_light(&mut self, park: &mut impl Park<Unpark = P>) {
// When tasks are submitted locally (from the parker), defer any
// notifications in hopes that the curent worker will grab those tasks.
self.owned().defer_notification.set(true);
@@ -326,7 +347,7 @@ where
}
}
fn drain_tasks_pending_drop(&self) {
fn drain_tasks_pending_drop(&mut self) {
for task in self.shared().pending_drop.drain() {
unsafe {
let owned = &mut *self.set().owned()[self.index].get();
@@ -340,7 +361,7 @@ where
///
/// Once the shutdown flag has been observed, it is guaranteed that no
/// further tasks may be pushed into the global queue.
fn shutdown(&self, park: &mut impl Park<Unpark = P>) {
fn shutdown(&mut self, park: &mut impl Park<Unpark = P>) {
// Transition all tasks owned by the worker to canceled.
self.owned().owned_tasks.shutdown();
@@ -369,13 +390,13 @@ where
}
/// Increment the tick, returning the value from before the increment.
fn tick_fetch_inc(&self) -> u16 {
fn tick_fetch_inc(&mut self) -> u16 {
let tick = self.owned().tick.get();
self.owned().tick.set(tick.wrapping_add(1));
tick
}
fn is_searching(&self) -> bool {
fn is_searching(&mut self) -> bool {
self.owned().is_searching.get()
}
@@ -387,7 +408,7 @@ where
&self.set().shared()[self.index]
}
fn owned(&self) -> &Owned<P> {
fn owned(&mut self) -> &Owned<P> {
// safety: we own the slot
unsafe { &*self.set().owned()[self.index].get() }
}