From 8287e520b947cb289f1f2e2b7f7d9106a8cc0f0f Mon Sep 17 00:00:00 2001 From: John Nunley Date: Sun, 25 Dec 2022 07:12:59 -0800 Subject: [PATCH] Implement debug output to be better (#33) --- src/lib.rs | 90 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 83 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 24fbfac..5a1d203 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,12 +20,13 @@ #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] +use std::fmt; use std::future::Future; use std::marker::PhantomData; use std::panic::{RefUnwindSafe, UnwindSafe}; use std::rc::Rc; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; -use std::sync::{Arc, Mutex, RwLock}; +use std::sync::{Arc, Mutex, RwLock, TryLockError}; use std::task::{Poll, Waker}; use async_lock::OnceCell; @@ -61,7 +62,6 @@ pub use async_task::Task; /// drop(signal); /// })); /// ``` -#[derive(Debug)] pub struct Executor<'a> { /// The executor state. state: OnceCell>, @@ -76,6 +76,12 @@ unsafe impl Sync for Executor<'_> {} impl UnwindSafe for Executor<'_> {} impl RefUnwindSafe for Executor<'_> {} +impl fmt::Debug for Executor<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + debug_executor(self, "Executor", f) + } +} + impl<'a> Executor<'a> { /// Creates a new executor. /// @@ -290,7 +296,6 @@ impl<'a> Default for Executor<'a> { /// println!("Hello world!"); /// })); /// ``` -#[derive(Debug)] pub struct LocalExecutor<'a> { /// The inner executor. inner: Executor<'a>, @@ -302,6 +307,12 @@ pub struct LocalExecutor<'a> { impl UnwindSafe for LocalExecutor<'_> {} impl RefUnwindSafe for LocalExecutor<'_> {} +impl fmt::Debug for LocalExecutor<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + debug_executor(&self.inner, "LocalExecutor", f) + } +} + impl<'a> LocalExecutor<'a> { /// Creates a single-threaded executor. /// @@ -459,7 +470,6 @@ impl<'a> Default for LocalExecutor<'a> { } /// The state of a executor. -#[derive(Debug)] struct State { /// The global queue. queue: ConcurrentQueue, @@ -510,7 +520,6 @@ impl State { } /// A list of sleeping tickers. -#[derive(Debug)] struct Sleepers { /// Number of sleeping tickers (both notified and unnotified). count: usize, @@ -587,7 +596,6 @@ impl Sleepers { } /// Runs task one by one. -#[derive(Debug)] struct Ticker<'a> { /// The executor state. state: &'a State, @@ -708,7 +716,6 @@ impl Drop for Ticker<'_> { /// A worker in a work-stealing executor. /// /// This is just a ticker that also has an associated local queue for improved cache locality. -#[derive(Debug)] struct Runner<'a> { /// The executor state. state: &'a State, @@ -833,6 +840,75 @@ fn steal(src: &ConcurrentQueue, dest: &ConcurrentQueue) { } } +/// Debug implementation for `Executor` and `LocalExecutor`. +fn debug_executor(executor: &Executor<'_>, name: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Get a reference to the state. + let state = match executor.state.get() { + Some(state) => state, + None => { + // The executor has not been initialized. + struct Uninitialized; + + impl fmt::Debug for Uninitialized { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("") + } + } + + return f.debug_tuple(name).field(&Uninitialized).finish(); + } + }; + + /// Debug wrapper for the number of active tasks. + struct ActiveTasks<'a>(&'a Mutex>); + + impl fmt::Debug for ActiveTasks<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0.try_lock() { + Ok(lock) => fmt::Debug::fmt(&lock.len(), f), + Err(TryLockError::WouldBlock) => f.write_str(""), + Err(TryLockError::Poisoned(_)) => f.write_str(""), + } + } + } + + /// Debug wrapper for the local runners. + struct LocalRunners<'a>(&'a RwLock>>>); + + impl fmt::Debug for LocalRunners<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0.try_read() { + Ok(lock) => f + .debug_list() + .entries(lock.iter().map(|queue| queue.len())) + .finish(), + Err(TryLockError::WouldBlock) => f.write_str(""), + Err(TryLockError::Poisoned(_)) => f.write_str(""), + } + } + } + + /// Debug wrapper for the sleepers. + struct SleepCount<'a>(&'a Mutex); + + impl fmt::Debug for SleepCount<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0.try_lock() { + Ok(lock) => fmt::Debug::fmt(&lock.count, f), + Err(TryLockError::WouldBlock) => f.write_str(""), + Err(TryLockError::Poisoned(_)) => f.write_str(""), + } + } + } + + f.debug_struct(name) + .field("active", &ActiveTasks(&state.active)) + .field("global_tasks", &state.queue.len()) + .field("local_runners", &LocalRunners(&state.local_queues)) + .field("sleepers", &SleepCount(&state.sleepers)) + .finish() +} + /// Runs a closure when dropped. struct CallOnDrop(F);