Skip to content
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

Implicitly inherit special Task local state during task spawning #6659

Closed
junderw opened this issue Jun 27, 2024 · 7 comments
Closed

Implicitly inherit special Task local state during task spawning #6659

junderw opened this issue Jun 27, 2024 · 7 comments
Labels
A-tokio Area: The main tokio crate C-feature-request Category: A feature request. M-task Module: tokio/task

Comments

@junderw
Copy link

junderw commented Jun 27, 2024

Is your feature request related to a problem? Please describe.
As mentioned in this reddit thread someone was trying to make a subscriber that does calls into hyper but silences all tracing events. However, it seems that hyper is spawning new tasks on a multi-threaded runtime, so thread local state and task local state is not possible to be stored to prevent re-entrancy loops into the subscriber.

Describe the solution you'd like
Setting a well known specific Task local state value will cause all tasks spawned from that Task to inherit the state. This will allow the subscriber to set a flag, and all of the spawned sub-tasks will contain that flag, and the subscriber can disable logging until it sees the flag return to its un-muted state.

@junderw junderw added A-tokio Area: The main tokio crate C-feature-request Category: A feature request. labels Jun 27, 2024
@Darksonn Darksonn added the M-task Module: tokio/task label Jun 27, 2024
@Darksonn
Copy link
Contributor

Tokio task locals do not support inheritance, and it's not possible to add inheritance to them. Their design is fundamentally tied to scopes, and task inheritance would not work with that assumption.

@junderw
Copy link
Author

junderw commented Jun 27, 2024

I was thinking something along the lines of adding a sort of wrapping instrumentation to all futures that are spawned ONLY IF one of these "special" task locals exists.

ie. at the top of tokio::spawn it would check for the existence of the task local state that is somehow flagged as needs inheritance, and if it exists, the passed in future would be wrapped in another future that would be passed in the value and instantiate the new task local on the new task. By placing this wrapped future in the worker queue, the first thing that task would do is to initialize the task local state of the new task.

@junderw
Copy link
Author

junderw commented Jul 1, 2024

Edit: Sorry for the confusion, "global pre-task closure" should be task-local. So when spawn is called it checks task-local state for the closure's existence.

Perhaps instead of adding inheritance, we could allow for a global pre-task closure F and store it in a global.

where
    F: (FnOnce() -> R) + Clone + Send + 'static,
    R: Future<Output = ()> + Send + 'static,

If a global pre-task closure is set, then every time a new task is created, the closure cloned, sent into the main future at the root of the task, and is run and the returned future is awaited at the start of the new task.

This will allow for people to coordinate between tasks started by libraries they use (but have no control over), by initializing task-local state within those closures.

The original problem from the reddit thread could then be solved by the subscriber setting the pre-task closure to set local task state in a well-known value, then subsequent calls into the subscriber could check for that state and silence itself, and at the end of the call, the subscriber would remove the pre-task closure.


I apologize if my solutions including the OP are fundamentally impossible and this whole thread turns into an education session... but the problem raised in that reddit thread really has me stumped.

@Darksonn
Copy link
Contributor

Darksonn commented Jul 2, 2024

It's not that a feature like this could never exist — it's just not compatible with how task locals are designed today, and so it would require adding a second kind of task local with a different implementation.

Unfortunately, I don't think I want to add such a feature at this time.

As for the problem from the Reddit thread, the recommended solution is to send logs from a separate Tokio runtime. You can ask on #tracing-users in the Tokio Discord server if you want more guidance on this.

@Darksonn Darksonn closed this as not planned Won't fix, can't repro, duplicate, stale Jul 2, 2024
joshka added a commit to joshka/hyper-util that referenced this issue Jul 3, 2024
This makes it possible to trace the entire request, including DNS
resolution.

The use case for this is to be able to suppress specific requests from
being traced in a situation where the request is used to transmit logs
to a remote server. This would result in an infinite loop of logs being
sent to the remote server. tokio-rs/tokio#6659
has more details.
@joshka
Copy link
Contributor

joshka commented Jul 3, 2024

One way to solve the underlying problem is to suppress specific HTTP requests from being logged. Assign a specific span to those request and then just filter the events under that span out. It may still be useful to send these logs, but they should just be queued to go with the next block of logs rather than being immediately sent to avoid the infinite loop.

Right now (assuming you're using reqwest), hyper-utils dns resolution gets called. This spawns a blocking thread to do dns resolution, which does not pick up the current span. This is addressed with a one liner PR: hyperium/hyper-util#134

There may be similar specific instances like this in hyper where the span is not carried through to a task. I couldn't find anything obvious however.

@joshka
Copy link
Contributor

joshka commented Jul 18, 2024

Not sure what the overlap in tokio vs hyper is by that PR seems like it would be a simple one to approve / merge if someone has 2 minutes.

@Darksonn
Copy link
Contributor

Pinging in this repo won't notify the right people.

seanmonstar pushed a commit to hyperium/hyper-util that referenced this issue Aug 7, 2024
…134)

This makes it possible to trace the entire request, including DNS
resolution.

The use case for this is to be able to suppress specific requests from
being traced in a situation where the request is used to transmit logs
to a remote server. This would result in an infinite loop of logs being
sent to the remote server. tokio-rs/tokio#6659
has more details.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-tokio Area: The main tokio crate C-feature-request Category: A feature request. M-task Module: tokio/task
Projects
None yet
Development

No branches or pull requests

3 participants