Skip to content

RFC: Task Hook Inheritance #7306

@Noah-Kennedy

Description

@Noah-Kennedy

Problem

Task hooks are the ideal mechanism for users of Tokio to implement their own telemetry. As they allow users to instrument actions such as spawning, termination, and polling, they give users a mechanism to instrument both the lifecycle and running loop of individual tasks.

Unfortunately, the current API does not offer a great way to track state between hooks. We offer a struct which contains an id which could be used as an index into an out-of-band map, but that requires expensive synchronization which is wholly unnecessary - the operations we expose via hooks are already implicitly synchronized by the runtime. More importantly however, it is often required that a tree of tasks be tracked together.

For example, Cloudflare has existing instrumentation for our legacy proxy stack that allows us to track the time spent processing a given request by an event loop, which we would like to carry over to new Rust proxy instances we are spinning up. We can safely assume that the sub-tree of tasks stemming from a top-level task are related, so if we could track state somehow across a tree of tasks in hooks, we could implicitly instrument a whole sub-tree without making invasive modifications to every crate in the tree which spawn Tokio tasks so that we can pass along context information and centrally record which task ids are children of which parents.

Solution

#7197 overhauls task hooks to use a harness trait object which provides implementations of task hooks. The user-implemented type can store whatever state the user wants to store. Propagation of parent-to-child state is handled by a hook which allows parent task harnesses to return a hook harness for a child which is being spawned. So long as default implementations are provided, adding new hooks will not be a breaking change.

Alternatives

I attempted to construct a mechanism to allow users to pass through a set of hooks which shared a generic type for their state to the runtime builder which tokio would box so that we could keep the existing API, but that could not be made to work with hooks passed to new tasks as explicit overrides to any inheritance when spawning, and was a nightmare to deal with. Another alternative which were explored included passing Box<dyn Any> to hooks to allow them to store whatever they wanted.

Both of those also required adding a hook which would be invoked when a parent task spawned a child so that parent tasks could marshall data via an OOB mechanism the user would come up with which the child task's spawn hook would pull in. The harnesses system accomplish the same thing, works quite similarly under the hood, and is much simpler to use.

Metadata

Metadata

Assignees

Labels

A-tokioArea: The main tokio crateC-feature-requestCategory: A feature request.C-musingCategory: musings about a better worldC-proposalCategory: a proposal and request for commentsM-runtimeModule: tokio/runtimeM-taskModule: tokio/task

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions