pub struct TaskTracker(/* private fields */);
Expand description
Hierarchical task tracker with pluggable scheduling and error policies
TaskTracker provides a composable system for managing background tasks with:
- Configurable scheduling via
TaskScheduler
implementations - Flexible error handling via
OnErrorPolicy
implementations - Parent-child relationships with independent metrics
- Cancellation propagation and isolation
- Built-in cancellation token support
Built on top of tokio_util::task::TaskTracker
for robust task lifecycle management.
§Example
// Create a task tracker with semaphore-based scheduling
let scheduler = SemaphoreScheduler::with_permits(3);
let policy = LogOnlyPolicy::new();
let root = TaskTracker::builder()
.scheduler(scheduler)
.error_policy(policy)
.build()?;
// Spawn some tasks
let handle1 = root.spawn(async { Ok(1) });
let handle2 = root.spawn(async { Ok(2) });
// Get results and join all tasks
let result1 = handle1.await.unwrap().unwrap();
let result2 = handle2.await.unwrap().unwrap();
assert_eq!(result1, 1);
assert_eq!(result2, 2);
Implementations§
Source§impl TaskTracker
impl TaskTracker
Sourcepub fn builder() -> TaskTrackerBuilder
pub fn builder() -> TaskTrackerBuilder
Create a new root task tracker using the builder pattern
This is the preferred way to create new task trackers.
§Example
let scheduler = SemaphoreScheduler::with_permits(10);
let error_policy = LogOnlyPolicy::new();
let tracker = TaskTracker::builder()
.scheduler(scheduler)
.error_policy(error_policy)
.build()?;
Sourcepub fn new(
scheduler: Arc<dyn TaskScheduler>,
error_policy: Arc<dyn OnErrorPolicy>,
) -> Result<Self>
pub fn new( scheduler: Arc<dyn TaskScheduler>, error_policy: Arc<dyn OnErrorPolicy>, ) -> Result<Self>
Create a new root task tracker with simple parameters (legacy)
This method is kept for backward compatibility. Use builder()
for new code.
Uses default metrics (no Prometheus integration).
§Arguments
scheduler
- Scheduling policy to use for all taskserror_policy
- Error handling policy for this tracker
§Example
let scheduler = SemaphoreScheduler::with_permits(10);
let error_policy = LogOnlyPolicy::new();
let tracker = TaskTracker::new(scheduler, error_policy)?;
Sourcepub fn new_with_prometheus<R: MetricsRegistry + ?Sized>(
scheduler: Arc<dyn TaskScheduler>,
error_policy: Arc<dyn OnErrorPolicy>,
registry: &R,
component_name: &str,
) -> Result<Self>
pub fn new_with_prometheus<R: MetricsRegistry + ?Sized>( scheduler: Arc<dyn TaskScheduler>, error_policy: Arc<dyn OnErrorPolicy>, registry: &R, component_name: &str, ) -> Result<Self>
Create a new root task tracker with Prometheus metrics integration
§Arguments
scheduler
- Scheduling policy to use for all taskserror_policy
- Error handling policy for this trackerregistry
- MetricsRegistry for Prometheus integrationcomponent_name
- Name for this tracker component
§Example
let scheduler = SemaphoreScheduler::with_permits(10);
let error_policy = LogOnlyPolicy::new();
let tracker = TaskTracker::new_with_prometheus(
scheduler,
error_policy,
registry.as_ref(),
"main_tracker"
)?;
Sourcepub fn child_tracker(&self) -> Result<TaskTracker>
pub fn child_tracker(&self) -> Result<TaskTracker>
Create a child tracker that inherits scheduling policy
The child tracker:
- Gets its own independent tokio TaskTracker
- Inherits the parent’s scheduler
- Gets a child error policy via
create_child()
- Has hierarchical metrics that chain to parent
- Gets a child cancellation token from the parent
- Is independent for cancellation (child cancellation doesn’t affect parent)
§Errors
Returns an error if the parent tracker is already closed
§Example
let child_tracker = root_tracker.child_tracker()?;
// Child inherits parent's policies but has separate metrics and lifecycle
Sourcepub fn spawn<F, T>(&self, future: F) -> TaskHandle<T> ⓘ
pub fn spawn<F, T>(&self, future: F) -> TaskHandle<T> ⓘ
Create a child tracker builder for flexible customization
The builder allows you to customize scheduling and error policies for the child tracker. If not specified, policies are inherited from the parent.
§Example
// Custom scheduler, inherit error policy
let child1 = root_tracker.child_tracker_builder()
.scheduler(SemaphoreScheduler::with_permits(5))
.build().unwrap();
// Custom error policy, inherit scheduler
let child2 = root_tracker.child_tracker_builder()
.error_policy(LogOnlyPolicy::new())
.build().unwrap();
// Both custom
let child3 = root_tracker.child_tracker_builder()
.scheduler(SemaphoreScheduler::with_permits(3))
.error_policy(LogOnlyPolicy::new())
.build().unwrap();
Spawn a new task
The task will be wrapped with scheduling and error handling logic,
then executed according to the configured policies. For tasks that
need to inspect cancellation tokens, use [spawn_cancellable
] instead.
§Arguments
future
- The async task to execute
§Returns
A TaskHandle
that can be used to await completion and access the task’s cancellation token
§Panics
Panics if the tracker has been closed. This indicates a programming error where tasks are being spawned after the tracker lifecycle has ended.
§Example
let handle = tracker.spawn(async {
// Your async work here
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
Ok(42)
});
// Access the task's cancellation token
let cancel_token = handle.cancellation_token();
let result = handle.await?;
Sourcepub fn spawn_cancellable<F, Fut, T>(&self, task_fn: F) -> TaskHandle<T> ⓘwhere
F: FnMut(CancellationToken) -> Fut + Send + 'static,
Fut: Future<Output = CancellableTaskResult<T>> + Send + 'static,
T: Send + 'static,
pub fn spawn_cancellable<F, Fut, T>(&self, task_fn: F) -> TaskHandle<T> ⓘwhere
F: FnMut(CancellationToken) -> Fut + Send + 'static,
Fut: Future<Output = CancellableTaskResult<T>> + Send + 'static,
T: Send + 'static,
Spawn a cancellable task that receives a cancellation token
This is useful for tasks that need to inspect the cancellation token
and gracefully handle cancellation within their logic. The task function
must return a CancellableTaskResult
to properly track cancellation vs errors.
§Arguments
task_fn
- Function that takes a cancellation token and returns a future that resolves toCancellableTaskResult<T>
§Returns
A TaskHandle
that can be used to await completion and access the task’s cancellation token
§Panics
Panics if the tracker has been closed. This indicates a programming error where tasks are being spawned after the tracker lifecycle has ended.
§Example
let handle = tracker.spawn_cancellable(|cancel_token| async move {
tokio::select! {
_ = tokio::time::sleep(std::time::Duration::from_millis(100)) => {
CancellableTaskResult::Ok(42)
},
_ = cancel_token.cancelled() => CancellableTaskResult::Cancelled,
}
});
// Access the task's individual cancellation token
let task_cancel_token = handle.cancellation_token();
let result = handle.await?;
Sourcepub fn metrics(&self) -> &dyn HierarchicalTaskMetrics
pub fn metrics(&self) -> &dyn HierarchicalTaskMetrics
Get metrics for this tracker
Metrics are specific to this tracker and do not include metrics from parent or child trackers.
§Example
let metrics = tracker.metrics();
println!("Success: {}, Failed: {}", metrics.success(), metrics.failed());
Sourcepub fn cancel(&self)
pub fn cancel(&self)
Cancel this tracker and all its tasks
This will signal cancellation to all currently running tasks and prevent new tasks from being spawned. The cancellation is immediate and forceful.
§Example
// Spawn a long-running task
let handle = tracker.spawn_cancellable(|cancel_token| async move {
tokio::select! {
_ = tokio::time::sleep(std::time::Duration::from_secs(10)) => {
dynamo_runtime::utils::tasks::tracker::CancellableTaskResult::Ok(42)
}
_ = cancel_token.cancelled() => {
dynamo_runtime::utils::tasks::tracker::CancellableTaskResult::Cancelled
}
}
}).await?;
// Cancel the tracker (and thus the task)
tracker.cancel();
Sourcepub fn cancellation_token(&self) -> CancellationToken
pub fn cancellation_token(&self) -> CancellationToken
Get the cancellation token for this tracker
This allows external code to observe or trigger cancellation of this tracker.
§Example
let token = tracker.cancellation_token();
// Can check cancellation state or cancel manually
if !token.is_cancelled() {
token.cancel();
}
Sourcepub fn child_count(&self) -> usize
pub fn child_count(&self) -> usize
Get the number of active child trackers
This counts only child trackers that are still alive (not dropped). Dropped child trackers are automatically cleaned up.
§Example
let child_count = tracker.child_count();
println!("This tracker has {} active children", child_count);
Sourcepub fn child_tracker_builder(&self) -> ChildTrackerBuilder<'_>
pub fn child_tracker_builder(&self) -> ChildTrackerBuilder<'_>
Create a child tracker builder with custom configuration
This provides fine-grained control over child tracker creation, allowing you to override the scheduler or error policy while maintaining the parent-child relationship.
§Example
// Custom scheduler, inherit error policy
let child1 = parent.child_tracker_builder()
.scheduler(SemaphoreScheduler::with_permits(5))
.build().unwrap();
// Custom error policy, inherit scheduler
let child2 = parent.child_tracker_builder()
.error_policy(LogOnlyPolicy::new())
.build().unwrap();
// Inherit both policies from parent
let child3 = parent.child_tracker_builder()
.build().unwrap();
Sourcepub async fn join(&self)
pub async fn join(&self)
Join this tracker and all child trackers
This method gracefully shuts down the entire tracker hierarchy by:
- Closing all trackers (preventing new task spawning)
- Waiting for all existing tasks to complete
Uses stack-safe traversal to prevent stack overflow in deep hierarchies. Children are processed before parents to ensure proper shutdown order.
Hierarchical Behavior:
- Processes children before parents to ensure proper shutdown order
- Each tracker is closed before waiting (Tokio requirement)
- Leaf trackers simply close and wait for their own tasks
§Example
tracker.join().await;
Trait Implementations§
Source§impl Clone for TaskTracker
impl Clone for TaskTracker
Source§fn clone(&self) -> TaskTracker
fn clone(&self) -> TaskTracker
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source
. Read moreAuto Trait Implementations§
impl Freeze for TaskTracker
impl !RefUnwindSafe for TaskTracker
impl Send for TaskTracker
impl Sync for TaskTracker
impl Unpin for TaskTracker
impl !UnwindSafe for TaskTracker
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left
is true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left(&self)
returns true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T
in a tonic::Request
Source§impl<T> Paint for Twhere
T: ?Sized,
impl<T> Paint for Twhere
T: ?Sized,
Source§fn fg(&self, value: Color) -> Painted<&T>
fn fg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self
with the foreground set to
value
.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like red()
and
green()
, which have the same functionality but are
pithier.
§Example
Set foreground color to white using fg()
:
use yansi::{Paint, Color};
painted.fg(Color::White);
Set foreground color to white using white()
.
use yansi::Paint;
painted.white();
Source§fn bright_black(&self) -> Painted<&T>
fn bright_black(&self) -> Painted<&T>
Source§fn bright_red(&self) -> Painted<&T>
fn bright_red(&self) -> Painted<&T>
Source§fn bright_green(&self) -> Painted<&T>
fn bright_green(&self) -> Painted<&T>
Source§fn bright_yellow(&self) -> Painted<&T>
fn bright_yellow(&self) -> Painted<&T>
Source§fn bright_blue(&self) -> Painted<&T>
fn bright_blue(&self) -> Painted<&T>
Source§fn bright_magenta(&self) -> Painted<&T>
fn bright_magenta(&self) -> Painted<&T>
Source§fn bright_cyan(&self) -> Painted<&T>
fn bright_cyan(&self) -> Painted<&T>
Source§fn bright_white(&self) -> Painted<&T>
fn bright_white(&self) -> Painted<&T>
Source§fn bg(&self, value: Color) -> Painted<&T>
fn bg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self
with the background set to
value
.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like on_red()
and
on_green()
, which have the same functionality but
are pithier.
§Example
Set background color to red using fg()
:
use yansi::{Paint, Color};
painted.bg(Color::Red);
Set background color to red using on_red()
.
use yansi::Paint;
painted.on_red();
Source§fn on_primary(&self) -> Painted<&T>
fn on_primary(&self) -> Painted<&T>
Source§fn on_magenta(&self) -> Painted<&T>
fn on_magenta(&self) -> Painted<&T>
Source§fn on_bright_black(&self) -> Painted<&T>
fn on_bright_black(&self) -> Painted<&T>
Source§fn on_bright_red(&self) -> Painted<&T>
fn on_bright_red(&self) -> Painted<&T>
Source§fn on_bright_green(&self) -> Painted<&T>
fn on_bright_green(&self) -> Painted<&T>
Source§fn on_bright_yellow(&self) -> Painted<&T>
fn on_bright_yellow(&self) -> Painted<&T>
Source§fn on_bright_blue(&self) -> Painted<&T>
fn on_bright_blue(&self) -> Painted<&T>
Source§fn on_bright_magenta(&self) -> Painted<&T>
fn on_bright_magenta(&self) -> Painted<&T>
Source§fn on_bright_cyan(&self) -> Painted<&T>
fn on_bright_cyan(&self) -> Painted<&T>
Source§fn on_bright_white(&self) -> Painted<&T>
fn on_bright_white(&self) -> Painted<&T>
Source§fn attr(&self, value: Attribute) -> Painted<&T>
fn attr(&self, value: Attribute) -> Painted<&T>
Enables the styling Attribute
value
.
This method should be used rarely. Instead, prefer to use
attribute-specific builder methods like bold()
and
underline()
, which have the same functionality
but are pithier.
§Example
Make text bold using attr()
:
use yansi::{Paint, Attribute};
painted.attr(Attribute::Bold);
Make text bold using using bold()
.
use yansi::Paint;
painted.bold();
Source§fn rapid_blink(&self) -> Painted<&T>
fn rapid_blink(&self) -> Painted<&T>
Source§fn quirk(&self, value: Quirk) -> Painted<&T>
fn quirk(&self, value: Quirk) -> Painted<&T>
Enables the yansi
Quirk
value
.
This method should be used rarely. Instead, prefer to use quirk-specific
builder methods like mask()
and
wrap()
, which have the same functionality but are
pithier.
§Example
Enable wrapping using .quirk()
:
use yansi::{Paint, Quirk};
painted.quirk(Quirk::Wrap);
Enable wrapping using wrap()
.
use yansi::Paint;
painted.wrap();
Source§fn clear(&self) -> Painted<&T>
👎Deprecated since 1.0.1: renamed to resetting()
due to conflicts with Vec::clear()
.
The clear()
method will be removed in a future release.
fn clear(&self) -> Painted<&T>
resetting()
due to conflicts with Vec::clear()
.
The clear()
method will be removed in a future release.Source§fn whenever(&self, value: Condition) -> Painted<&T>
fn whenever(&self, value: Condition) -> Painted<&T>
Conditionally enable styling based on whether the Condition
value
applies. Replaces any previous condition.
See the crate level docs for more details.
§Example
Enable styling painted
only when both stdout
and stderr
are TTYs:
use yansi::{Paint, Condition};
painted.red().on_yellow().whenever(Condition::STDOUTERR_ARE_TTY);