66//! [Tokio]: https://crates.io/crates/tokio
77//! [async-std]: https://crates.io/crates/async-std
88
9- use futures_util:: { future :: BoxFuture , stream :: Stream } ;
9+ use futures_util:: stream :: { unfold , Stream } ;
1010use std:: { fmt:: Debug , future:: Future , time:: Duration } ;
1111use thiserror:: Error ;
1212
1313/// 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 .
1515///
1616/// [Tokio]: https://crates.io/crates/tokio
1717/// [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.
1824#[ cfg( feature = "experimental_async_runtime" ) ]
1925pub 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-
3226 /// Spawn a new task or thread, which executes the given future.
3327 ///
3428 /// # Note
3529 ///
3630 /// This is mainly used to run batch span processing in the background. Note, that the function
3731 /// 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
3935 /// current thread. This means runtime implementations need to make sure they can still execute
4036 /// 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+ }
4244
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+ } )
4560}
4661
4762/// Runtime implementation, which works with Tokio's multi thread runtime.
@@ -59,21 +74,17 @@ pub struct Tokio;
5974 doc( cfg( all( feature = "experimental_async_runtime" , feature = "rt-tokio" ) ) )
6075) ]
6176impl 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+ {
7081 #[ allow( clippy:: let_underscore_future) ]
7182 // we don't have to await on the returned future to execute
7283 let _ = tokio:: spawn ( future) ;
7384 }
7485
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)
7788 }
7889}
7990
@@ -104,14 +115,10 @@ pub struct TokioCurrentThread;
104115 ) ) )
105116) ]
106117impl 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+ {
115122 // We cannot force push tracing in current thread tokio scheduler because we rely on
116123 // BatchSpanProcessor to export spans in a background task, meanwhile we need to block the
117124 // shutdown function so that the runtime will not finish the blocked task and kill any
@@ -127,8 +134,8 @@ impl Runtime for TokioCurrentThread {
127134 } ) ;
128135 }
129136
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)
132139 }
133140}
134141
@@ -147,20 +154,16 @@ pub struct AsyncStd;
147154 doc( cfg( all( feature = "experimental_async_runtime" , feature = "rt-async-std" ) ) )
148155) ]
149156impl 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+ {
158161 #[ allow( clippy:: let_underscore_future) ]
159162 let _ = async_std:: task:: spawn ( future) ;
160163 }
161164
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)
164167 }
165168}
166169
@@ -193,7 +196,7 @@ pub enum TrySendError {
193196 /// Send failed due to the channel being closed.
194197 #[ error( "cannot send message to batch processor as the channel is closed" ) ]
195198 ChannelClosed ,
196- /// Any other send error that isnt covered above.
199+ /// Any other send error that isn't covered above.
197200 #[ error( transparent) ]
198201 Other ( #[ from] Box < dyn std:: error:: Error + Send + Sync + ' static > ) ,
199202}
0 commit comments