mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-08-21 00:00:10 +02:00
Terminate backup threads when idle (#489)
This commit is contained in:
committed by
Carl Lerche
parent
e5b2681513
commit
9352249c3e
@@ -5,6 +5,7 @@ use std::cell::UnsafeCell;
|
||||
use std::fmt;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::atomic::Ordering::{self, Acquire, AcqRel, Relaxed};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
/// State associated with a thread in the thread pool.
|
||||
///
|
||||
@@ -155,7 +156,8 @@ impl Backup {
|
||||
}
|
||||
|
||||
/// Wait for a worker handoff
|
||||
pub fn wait_for_handoff(&self, sleep: bool) -> Handoff {
|
||||
pub fn wait_for_handoff(&self, timeout: Option<Duration>) -> Handoff {
|
||||
let sleep_until = timeout.map(|dur| Instant::now() + dur);
|
||||
let mut state: State = self.state.load(Acquire).into();
|
||||
|
||||
// Run in a loop since there can be spurious wakeups
|
||||
@@ -169,36 +171,40 @@ impl Backup {
|
||||
(*self.handoff.get()).take()
|
||||
.expect("no worker handoff")
|
||||
};
|
||||
|
||||
return Handoff::Worker(worker_id);
|
||||
}
|
||||
|
||||
if sleep {
|
||||
// TODO: Park with a timeout
|
||||
self.park.park_sync(None);
|
||||
|
||||
// Reload the state
|
||||
state = self.state.load(Acquire).into();
|
||||
debug_assert!(state.is_running());
|
||||
} else {
|
||||
debug_assert!(state.is_running());
|
||||
|
||||
// Transition out of running
|
||||
let mut next = state;
|
||||
next.unset_running();
|
||||
|
||||
let actual = self.state.compare_and_swap(
|
||||
state.into(),
|
||||
next.into(),
|
||||
AcqRel).into();
|
||||
|
||||
if actual == state {
|
||||
debug_assert!(!next.is_running());
|
||||
|
||||
return Handoff::Idle;
|
||||
match sleep_until {
|
||||
None => {
|
||||
self.park.park_sync(None);
|
||||
state = self.state.load(Acquire).into();
|
||||
}
|
||||
Some(when) => {
|
||||
let now = Instant::now();
|
||||
|
||||
state = actual;
|
||||
if now < when {
|
||||
self.park.park_sync(Some(when - now));
|
||||
state = self.state.load(Acquire).into();
|
||||
} else {
|
||||
debug_assert!(state.is_running());
|
||||
|
||||
// Transition out of running
|
||||
let mut next = state;
|
||||
next.unset_running();
|
||||
|
||||
let actual = self.state.compare_and_swap(
|
||||
state.into(),
|
||||
next.into(),
|
||||
AcqRel).into();
|
||||
|
||||
if actual == state {
|
||||
debug_assert!(!next.is_running());
|
||||
return Handoff::Idle;
|
||||
}
|
||||
|
||||
state = actual;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,6 +417,8 @@ impl Pool {
|
||||
break;
|
||||
}
|
||||
|
||||
debug_assert!(!inner.backup[backup_id.0].is_pushed());
|
||||
|
||||
// Push the thread back onto the backup stack. This makes it
|
||||
// available for future handoffs.
|
||||
//
|
||||
@@ -437,20 +439,14 @@ impl Pool {
|
||||
|
||||
// Wait for a handoff
|
||||
let handoff = inner.backup[backup_id.0]
|
||||
.wait_for_handoff(true);
|
||||
.wait_for_handoff(inner.config.keep_alive);
|
||||
|
||||
match handoff {
|
||||
Handoff::Worker(id) => {
|
||||
debug_assert!(inner.backup[backup_id.0].is_running());
|
||||
worker_id = id;
|
||||
}
|
||||
Handoff::Idle => {
|
||||
// Worker is idle
|
||||
break;
|
||||
}
|
||||
Handoff::Terminated => {
|
||||
// TODO: When wait_for_handoff supports blocking with a
|
||||
// timeout, this will have to be smarter
|
||||
Handoff::Idle | Handoff::Terminated => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user