15
15
// specific language governing permissions and limitations
16
16
// under the License.
17
17
18
- use futures:: FutureExt ;
19
- use std:: any:: Any ;
18
+ use crate :: trace_utils:: { trace_block, trace_future} ;
20
19
use std:: future:: Future ;
21
20
use std:: task:: { Context , Poll } ;
22
21
use tokio:: runtime:: Handle ;
23
22
use tokio:: task:: { AbortHandle , Id , JoinError , LocalSet } ;
24
23
25
- pub mod trace_utils {
26
- use super :: * ;
27
- use futures:: future:: BoxFuture ;
28
- use tokio:: sync:: OnceCell ;
29
-
30
- /// A trait for injecting instrumentation into either asynchronous futures or
31
- /// blocking closures at runtime.
32
- pub trait JoinSetTracer : Send + Sync + ' static {
33
- /// Function pointer type for tracing a future.
34
- ///
35
- /// This function takes a boxed future (with its output type erased)
36
- /// and returns a boxed future (with its output still erased). The
37
- /// tracer must apply instrumentation without altering the output.
38
- fn trace_future (
39
- & self ,
40
- fut : BoxFuture < ' static , Box < dyn Any + Send > > ,
41
- ) -> BoxFuture < ' static , Box < dyn Any + Send > > ;
42
-
43
- /// Function pointer type for tracing a blocking closure.
44
- ///
45
- /// This function takes a boxed closure (with its return type erased)
46
- /// and returns a boxed closure (with its return type still erased). The
47
- /// tracer must apply instrumentation without changing the return value.
48
- fn trace_block (
49
- & self ,
50
- f : Box < dyn FnOnce ( ) -> Box < dyn Any + Send > + Send > ,
51
- ) -> Box < dyn FnOnce ( ) -> Box < dyn Any + Send > + Send > ;
52
- }
53
-
54
- /// A no-op tracer that does not modify or instrument any futures or closures.
55
- /// This is used as a fallback if no custom tracer is set.
56
- struct NoopTracer ;
57
-
58
- impl JoinSetTracer for NoopTracer {
59
- fn trace_future (
60
- & self ,
61
- fut : BoxFuture < ' static , Box < dyn Any + Send > > ,
62
- ) -> BoxFuture < ' static , Box < dyn Any + Send > > {
63
- fut
64
- }
65
-
66
- fn trace_block (
67
- & self ,
68
- f : Box < dyn FnOnce ( ) -> Box < dyn Any + Send > + Send > ,
69
- ) -> Box < dyn FnOnce ( ) -> Box < dyn Any + Send > + Send > {
70
- f
71
- }
72
- }
73
-
74
- /// Global storage for an injected tracer. If no tracer is injected, a no-op
75
- /// tracer is used instead. This ensures that calls to [`trace_future`] or
76
- /// [`trace_block`] never panic due to missing instrumentation.
77
- static GLOBAL_TRACER : OnceCell < & ' static dyn JoinSetTracer > = OnceCell :: const_new ( ) ;
78
-
79
- /// A no-op tracer singleton that is returned by [`get_tracer`] if no custom
80
- /// tracer has been registered.
81
- static NOOP_TRACER : NoopTracer = NoopTracer ;
82
-
83
- /// Return the currently registered tracer, or the no-op tracer if none was
84
- /// registered.
85
- #[ inline]
86
- fn get_tracer ( ) -> & ' static dyn JoinSetTracer {
87
- GLOBAL_TRACER . get ( ) . copied ( ) . unwrap_or ( & NOOP_TRACER )
88
- }
89
-
90
- /// Set the custom tracer for both futures and blocking closures.
91
- ///
92
- /// This should be called once at startup. If called more than once, an
93
- /// `Err(())` is returned. If not called at all, a no-op tracer that does nothing
94
- /// is used.
95
- pub fn set_join_set_tracer ( tracer : & ' static dyn JoinSetTracer ) -> Result < ( ) , ( ) > {
96
- GLOBAL_TRACER . set ( tracer) . map_err ( |_| ( ) )
97
- }
98
-
99
- /// Optionally instruments a future with custom tracing.
100
- ///
101
- /// If a tracer has been injected via `set_tracer`, the future's output is
102
- /// boxed (erasing its type), passed to the tracer, and then downcast back
103
- /// to the expected type. If no tracer is set, the original future is returned.
104
- ///
105
- /// # Type Parameters
106
- /// * `T` - The concrete output type of the future.
107
- /// * `F` - The future type.
108
- ///
109
- /// # Parameters
110
- /// * `future` - The future to potentially instrument.
111
- pub fn trace_future < T , F > ( future : F ) -> BoxFuture < ' static , T >
112
- where
113
- F : Future < Output = T > + Send + ' static ,
114
- T : Send + ' static ,
115
- {
116
- // Erase the future’s output type first:
117
- let erased_future = async move {
118
- let result = future. await ;
119
- Box :: new ( result) as Box < dyn Any + Send >
120
- }
121
- . boxed ( ) ;
122
-
123
- // Forward through the global tracer:
124
- get_tracer ( )
125
- . trace_future ( erased_future)
126
- // Downcast from `Box<dyn Any + Send>` back to `T`:
127
- . map ( |any_box| {
128
- * any_box
129
- . downcast :: < T > ( )
130
- . expect ( "Tracer must preserve the future’s output type!" )
131
- } )
132
- . boxed ( )
133
- }
134
-
135
- /// Optionally instruments a blocking closure with custom tracing.
136
- ///
137
- /// If a tracer has been injected via `set_tracer`, the closure is wrapped so that
138
- /// its return value is boxed (erasing its type), passed to the tracer, and then the
139
- /// result is downcast back to the original type. If no tracer is set, the closure is
140
- /// returned unmodified (except for being boxed).
141
- ///
142
- /// # Type Parameters
143
- /// * `T` - The concrete return type of the closure.
144
- /// * `F` - The closure type.
145
- ///
146
- /// # Parameters
147
- /// * `f` - The blocking closure to potentially instrument.
148
- pub fn trace_block < T , F > ( f : F ) -> Box < dyn FnOnce ( ) -> T + Send >
149
- where
150
- F : FnOnce ( ) -> T + Send + ' static ,
151
- T : Send + ' static ,
152
- {
153
- // Erase the closure’s return type first:
154
- let erased_closure = Box :: new ( || {
155
- let result = f ( ) ;
156
- Box :: new ( result) as Box < dyn Any + Send >
157
- } ) ;
158
-
159
- // Forward through the global tracer:
160
- let traced_closure = get_tracer ( ) . trace_block ( erased_closure) ;
161
-
162
- // Downcast from `Box<dyn Any + Send>` back to `T`:
163
- Box :: new ( move || {
164
- let any_box = traced_closure ( ) ;
165
- * any_box
166
- . downcast :: < T > ( )
167
- . expect ( "Tracer must preserve the closure’s return type!" )
168
- } )
169
- }
170
- }
171
-
172
24
/// A wrapper around Tokio's JoinSet that forwards all API calls while optionally
173
25
/// instrumenting spawned tasks and blocking closures with custom tracing behavior.
174
26
/// If no tracer is injected via `trace_utils::set_tracer`, tasks and closures are executed
@@ -211,7 +63,7 @@ impl<T: 'static> JoinSet<T> {
211
63
F : Send + ' static ,
212
64
T : Send ,
213
65
{
214
- self . inner . spawn ( trace_utils :: trace_future ( task) )
66
+ self . inner . spawn ( trace_future ( task) )
215
67
}
216
68
217
69
/// [JoinSet::spawn_on](tokio::task::JoinSet::spawn_on) - Spawn a task on a provided runtime.
@@ -221,7 +73,7 @@ impl<T: 'static> JoinSet<T> {
221
73
F : Send + ' static ,
222
74
T : Send ,
223
75
{
224
- self . inner . spawn_on ( trace_utils :: trace_future ( task) , handle)
76
+ self . inner . spawn_on ( trace_future ( task) , handle)
225
77
}
226
78
227
79
/// [JoinSet::spawn_local](tokio::task::JoinSet::spawn_local) - Spawn a local task.
@@ -249,7 +101,7 @@ impl<T: 'static> JoinSet<T> {
249
101
F : Send + ' static ,
250
102
T : Send ,
251
103
{
252
- self . inner . spawn_blocking ( trace_utils :: trace_block ( f) )
104
+ self . inner . spawn_blocking ( trace_block ( f) )
253
105
}
254
106
255
107
/// [JoinSet::spawn_blocking_on](tokio::task::JoinSet::spawn_blocking_on) - Spawn a blocking task on a provided runtime.
@@ -259,8 +111,7 @@ impl<T: 'static> JoinSet<T> {
259
111
F : Send + ' static ,
260
112
T : Send ,
261
113
{
262
- self . inner
263
- . spawn_blocking_on ( trace_utils:: trace_block ( f) , handle)
114
+ self . inner . spawn_blocking_on ( trace_block ( f) , handle)
264
115
}
265
116
266
117
/// [JoinSet::join_next](tokio::task::JoinSet::join_next) - Await the next completed task.
0 commit comments