Implement program cancellation
This commit is contained in:
		
							parent
							
								
									6d33b7ed50
								
							
						
					
					
						commit
						9d5877106b
					
				| @ -23,6 +23,7 @@ enum RunnerMsg { | ||||
| pub enum ProgramEvent { | ||||
|     RunStart(ProgramRef), | ||||
|     RunFinish(ProgramRef), | ||||
|     RunCancel(ProgramRef), | ||||
| } | ||||
| 
 | ||||
| pub type ProgramEventRecv = broadcast::Receiver<ProgramEvent>; | ||||
| @ -35,6 +36,7 @@ enum RunState { | ||||
|     Waiting, | ||||
|     Running, | ||||
|     Finished, | ||||
|     Cancelled, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| @ -141,6 +143,18 @@ impl RunnerTask { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     async fn cancel_program_run(&mut self, run: &mut ProgRun) { | ||||
|         for handle in run.sec_run_handles.drain(..) { | ||||
|             if let Err(_closed) = self.section_runner.cancel_run(handle).await { | ||||
|                 error!("section runner channel closed"); | ||||
|                 self.running = false; | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|         debug!(program_id = run.program.id, "program run is cancelled"); | ||||
|         self.send_event(ProgramEvent::RunCancel(run.program.clone())); | ||||
|     } | ||||
| 
 | ||||
|     async fn process_queue(&mut self, run_queue: &mut RunQueue) { | ||||
|         while let Some(current_run) = run_queue.front_mut() { | ||||
|             let run_finished = match current_run.state { | ||||
| @ -150,6 +164,10 @@ impl RunnerTask { | ||||
|                 } | ||||
|                 RunState::Running => false, | ||||
|                 RunState::Finished => true, | ||||
|                 RunState::Cancelled => { | ||||
|                     self.cancel_program_run(current_run).await; | ||||
|                     true | ||||
|                 } | ||||
|             }; | ||||
|             if run_finished { | ||||
|                 run_queue.pop_front(); | ||||
| @ -189,7 +207,13 @@ impl RunnerTask { | ||||
|             RunProgram(program) => { | ||||
|                 run_queue.push_back(ProgRun::new(program)); | ||||
|             } | ||||
|             RunnerMsg::CancelProgram(_) => todo!(), | ||||
|             RunnerMsg::CancelProgram(program_id) => { | ||||
|                 for run in run_queue { | ||||
|                     if run.program.id == program_id { | ||||
|                         run.state = RunState::Cancelled; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -352,8 +376,8 @@ mod test { | ||||
|     }; | ||||
|     use im::ordmap; | ||||
|     use std::{sync::Arc, time::Duration}; | ||||
|     use tracing_subscriber::prelude::*; | ||||
|     use tokio::task::yield_now; | ||||
|     use tracing_subscriber::prelude::*; | ||||
| 
 | ||||
|     #[tokio::test] | ||||
|     async fn test_quit() { | ||||
| @ -475,23 +499,19 @@ mod test { | ||||
|         let program1: ProgramRef = Program { | ||||
|             id: 1, | ||||
|             name: "Program 1".into(), | ||||
|             sequence: vec![ | ||||
|                 ProgramItem { | ||||
|                     section_id: 3, | ||||
|                     duration: Duration::from_secs(10), | ||||
|                 }, | ||||
|             ], | ||||
|             sequence: vec![ProgramItem { | ||||
|                 section_id: 3, | ||||
|                 duration: Duration::from_secs(10), | ||||
|             }], | ||||
|         } | ||||
|         .into(); | ||||
|         let program2: ProgramRef = Program { | ||||
|             id: 2, | ||||
|             name: "Program 2".into(), | ||||
|             sequence: vec![ | ||||
|                 ProgramItem { | ||||
|                     section_id: 1, | ||||
|                     duration: Duration::from_secs(10), | ||||
|                 }, | ||||
|             ], | ||||
|             sequence: vec![ProgramItem { | ||||
|                 section_id: 1, | ||||
|                 duration: Duration::from_secs(10), | ||||
|             }], | ||||
|         } | ||||
|         .into(); | ||||
| 
 | ||||
| @ -570,23 +590,19 @@ mod test { | ||||
|         let program1: ProgramRef = Program { | ||||
|             id: 1, | ||||
|             name: "Program 1".into(), | ||||
|             sequence: vec![ | ||||
|                 ProgramItem { | ||||
|                     section_id: 2, | ||||
|                     duration: Duration::from_secs(10), | ||||
|                 }, | ||||
|             ], | ||||
|             sequence: vec![ProgramItem { | ||||
|                 section_id: 2, | ||||
|                 duration: Duration::from_secs(10), | ||||
|             }], | ||||
|         } | ||||
|         .into(); | ||||
|         let program2: ProgramRef = Program { | ||||
|             id: 2, | ||||
|             name: "Program 2".into(), | ||||
|             sequence: vec![ | ||||
|                 ProgramItem { | ||||
|                     section_id: 2, | ||||
|                     duration: Duration::from_secs(10), | ||||
|                 }, | ||||
|             ], | ||||
|             sequence: vec![ProgramItem { | ||||
|                 section_id: 2, | ||||
|                 duration: Duration::from_secs(10), | ||||
|             }], | ||||
|         } | ||||
|         .into(); | ||||
|         let programs = ordmap![ 1 => program1, 2 => program2 ]; | ||||
| @ -634,4 +650,58 @@ mod test { | ||||
|         sec_runner.quit().await.unwrap(); | ||||
|         yield_now().await; | ||||
|     } | ||||
| 
 | ||||
|     #[tokio::test] | ||||
|     async fn test_cancel_program() { | ||||
|         let (sections, mut sec_runner, _) = make_sections_and_runner(); | ||||
|         let mut sec_events = sec_runner.subscribe().await.unwrap(); | ||||
|         let mut runner = ProgramRunner::new(sec_runner.clone()); | ||||
|         let mut prog_events = runner.subscribe().await.unwrap(); | ||||
| 
 | ||||
|         let program: ProgramRef = Program { | ||||
|             id: 1, | ||||
|             name: "Program 1".into(), | ||||
|             sequence: vec![ | ||||
|                 ProgramItem { | ||||
|                     section_id: 1, | ||||
|                     duration: Duration::from_secs(10), | ||||
|                 }, | ||||
|                 ProgramItem { | ||||
|                     section_id: 2, | ||||
|                     duration: Duration::from_secs(10), | ||||
|                 }, | ||||
|             ], | ||||
|         } | ||||
|         .into(); | ||||
| 
 | ||||
|         runner.update_sections(sections.clone()).await.unwrap(); | ||||
| 
 | ||||
|         runner.run_program(program.clone()).await.unwrap(); | ||||
|         yield_now().await; | ||||
|         assert!(matches!( | ||||
|             prog_events.try_recv().unwrap(), | ||||
|             ProgramEvent::RunStart(prog) | ||||
|             if prog.id == 1 | ||||
|         )); | ||||
|         assert!(matches!( | ||||
|             sec_events.try_recv().unwrap(), | ||||
|             SectionEvent::RunStart(_) | ||||
|         )); | ||||
| 
 | ||||
|         runner.cancel_program(program.id).await.unwrap(); | ||||
|         yield_now().await; | ||||
|         assert!(matches!( | ||||
|             prog_events.recv().await.unwrap(), | ||||
|             ProgramEvent::RunCancel(prog) | ||||
|             if prog.id == 1 | ||||
|         )); | ||||
|         assert!(matches!( | ||||
|             sec_events.recv().await.unwrap(), | ||||
|             SectionEvent::RunCancel(_) | ||||
|         )); | ||||
| 
 | ||||
|         runner.quit().await.unwrap(); | ||||
|         sec_runner.quit().await.unwrap(); | ||||
|         yield_now().await; | ||||
|     } | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user