6
6
//! [Tokio]: https://crates.io/crates/tokio
7
7
//! [async-std]: https://crates.io/crates/async-std
8
8
9
- use futures_util:: { future :: BoxFuture , stream :: Stream } ;
9
+ use futures_util:: stream :: { unfold , Stream } ;
10
10
use std:: { fmt:: Debug , future:: Future , time:: Duration } ;
11
11
use thiserror:: Error ;
12
12
13
13
/// A runtime is an abstraction of an async runtime like [Tokio] or [async-std]. It allows
14
- /// OpenTelemetry to work with any current and hopefully future runtime implementation .
14
+ /// OpenTelemetry to work with any current and hopefully future runtime implementations .
15
15
///
16
16
/// [Tokio]: https://crates.io/crates/tokio
17
17
/// [async-std]: https://crates.io/crates/async-std
18
+ ///
19
+ /// # Note
20
+ ///
21
+ /// OpenTelemetry expects a *multithreaded* runtime because its types can move across threads.
22
+ /// For this reason, this trait requires the `Send` and `Sync` bounds. Single-threaded runtimes
23
+ /// can implement this trait in a way that spawns the tasks on the same thread as the calling code.
18
24
#[ cfg( feature = "experimental_async_runtime" ) ]
19
25
pub trait Runtime : Clone + Send + Sync + ' static {
20
- /// A future stream, which returns items in a previously specified interval. The item type is
21
- /// not important.
22
- type Interval : Stream + Send ;
23
-
24
- /// A future, which resolves after a previously specified amount of time. The output type is
25
- /// not important.
26
- type Delay : Future + Send + Unpin ;
27
-
28
- /// Create a [futures_util::stream::Stream], which returns a new item every
29
- /// [std::time::Duration].
30
- fn interval ( & self , duration : Duration ) -> Self :: Interval ;
31
-
32
26
/// Spawn a new task or thread, which executes the given future.
33
27
///
34
28
/// # Note
35
29
///
36
30
/// This is mainly used to run batch span processing in the background. Note, that the function
37
31
/// does not return a handle. OpenTelemetry will use a different way to wait for the future to
38
- /// finish when TracerProvider gets shutdown. At the moment this happens by blocking the
32
+ /// finish when the caller shuts down.
33
+ ///
34
+ /// At the moment, the shutdown happens by blocking the
39
35
/// current thread. This means runtime implementations need to make sure they can still execute
40
36
/// the given future even if the main thread is blocked.
41
- fn spawn ( & self , future : BoxFuture < ' static , ( ) > ) ;
37
+ fn spawn < F > ( & self , future : F )
38
+ where
39
+ F : Future < Output = ( ) > + Send + ' static ;
40
+
41
+ /// Return a future that resolves after the specified [Duration].
42
+ fn delay ( & self , duration : Duration ) -> impl Future < Output = ( ) > + Send + ' static ;
43
+ }
42
44
43
- /// Return a new future, which resolves after the specified [std::time::Duration].
44
- fn delay ( & self , duration : Duration ) -> Self :: Delay ;
45
+ /// Uses the given runtime to produce an interval stream.
46
+ #[ cfg( feature = "experimental_async_runtime" ) ]
47
+ #[ allow( dead_code) ]
48
+ pub ( crate ) fn to_interval_stream < T : Runtime > (
49
+ runtime : T ,
50
+ interval : Duration ,
51
+ ) -> impl Stream < Item = ( ) > {
52
+ unfold ( ( ) , move |_| {
53
+ let runtime_cloned = runtime. clone ( ) ;
54
+
55
+ async move {
56
+ runtime_cloned. delay ( interval) . await ;
57
+ Some ( ( ( ) , ( ) ) )
58
+ }
59
+ } )
45
60
}
46
61
47
62
/// Runtime implementation, which works with Tokio's multi thread runtime.
@@ -59,21 +74,17 @@ pub struct Tokio;
59
74
doc( cfg( all( feature = "experimental_async_runtime" , feature = "rt-tokio" ) ) )
60
75
) ]
61
76
impl Runtime for Tokio {
62
- type Interval = tokio_stream:: wrappers:: IntervalStream ;
63
- type Delay = :: std:: pin:: Pin < Box < tokio:: time:: Sleep > > ;
64
-
65
- fn interval ( & self , duration : Duration ) -> Self :: Interval {
66
- crate :: util:: tokio_interval_stream ( duration)
67
- }
68
-
69
- fn spawn ( & self , future : BoxFuture < ' static , ( ) > ) {
77
+ fn spawn < F > ( & self , future : F )
78
+ where
79
+ F : Future < Output = ( ) > + Send + ' static ,
80
+ {
70
81
#[ allow( clippy:: let_underscore_future) ]
71
82
// we don't have to await on the returned future to execute
72
83
let _ = tokio:: spawn ( future) ;
73
84
}
74
85
75
- fn delay ( & self , duration : Duration ) -> Self :: Delay {
76
- Box :: pin ( tokio:: time:: sleep ( duration) )
86
+ fn delay ( & self , duration : Duration ) -> impl Future < Output = ( ) > + Send + ' static {
87
+ tokio:: time:: sleep ( duration)
77
88
}
78
89
}
79
90
@@ -104,14 +115,10 @@ pub struct TokioCurrentThread;
104
115
) ) )
105
116
) ]
106
117
impl Runtime for TokioCurrentThread {
107
- type Interval = tokio_stream:: wrappers:: IntervalStream ;
108
- type Delay = :: std:: pin:: Pin < Box < tokio:: time:: Sleep > > ;
109
-
110
- fn interval ( & self , duration : Duration ) -> Self :: Interval {
111
- crate :: util:: tokio_interval_stream ( duration)
112
- }
113
-
114
- fn spawn ( & self , future : BoxFuture < ' static , ( ) > ) {
118
+ fn spawn < F > ( & self , future : F )
119
+ where
120
+ F : Future < Output = ( ) > + Send + ' static ,
121
+ {
115
122
// We cannot force push tracing in current thread tokio scheduler because we rely on
116
123
// BatchSpanProcessor to export spans in a background task, meanwhile we need to block the
117
124
// shutdown function so that the runtime will not finish the blocked task and kill any
@@ -127,8 +134,8 @@ impl Runtime for TokioCurrentThread {
127
134
} ) ;
128
135
}
129
136
130
- fn delay ( & self , duration : Duration ) -> Self :: Delay {
131
- Box :: pin ( tokio:: time:: sleep ( duration) )
137
+ fn delay ( & self , duration : Duration ) -> impl Future < Output = ( ) > + Send + ' static {
138
+ tokio:: time:: sleep ( duration)
132
139
}
133
140
}
134
141
@@ -147,20 +154,16 @@ pub struct AsyncStd;
147
154
doc( cfg( all( feature = "experimental_async_runtime" , feature = "rt-async-std" ) ) )
148
155
) ]
149
156
impl Runtime for AsyncStd {
150
- type Interval = async_std:: stream:: Interval ;
151
- type Delay = BoxFuture < ' static , ( ) > ;
152
-
153
- fn interval ( & self , duration : Duration ) -> Self :: Interval {
154
- async_std:: stream:: interval ( duration)
155
- }
156
-
157
- fn spawn ( & self , future : BoxFuture < ' static , ( ) > ) {
157
+ fn spawn < F > ( & self , future : F )
158
+ where
159
+ F : Future < Output = ( ) > + Send + ' static ,
160
+ {
158
161
#[ allow( clippy:: let_underscore_future) ]
159
162
let _ = async_std:: task:: spawn ( future) ;
160
163
}
161
164
162
- fn delay ( & self , duration : Duration ) -> Self :: Delay {
163
- Box :: pin ( async_std:: task:: sleep ( duration) )
165
+ fn delay ( & self , duration : Duration ) -> impl Future < Output = ( ) > + Send + ' static {
166
+ async_std:: task:: sleep ( duration)
164
167
}
165
168
}
166
169
@@ -193,7 +196,7 @@ pub enum TrySendError {
193
196
/// Send failed due to the channel being closed.
194
197
#[ error( "cannot send message to batch processor as the channel is closed" ) ]
195
198
ChannelClosed ,
196
- /// Any other send error that isnt covered above.
199
+ /// Any other send error that isn't covered above.
197
200
#[ error( transparent) ]
198
201
Other ( #[ from] Box < dyn std:: error:: Error + Send + Sync + ' static > ) ,
199
202
}
0 commit comments