15
15
use std:: sync:: atomic:: { AtomicUsize , Ordering } ;
16
16
use std:: sync:: Arc ;
17
17
18
+ use anyhow:: anyhow;
18
19
use await_tree:: InstrumentAwait ;
19
20
use futures:: future:: join_all;
20
- use futures:: pin_mut;
21
21
use hytra:: TrAdder ;
22
22
use minitrace:: prelude:: * ;
23
23
use parking_lot:: Mutex ;
@@ -173,33 +173,33 @@ where
173
173
} ;
174
174
175
175
let mut last_epoch: Option < EpochPair > = None ;
176
-
177
- let stream = Box :: new ( self . consumer ) . execute ( ) ;
178
- pin_mut ! ( stream) ;
176
+ let mut stream = Box :: pin ( Box :: new ( self . consumer ) . execute ( ) ) ;
179
177
180
178
// Drive the streaming task with an infinite loop
181
- while let Some ( barrier) = stream
182
- . next ( )
183
- . in_span ( span)
184
- . instrument_await (
185
- last_epoch. map_or ( "Epoch <initial>" . into ( ) , |e| format ! ( "Epoch {}" , e. curr) ) ,
186
- )
187
- . await
188
- . transpose ( ) ?
189
- {
190
- last_epoch = Some ( barrier. epoch ) ;
179
+ let result = loop {
180
+ let barrier = match stream
181
+ . try_next ( )
182
+ . in_span ( span)
183
+ . instrument_await (
184
+ last_epoch. map_or ( "Epoch <initial>" . into ( ) , |e| format ! ( "Epoch {}" , e. curr) ) ,
185
+ )
186
+ . await
187
+ {
188
+ Ok ( Some ( barrier) ) => barrier,
189
+ Ok ( None ) => break Err ( anyhow ! ( "actor exited unexpectedly" ) . into ( ) ) ,
190
+ Err ( err) => break Err ( err) ,
191
+ } ;
191
192
192
193
// Collect barriers to local barrier manager
193
194
self . context . lock_barrier_manager ( ) . collect ( id, & barrier) ;
194
195
195
196
// Then stop this actor if asked
196
- let to_stop = barrier. is_stop_or_update_drop_actor ( id) ;
197
- if to_stop {
198
- tracing:: trace!( actor_id = id, "actor exit" ) ;
199
- return Ok ( ( ) ) ;
197
+ if barrier. is_stop_or_update_drop_actor ( id) {
198
+ break Ok ( ( ) ) ;
200
199
}
201
200
202
201
// Tracing related work
202
+ last_epoch = Some ( barrier. epoch ) ;
203
203
span = {
204
204
let mut span = Span :: enter_with_local_parent ( "actor_poll" ) ;
205
205
span. add_property ( || ( "otel.name" , span_name. to_string ( ) ) ) ;
@@ -208,8 +208,24 @@ where
208
208
span. add_property ( || ( "epoch" , barrier. epoch . curr . to_string ( ) ) ) ;
209
209
span
210
210
} ;
211
- }
211
+ } ;
212
+
213
+ spawn_blocking_drop_stream ( stream) . await ;
212
214
213
- Ok ( ( ) )
215
+ tracing:: trace!( actor_id = id, "actor exit" ) ;
216
+ result
214
217
}
215
218
}
219
+
220
+ /// Drop the stream in a blocking task to avoid interfering with other actors.
221
+ ///
222
+ /// Logically the actor is dropped after we send the barrier with `Drop` mutation to the
223
+ /// downstream,thus making the `drop`'s progress asynchronous. However, there might be a
224
+ /// considerable amount of data in the executors' in-memory cache, dropping these structures might
225
+ /// be a CPU-intensive task. This may lead to the runtime being unable to schedule other actors if
226
+ /// the `drop` is called on the current thread.
227
+ pub async fn spawn_blocking_drop_stream < T : Send + ' static > ( stream : T ) {
228
+ let _ = tokio:: task:: spawn_blocking ( move || drop ( stream) )
229
+ . instrument_await ( "drop_stream" )
230
+ . await ;
231
+ }
0 commit comments