-
Notifications
You must be signed in to change notification settings - Fork 35
export more schedule info via Extras
#86
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| // Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. | ||
|
|
||
| use crate::pool::{Local, Runner}; | ||
| use crate::queue::{Pop, TaskCell}; | ||
| use crate::queue::{AcquireState, Pop, TaskCell}; | ||
| use parking_lot_core::SpinWait; | ||
|
|
||
| pub(crate) struct WorkerThread<T, R> { | ||
|
|
@@ -20,31 +20,52 @@ where | |
| T: TaskCell + Send, | ||
| R: Runner<TaskCell = T>, | ||
| { | ||
| /// Pops a task from the queue. | ||
| /// Returns `Some((Pop<T>, AcquireState))` if a task is found, where `AcquireState` indicates | ||
| /// how the task was acquired (immediate, after spin, or after park). | ||
| #[inline] | ||
| fn pop(&mut self) -> Option<Pop<T>> { | ||
| fn pop(&mut self, retry_after_park: bool) -> Option<(Pop<T>, AcquireState)> { | ||
| // Wait some time before going to sleep, which is more expensive. | ||
| let mut spin = SpinWait::new(); | ||
| let mut state = if retry_after_park { | ||
| AcquireState::AfterPark | ||
| } else { | ||
| AcquireState::Immediate | ||
| }; | ||
| loop { | ||
| if let Some(t) = self.local.pop() { | ||
| return Some(t); | ||
| return Some((t, state)); | ||
| } | ||
| if !spin.spin() { | ||
| break; | ||
| } | ||
| if state == AcquireState::Immediate { | ||
| state = AcquireState::AfterSpin; | ||
| } | ||
|
Comment on lines
+42
to
+44
|
||
| } | ||
| self.runner.pause(&mut self.local); | ||
| let t = self.local.pop_or_sleep(); | ||
| self.runner.resume(&mut self.local); | ||
| t | ||
| t.map(|task| (task, AcquireState::AfterPark)) | ||
| } | ||
|
|
||
| pub fn run(mut self) { | ||
| self.runner.start(&mut self.local); | ||
| let mut retry_after_park = false; | ||
| while !self.local.core().is_shutdown() { | ||
| let task = match self.pop() { | ||
| Some(t) => t, | ||
| None => continue, | ||
| let (mut task, acquire_state) = match self.pop(retry_after_park) { | ||
| Some(t) => { | ||
| retry_after_park = false; | ||
| t | ||
| } | ||
| None => { | ||
| retry_after_park = true; | ||
| continue; | ||
| } | ||
| }; | ||
| let extras = task.task_cell.mut_extras(); | ||
| extras.acquire_state = Some(acquire_state); | ||
| extras.task_source = Some(task.task_source); | ||
| self.runner.handle(&mut self.local, task.task_cell); | ||
| } | ||
| self.runner.end(&mut self.local); | ||
|
|
@@ -59,7 +80,7 @@ mod tests { | |
| use super::*; | ||
| use crate::pool::spawn::*; | ||
| use crate::pool::SchedConfig; | ||
| use crate::queue::QueueType; | ||
| use crate::queue::{AcquireState, Extras, QueueType, TaskCell, TaskSource}; | ||
| use crate::task::callback; | ||
| use std::sync::atomic::AtomicUsize; | ||
| use std::sync::*; | ||
|
|
@@ -116,6 +137,56 @@ mod tests { | |
| } | ||
| } | ||
|
|
||
| struct InspectTask { | ||
| extras: Extras, | ||
| } | ||
|
|
||
| impl InspectTask { | ||
| fn new() -> Self { | ||
| InspectTask { | ||
| extras: Extras::single_level(), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl TaskCell for InspectTask { | ||
| fn mut_extras(&mut self) -> &mut Extras { | ||
| &mut self.extras | ||
| } | ||
| } | ||
|
|
||
| enum Event { | ||
| Paused, | ||
| Handled(TaskSource, AcquireState), | ||
| } | ||
|
|
||
| struct InspectRunner { | ||
| tx: mpsc::Sender<Event>, | ||
| } | ||
|
|
||
| impl crate::pool::Runner for InspectRunner { | ||
| type TaskCell = InspectTask; | ||
|
|
||
| fn handle( | ||
| &mut self, | ||
| _local: &mut Local<Self::TaskCell>, | ||
| mut task_cell: Self::TaskCell, | ||
| ) -> bool { | ||
| let extras = task_cell.mut_extras(); | ||
| let task_source = extras.task_source().unwrap(); | ||
| let acquire_state = extras.acquire_state().unwrap(); | ||
| self.tx | ||
| .send(Event::Handled(task_source, acquire_state)) | ||
| .unwrap(); | ||
| true | ||
| } | ||
|
|
||
| fn pause(&mut self, _local: &mut Local<Self::TaskCell>) -> bool { | ||
| self.tx.send(Event::Paused).unwrap(); | ||
| true | ||
| } | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_hooks() { | ||
| let (tx, rx) = mpsc::channel(); | ||
|
|
@@ -151,4 +222,69 @@ mod tests { | |
| expected_metrics.end = 1; | ||
| assert_eq!(expected_metrics, *metrics.lock().unwrap()); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_worker_run_task_from_local_immediate() { | ||
| let mut config: SchedConfig = Default::default(); | ||
| config.max_thread_count = 1; | ||
| config.core_thread_count = AtomicUsize::new(1); | ||
| let (remote, mut locals) = build_spawn(QueueType::SingleLevel, config); | ||
| let (tx, rx) = mpsc::channel(); | ||
| let runner = InspectRunner { tx }; | ||
|
|
||
| let mut local = locals.remove(0); | ||
| local.spawn(InspectTask::new()); // spawn a local task before worker starts | ||
| let th = WorkerThread::new(local, runner); | ||
| let handle = std::thread::spawn(move || { | ||
| th.run(); | ||
| }); | ||
|
|
||
| match rx.recv_timeout(Duration::from_secs(1)).unwrap() { | ||
| Event::Handled(task_source, acquire_state) => { | ||
| assert_eq!(task_source, TaskSource::LocalQueue); | ||
| assert_eq!(acquire_state, AcquireState::Immediate); | ||
| } | ||
| Event::Paused => panic!("did not expect pause before handling task"), | ||
| } | ||
|
|
||
| remote.stop(); | ||
| handle.join().unwrap(); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_worker_run_task_from_global_after_park() { | ||
| let mut config: SchedConfig = Default::default(); | ||
| config.max_thread_count = 1; | ||
| config.core_thread_count = AtomicUsize::new(1); | ||
| let (remote, mut locals) = build_spawn(QueueType::SingleLevel, config); | ||
| let (tx, rx) = mpsc::channel(); | ||
| let runner = InspectRunner { tx }; | ||
|
|
||
| let th = WorkerThread::new(locals.remove(0), runner); | ||
| let handle = std::thread::spawn(move || { | ||
| th.run(); | ||
| }); | ||
|
|
||
| match rx.recv_timeout(Duration::from_secs(1)).unwrap() { | ||
| Event::Paused => {} | ||
| Event::Handled(_, _) => panic!("expected pause before handling task"), | ||
| } | ||
|
|
||
| remote.spawn(InspectTask::new()); | ||
|
|
||
| let deadline = Instant::now() + Duration::from_secs(1); | ||
| let (task_source, acquire_state) = loop { | ||
| let timeout = deadline.saturating_duration_since(Instant::now()); | ||
| let event = rx.recv_timeout(timeout).unwrap(); | ||
| if let Event::Handled(task_source, acquire_state) = event { | ||
| break (task_source, acquire_state); | ||
| } | ||
| }; | ||
|
|
||
| assert_eq!(task_source, TaskSource::GlobalQueue); | ||
| assert_eq!(acquire_state, AcquireState::AfterPark); | ||
|
|
||
| remote.stop(); | ||
| handle.join().unwrap(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -14,7 +14,7 @@ pub mod priority; | |||||||||||||||||||||||||||||||
| mod extras; | ||||||||||||||||||||||||||||||||
| mod single_level; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| pub use self::extras::Extras; | ||||||||||||||||||||||||||||||||
| pub use self::extras::{AcquireState, Extras, TaskSource}; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| use std::time::Instant; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
@@ -74,9 +74,8 @@ pub struct Pop<T> { | |||||||||||||||||||||||||||||||
| /// When the task was pushed to the queue. | ||||||||||||||||||||||||||||||||
| pub schedule_time: Instant, | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| /// Whether the task comes from the current [`LocalQueue`] instead of being | ||||||||||||||||||||||||||||||||
| /// just stolen from the injector or other [`LocalQueue`]s. | ||||||||||||||||||||||||||||||||
| pub from_local: bool, | ||||||||||||||||||||||||||||||||
| /// The source of the task, indicating where the task comes from. | ||||||||||||||||||||||||||||||||
| pub task_source: TaskSource, | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
| impl<T> Pop<T> { | |
| /// Returns whether this task was popped from a local queue. | |
| /// | |
| /// This provides a compatibility shim for the former `from_local: bool` | |
| /// field, using the newer `task_source` information instead. | |
| #[deprecated( | |
| since = "0.0.0", | |
| note = "use `task_source` instead; this accessor is kept for backward compatibility" | |
| )] | |
| pub fn from_local(&self) -> bool { | |
| self.task_source.is_local() | |
| } | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When
retry_after_parkis true,stateis initialized toAfterPark, so if a task is popped immediately in this call it will still be labeledAfterParkeven though no spinning/parking happened during this acquisition. Consider derivingAcquireStatebased on what happened in the currentpop()call (or clarify the semantics in docs), rather than carrying state across iterations viaretry_after_park.