@@ -27,14 +27,14 @@ use anyhow::{anyhow, Context};
2727use futures_core:: Stream ;
2828use std:: {
2929 collections:: HashMap ,
30- ops:: ControlFlow ,
3130 pin:: Pin ,
3231 sync:: Arc ,
3332 task:: { Context as TaskContext , Poll } ,
3433 time:: Duration ,
3534} ;
3635use tokio:: sync:: { mpsc, oneshot, watch} ;
3736use tokio_stream:: wrappers:: ReceiverStream ;
37+ use tokio_util:: sync:: CancellationToken ;
3838
3939/// Options for creating a [`Manager`].
4040#[ derive( Debug ) ]
@@ -52,7 +52,7 @@ pub struct Manager {
5252 event_in_tx : mpsc:: Sender < InputEvent > ,
5353 event_in_rx : mpsc:: Receiver < InputEvent > ,
5454 event_out_tx : mpsc:: Sender < OutputEvent > ,
55- shutdown_rx : watch :: Receiver < bool > ,
55+ token : CancellationToken ,
5656 handle_allocator : packet:: HandleAllocator ,
5757 descriptors : HashMap < Handle , Descriptor > ,
5858}
@@ -69,15 +69,15 @@ impl Manager {
6969 pub fn new ( options : ManagerOptions ) -> ( Self , ManagerInput , ManagerOutput ) {
7070 let ( event_in_tx, event_in_rx) = mpsc:: channel ( Self :: EVENT_BUFFER_COUNT ) ;
7171 let ( event_out_tx, event_out_rx) = mpsc:: channel ( Self :: EVENT_BUFFER_COUNT ) ;
72- let ( shutdown_tx , shutdown_rx ) = watch :: channel ( false ) ;
72+ let token = CancellationToken :: new ( ) ;
7373
74- let event_in = ManagerInput :: new ( event_in_tx. clone ( ) , shutdown_tx ) ;
74+ let event_in = ManagerInput :: new ( event_in_tx. clone ( ) , token . clone ( ) ) ;
7575 let manager = Manager {
7676 encryption_provider : options. encryption_provider ,
7777 event_in_tx,
7878 event_in_rx,
7979 event_out_tx,
80- shutdown_rx ,
80+ token ,
8181 handle_allocator : packet:: HandleAllocator :: default ( ) ,
8282 descriptors : HashMap :: new ( ) ,
8383 } ;
@@ -88,42 +88,30 @@ impl Manager {
8888
8989 /// Run the manager task, consuming self.
9090 ///
91- /// The manager will continue running until receiving [`InputEvent::Shutdown`].
91+ /// The manager continues until [`ManagerInput::shutdown`] is called, the last
92+ /// [`ManagerInput`] is dropped, or the input channel closes.
9293 ///
9394 pub async fn run ( mut self ) {
9495 log:: debug!( "Task started" ) ;
9596 loop {
9697 tokio:: select! {
97- // Biased so queued events are still processed in order once
98- // shutdown has been signalled out-of-band; see `ManagerInput::send`.
98+ // Biased so shutdown ends event processing immediately.
9999 biased;
100+ _ = self . token. cancelled( ) => {
101+ break ;
102+ }
100103 event = self . event_in_rx. recv( ) => {
101104 let Some ( event) = event else { break } ;
102- if self . handle_event( event) . await . is_break( ) {
103- break ;
104- }
105- }
106- _ = self . shutdown_rx. changed( ) => {
107- self . drain_pending( ) . await ;
108- break ;
105+ self . handle_event( event) . await ;
109106 }
110107 }
111108 }
112109 self . shutdown ( ) . await ;
113110 log:: debug!( "Task ended" ) ;
114111 }
115112
116- /// Drains events that were queued ahead of an out-of-band shutdown signal.
117- async fn drain_pending ( & mut self ) {
118- while let Ok ( event) = self . event_in_rx . try_recv ( ) {
119- if self . handle_event ( event) . await . is_break ( ) {
120- break ;
121- }
122- }
123- }
124-
125- /// Handles a single input event, reporting whether the task should stop.
126- async fn handle_event ( & mut self , event : InputEvent ) -> ControlFlow < ( ) > {
113+ /// Handles a single input event.
114+ async fn handle_event ( & mut self , event : InputEvent ) {
127115 log:: debug!( "Input event: {:?}" , event) ;
128116 match event {
129117 InputEvent :: PublishRequest ( event) => self . on_publish_request ( event) . await ,
@@ -133,9 +121,7 @@ impl Manager {
133121 InputEvent :: SfuPublishResponse ( event) => self . on_sfu_publish_response ( event) . await ,
134122 InputEvent :: SfuUnpublishResponse ( event) => self . on_sfu_unpublish_response ( event) . await ,
135123 InputEvent :: RepublishTracks => self . on_republish_tracks ( ) . await ,
136- InputEvent :: Shutdown => return ControlFlow :: Break ( ( ) ) ,
137124 }
138- ControlFlow :: Continue ( ( ) )
139125 }
140126
141127 async fn on_publish_request ( & mut self , event : PublishRequest ) {
@@ -286,6 +272,7 @@ impl Manager {
286272 frame_rx,
287273 event_in_tx : self . event_in_tx . clone ( ) ,
288274 event_out_tx : self . event_out_tx . clone ( ) ,
275+ token : self . token . child_token ( ) ,
289276 } ;
290277 let task_handle = livekit_runtime:: spawn ( track_task. run ( ) ) ;
291278
@@ -310,7 +297,9 @@ impl Manager {
310297 return ;
311298 } ;
312299 if * state_tx. borrow ( ) != PublishState :: Unpublished {
313- _ = state_tx. send ( PublishState :: Unpublished ) ;
300+ // `send_replace` updates even if the track task already dropped its receiver
301+ // after observing manager cancellation.
302+ _ = state_tx. send_replace ( PublishState :: Unpublished ) ;
314303 }
315304 }
316305
@@ -347,28 +336,18 @@ impl Manager {
347336 _ = result_tx. send ( Err ( PublishError :: Disconnected ) )
348337 }
349338 Descriptor :: Active { state_tx, task_handle, .. } => {
350- _ = state_tx. send ( PublishState :: Unpublished ) ;
339+ // `send_replace` updates even if the track task already dropped its
340+ // receiver after observing manager cancellation.
341+ _ = state_tx. send_replace ( PublishState :: Unpublished ) ;
351342 task_handles. push ( task_handle) ;
352343 }
353344 }
354345 }
355346
356- // Track tasks emit a final unpublish request as they end, so the input
357- // channel has to keep draining while they are joined. Joining without
358- // draining deadlocks as soon as more tasks are ending than the channel
359- // can buffer.
360- let join_tasks = async {
361- for task_handle in task_handles {
362- task_handle. await ;
363- }
364- } ;
365- tokio:: pin!( join_tasks) ;
366- loop {
367- tokio:: select! {
368- _ = & mut join_tasks => break ,
369- // Never yields `None`: the manager owns a sender for its own lifetime.
370- _ = self . event_in_rx. recv( ) => { }
371- }
347+ // Track tasks observe the parent cancellation token via child tokens and
348+ // skip their final unpublish request, so joining alone is sufficient.
349+ for task_handle in task_handles {
350+ task_handle. await ;
372351 }
373352 }
374353
@@ -387,6 +366,7 @@ struct TrackTask {
387366 frame_rx : mpsc:: Receiver < DataTrackFrame > ,
388367 event_in_tx : mpsc:: Sender < InputEvent > ,
389368 event_out_tx : mpsc:: Sender < OutputEvent > ,
369+ token : CancellationToken ,
390370}
391371
392372impl TrackTask {
@@ -397,6 +377,8 @@ impl TrackTask {
397377 let mut state = * self . state_rx . borrow ( ) ;
398378 while state != PublishState :: Unpublished {
399379 tokio:: select! {
380+ biased;
381+ _ = self . token. cancelled( ) => break ,
400382 _ = self . state_rx. changed( ) => {
401383 state = * self . state_rx. borrow( ) ;
402384 }
@@ -410,8 +392,11 @@ impl TrackTask {
410392 }
411393 }
412394
413- let event = UnpublishRequest { handle : self . info . pub_handle } ;
414- _ = self . event_in_tx . send ( event. into ( ) ) . await ;
395+ // Manager-wide shutdown already owns cleanup; only notify for per-track unpublish.
396+ if !self . token . is_cancelled ( ) {
397+ let event = UnpublishRequest { handle : self . info . pub_handle } ;
398+ _ = self . event_in_tx . send ( event. into ( ) ) . await ;
399+ }
415400
416401 log:: debug!( "Track task ended: sid={}" , sid) ;
417402 }
@@ -465,7 +450,9 @@ pub(crate) enum PublishState {
465450#[ derive( Debug , Clone ) ]
466451pub struct ManagerInput {
467452 event_in_tx : mpsc:: Sender < InputEvent > ,
468- drop_guard : Arc < DropGuard > ,
453+ token : CancellationToken ,
454+ /// Cancels the manager when the last [`ManagerInput`] is dropped.
455+ _drop_guard : Arc < CancelOnDrop > ,
469456}
470457
471458/// Stream of [`OutputEvent`]s produced by [`Manager`].
@@ -480,32 +467,37 @@ impl Stream for ManagerOutput {
480467 }
481468}
482469
483- /// Guard that signals shutdown when the last reference is dropped.
470+ /// Cancels a [`CancellationToken`] when dropped.
484471#[ derive( Debug ) ]
485- struct DropGuard {
486- shutdown_tx : watch:: Sender < bool > ,
487- }
472+ struct CancelOnDrop ( CancellationToken ) ;
488473
489- impl Drop for DropGuard {
474+ impl Drop for CancelOnDrop {
490475 fn drop ( & mut self ) {
491- _ = self . shutdown_tx . send ( true ) ;
476+ self . 0 . cancel ( ) ;
492477 }
493478}
494479
495480impl ManagerInput {
496- fn new ( event_in_tx : mpsc:: Sender < InputEvent > , shutdown_tx : watch:: Sender < bool > ) -> Self {
497- Self { event_in_tx, drop_guard : DropGuard { shutdown_tx } . into ( ) }
481+ fn new ( event_in_tx : mpsc:: Sender < InputEvent > , token : CancellationToken ) -> Self {
482+ Self { event_in_tx, token : token. clone ( ) , _drop_guard : Arc :: new ( CancelOnDrop ( token) ) }
483+ }
484+
485+ /// Shuts down the manager, ending all event processing.
486+ ///
487+ /// Unlike [`Self::send`], this does not use the bounded event channel, so it
488+ /// cannot be dropped when the channel is saturated.
489+ ///
490+ pub fn shutdown ( & self ) {
491+ self . token . cancel ( ) ;
492+ }
493+
494+ /// Returns a clone of the manager's cancellation token.
495+ pub fn cancellation_token ( & self ) -> CancellationToken {
496+ self . token . clone ( )
498497 }
499498
500499 /// Sends an input event to the manager's task to be processed.
501500 pub fn send ( & self , event : InputEvent ) -> Result < ( ) , InternalError > {
502- // Shutdown bypasses the bounded event channel. In-flight track events
503- // routinely saturate it, and a shutdown dropped for lack of capacity
504- // strands the manager task along with everyone awaiting its completion.
505- if matches ! ( event, InputEvent :: Shutdown ) {
506- _ = self . drop_guard . shutdown_tx . send ( true ) ;
507- return Ok ( ( ) ) ;
508- }
509501 Ok ( self . event_in_tx . try_send ( event) . context ( "Failed to handle input event" ) ?)
510502 }
511503
@@ -579,7 +571,7 @@ mod tests {
579571 let ( manager, input, _) = Manager :: new ( options) ;
580572
581573 let join_handle = livekit_runtime:: spawn ( manager. run ( ) ) ;
582- _ = input. send ( InputEvent :: Shutdown ) ;
574+ input. shutdown ( ) ;
583575
584576 timeout ( Duration :: from_secs ( 1 ) , join_handle) . await . unwrap ( ) ;
585577 }
@@ -591,21 +583,14 @@ mod tests {
591583
592584 // Fill the event channel before the manager starts draining it so that
593585 // shutdown cannot depend on any remaining capacity.
594- let mut result_rxs = Vec :: new ( ) ;
595586 for _ in 0 ..Manager :: EVENT_BUFFER_COUNT {
596- let ( result_tx, result_rx ) = oneshot:: channel ( ) ;
587+ let ( result_tx, _result_rx ) = oneshot:: channel ( ) ;
597588 input. send ( QueryPublished { result_tx } . into ( ) ) . unwrap ( ) ;
598- result_rxs. push ( result_rx) ;
599589 }
600- input. send ( InputEvent :: Shutdown ) . unwrap ( ) ;
590+ input. shutdown ( ) ;
601591
602592 let join_handle = livekit_runtime:: spawn ( manager. run ( ) ) ;
603593 timeout ( Duration :: from_secs ( 1 ) , join_handle) . await . unwrap ( ) ;
604-
605- // Events queued ahead of the shutdown signal are still processed.
606- for result_rx in result_rxs {
607- assert ! ( result_rx. await . is_ok( ) ) ;
608- }
609594 }
610595
611596 #[ tokio:: test]
@@ -900,7 +885,7 @@ mod tests {
900885 assert ! ( active_track. is_published( ) ) ;
901886
902887 // Shutdown the manager
903- input. send ( InputEvent :: Shutdown ) . unwrap ( ) ;
888+ input. shutdown ( ) ;
904889 sleep ( Duration :: from_millis ( 50 ) ) . await ;
905890
906891 // Pending publish receives disconnected error
0 commit comments