@@ -21,11 +21,13 @@ mod worker;
2121
2222pub mod deadline;
2323pub mod heartbeat;
24+ pub mod logic;
2425
2526use crate :: common:: { Monitor , MonitorEvalHandle } ;
2627use crate :: deadline:: { DeadlineMonitor , DeadlineMonitorBuilder } ;
2728use crate :: heartbeat:: { HeartbeatMonitor , HeartbeatMonitorBuilder } ;
2829use crate :: log:: { error, ScoreDebug } ;
30+ use crate :: logic:: { LogicMonitor , LogicMonitorBuilder } ;
2931pub use common:: TimeRange ;
3032use containers:: fixed_capacity:: FixedCapacityVec ;
3133use core:: time:: Duration ;
@@ -48,6 +50,7 @@ pub enum HealthMonitorError {
4850pub struct HealthMonitorBuilder {
4951 deadline_monitor_builders : HashMap < MonitorTag , DeadlineMonitorBuilder > ,
5052 heartbeat_monitor_builders : HashMap < MonitorTag , HeartbeatMonitorBuilder > ,
53+ logic_monitor_builders : HashMap < MonitorTag , LogicMonitorBuilder > ,
5154 supervisor_api_cycle : Duration ,
5255 internal_processing_cycle : Duration ,
5356}
@@ -58,6 +61,7 @@ impl HealthMonitorBuilder {
5861 Self {
5962 deadline_monitor_builders : HashMap :: new ( ) ,
6063 heartbeat_monitor_builders : HashMap :: new ( ) ,
64+ logic_monitor_builders : HashMap :: new ( ) ,
6165 supervisor_api_cycle : Duration :: from_millis ( 500 ) ,
6266 internal_processing_cycle : Duration :: from_millis ( 100 ) ,
6367 }
@@ -89,6 +93,19 @@ impl HealthMonitorBuilder {
8993 self
9094 }
9195
96+ /// Add a [`LogicMonitor`] for the given [`MonitorTag`].
97+ ///
98+ /// - `monitor_tag` - unique tag for the [`LogicMonitor`].
99+ /// - `monitor_builder` - monitor builder to finalize.
100+ ///
101+ /// # Note
102+ ///
103+ /// If a logic monitor with the same tag already exists, it will be overwritten.
104+ pub fn add_logic_monitor ( mut self , monitor_tag : MonitorTag , monitor_builder : LogicMonitorBuilder ) -> Self {
105+ self . add_logic_monitor_internal ( monitor_tag, monitor_builder) ;
106+ self
107+ }
108+
92109 /// Set the interval between supervisor API notifications.
93110 /// This duration determines how often the health monitor notifies the supervisor about system liveness.
94111 ///
@@ -121,7 +138,9 @@ impl HealthMonitorBuilder {
121138 }
122139
123140 // Check number of monitors.
124- let num_monitors = self . deadline_monitor_builders . len ( ) + self . heartbeat_monitor_builders . len ( ) ;
141+ let num_monitors = self . deadline_monitor_builders . len ( )
142+ + self . heartbeat_monitor_builders . len ( )
143+ + self . logic_monitor_builders . len ( ) ;
125144 if num_monitors == 0 {
126145 error ! ( "No monitors have been added. HealthMonitor cannot be created." ) ;
127146 return Err ( HealthMonitorError :: WrongState ) ;
@@ -144,9 +163,17 @@ impl HealthMonitorBuilder {
144163 heartbeat_monitors. insert ( tag, Some ( MonitorState :: Available ( monitor) ) ) ;
145164 }
146165
166+ // Create logic monitors.
167+ let mut logic_monitors = HashMap :: new ( ) ;
168+ for ( tag, builder) in self . logic_monitor_builders {
169+ let monitor = builder. build ( tag, & allocator) ?;
170+ logic_monitors. insert ( tag, Some ( MonitorState :: Available ( monitor) ) ) ;
171+ }
172+
147173 Ok ( HealthMonitor {
148174 deadline_monitors,
149175 heartbeat_monitors,
176+ logic_monitors,
150177 worker : worker:: UniqueThreadRunner :: new ( self . internal_processing_cycle ) ,
151178 supervisor_api_cycle : self . supervisor_api_cycle ,
152179 } )
@@ -170,6 +197,10 @@ impl HealthMonitorBuilder {
170197 self . heartbeat_monitor_builders . insert ( monitor_tag, monitor_builder) ;
171198 }
172199
200+ pub ( crate ) fn add_logic_monitor_internal ( & mut self , monitor_tag : MonitorTag , monitor_builder : LogicMonitorBuilder ) {
201+ self . logic_monitor_builders . insert ( monitor_tag, monitor_builder) ;
202+ }
203+
173204 pub ( crate ) fn with_supervisor_api_cycle_internal ( & mut self , cycle_duration : Duration ) {
174205 self . supervisor_api_cycle = cycle_duration;
175206 }
@@ -196,6 +227,7 @@ type MonitorContainer<M> = Option<MonitorState<M>>;
196227pub struct HealthMonitor {
197228 deadline_monitors : HashMap < MonitorTag , MonitorContainer < DeadlineMonitor > > ,
198229 heartbeat_monitors : HashMap < MonitorTag , MonitorContainer < HeartbeatMonitor > > ,
230+ logic_monitors : HashMap < MonitorTag , MonitorContainer < LogicMonitor > > ,
199231 worker : worker:: UniqueThreadRunner ,
200232 supervisor_api_cycle : Duration ,
201233}
@@ -241,6 +273,16 @@ impl HealthMonitor {
241273 Self :: get_monitor ( & mut self . heartbeat_monitors , monitor_tag)
242274 }
243275
276+ /// Get and pass ownership of a [`LogicMonitor`] for the given [`MonitorTag`].
277+ ///
278+ /// - `monitor_tag` - unique tag for the [`LogicMonitor`].
279+ ///
280+ /// Returns [`Some`] containing [`LogicMonitor`] if found and not taken.
281+ /// Otherwise returns [`None`].
282+ pub fn get_logic_monitor ( & mut self , monitor_tag : MonitorTag ) -> Option < LogicMonitor > {
283+ Self :: get_monitor ( & mut self . logic_monitors , monitor_tag)
284+ }
285+
244286 fn collect_given_monitors < M > (
245287 monitors_to_collect : & mut HashMap < MonitorTag , MonitorContainer < M > > ,
246288 collected_monitors : & mut FixedCapacityVec < MonitorEvalHandle > ,
@@ -287,10 +329,11 @@ impl HealthMonitor {
287329 /// Health monitoring logic stops when the [`HealthMonitor`] is dropped.
288330 pub fn start ( & mut self ) -> Result < ( ) , HealthMonitorError > {
289331 // Collect all monitors.
290- let num_monitors = self . deadline_monitors . len ( ) + self . heartbeat_monitors . len ( ) ;
332+ let num_monitors = self . deadline_monitors . len ( ) + self . heartbeat_monitors . len ( ) + self . logic_monitors . len ( ) ;
291333 let mut collected_monitors = FixedCapacityVec :: new ( num_monitors) ;
292334 Self :: collect_given_monitors ( & mut self . deadline_monitors , & mut collected_monitors) ?;
293335 Self :: collect_given_monitors ( & mut self . heartbeat_monitors , & mut collected_monitors) ?;
336+ Self :: collect_given_monitors ( & mut self . logic_monitors , & mut collected_monitors) ?;
294337
295338 // Start monitoring logic.
296339 let monitoring_logic = worker:: MonitoringLogic :: new (
@@ -315,7 +358,8 @@ mod tests {
315358 use crate :: common:: TimeRange ;
316359 use crate :: deadline:: DeadlineMonitorBuilder ;
317360 use crate :: heartbeat:: HeartbeatMonitorBuilder ;
318- use crate :: tag:: MonitorTag ;
361+ use crate :: logic:: LogicMonitorBuilder ;
362+ use crate :: tag:: { MonitorTag , StateTag } ;
319363 use crate :: { HealthMonitorBuilder , HealthMonitorError } ;
320364 use core:: time:: Duration ;
321365
@@ -324,11 +368,20 @@ mod tests {
324368 HeartbeatMonitorBuilder :: new ( range)
325369 }
326370
371+ fn def_logic_monitor_builder ( ) -> LogicMonitorBuilder {
372+ let state1 = StateTag :: from ( "state1" ) ;
373+ let state2 = StateTag :: from ( "state2" ) ;
374+ LogicMonitorBuilder :: new ( state1)
375+ . add_state ( state2)
376+ . add_transition ( ( state1, state2) )
377+ }
378+
327379 #[ test]
328380 fn health_monitor_builder_new_succeeds ( ) {
329381 let health_monitor_builder = HealthMonitorBuilder :: new ( ) ;
330382 assert ! ( health_monitor_builder. deadline_monitor_builders. is_empty( ) ) ;
331383 assert ! ( health_monitor_builder. heartbeat_monitor_builders. is_empty( ) ) ;
384+ assert ! ( health_monitor_builder. logic_monitor_builders. is_empty( ) ) ;
332385 assert_eq ! ( health_monitor_builder. supervisor_api_cycle, Duration :: from_millis( 500 ) ) ;
333386 assert_eq ! (
334387 health_monitor_builder. internal_processing_cycle,
@@ -342,10 +395,13 @@ mod tests {
342395 let deadline_monitor_builder = DeadlineMonitorBuilder :: new ( ) ;
343396 let heartbeat_monitor_tag = MonitorTag :: from ( "heartbeat_monitor" ) ;
344397 let heartbeat_monitor_builder = def_heartbeat_monitor_builder ( ) ;
398+ let logic_monitor_tag = MonitorTag :: from ( "logic_monitor" ) ;
399+ let logic_monitor_builder = def_logic_monitor_builder ( ) ;
345400
346401 let result = HealthMonitorBuilder :: new ( )
347402 . add_deadline_monitor ( deadline_monitor_tag, deadline_monitor_builder)
348403 . add_heartbeat_monitor ( heartbeat_monitor_tag, heartbeat_monitor_builder)
404+ . add_logic_monitor ( logic_monitor_tag, logic_monitor_builder)
349405 . build ( ) ;
350406 assert ! ( result. is_ok( ) ) ;
351407 }
@@ -475,21 +531,80 @@ mod tests {
475531 assert ! ( result. is_none( ) ) ;
476532 }
477533
534+ #[ test]
535+ fn health_monitor_get_logic_monitor_available ( ) {
536+ let logic_monitor_tag = MonitorTag :: from ( "logic_monitor" ) ;
537+ let logic_monitor_builder = def_logic_monitor_builder ( ) ;
538+ let mut health_monitor = HealthMonitorBuilder :: new ( )
539+ . add_logic_monitor ( logic_monitor_tag, logic_monitor_builder)
540+ . build ( )
541+ . unwrap ( ) ;
542+
543+ let result = health_monitor. get_logic_monitor ( logic_monitor_tag) ;
544+ assert ! ( result. is_some( ) ) ;
545+ }
546+
547+ #[ test]
548+ fn health_monitor_get_logic_monitor_taken ( ) {
549+ let logic_monitor_tag = MonitorTag :: from ( "logic_monitor" ) ;
550+ let logic_monitor_builder = def_logic_monitor_builder ( ) ;
551+ let mut health_monitor = HealthMonitorBuilder :: new ( )
552+ . add_logic_monitor ( logic_monitor_tag, logic_monitor_builder)
553+ . build ( )
554+ . unwrap ( ) ;
555+
556+ let _ = health_monitor. get_logic_monitor ( logic_monitor_tag) ;
557+ let result = health_monitor. get_logic_monitor ( logic_monitor_tag) ;
558+ assert ! ( result. is_none( ) ) ;
559+ }
560+
561+ #[ test]
562+ fn health_monitor_get_logic_monitor_unknown ( ) {
563+ let logic_monitor_builder = def_logic_monitor_builder ( ) ;
564+ let mut health_monitor = HealthMonitorBuilder :: new ( )
565+ . add_logic_monitor ( MonitorTag :: from ( "logic_monitor" ) , logic_monitor_builder)
566+ . build ( )
567+ . unwrap ( ) ;
568+
569+ let result = health_monitor. get_logic_monitor ( MonitorTag :: from ( "undefined_monitor" ) ) ;
570+ assert ! ( result. is_none( ) ) ;
571+ }
572+
573+ #[ test]
574+ fn health_monitor_get_logic_monitor_invalid_state ( ) {
575+ let logic_monitor_tag = MonitorTag :: from ( "logic_monitor" ) ;
576+ let logic_monitor_builder = def_logic_monitor_builder ( ) ;
577+ let mut health_monitor = HealthMonitorBuilder :: new ( )
578+ . add_logic_monitor ( logic_monitor_tag, logic_monitor_builder)
579+ . build ( )
580+ . unwrap ( ) ;
581+
582+ // Inject broken state - unreachable otherwise.
583+ health_monitor. logic_monitors . insert ( logic_monitor_tag, None ) ;
584+
585+ let result = health_monitor. get_logic_monitor ( logic_monitor_tag) ;
586+ assert ! ( result. is_none( ) ) ;
587+ }
588+
478589 #[ test]
479590 fn health_monitor_start_succeeds ( ) {
480591 let deadline_monitor_tag = MonitorTag :: from ( "deadline_monitor" ) ;
481592 let deadline_monitor_builder = DeadlineMonitorBuilder :: new ( ) ;
482593 let heartbeat_monitor_tag = MonitorTag :: from ( "heartbeat_monitor" ) ;
483594 let heartbeat_monitor_builder = def_heartbeat_monitor_builder ( ) ;
595+ let logic_monitor_tag = MonitorTag :: from ( "logic_monitor" ) ;
596+ let logic_monitor_builder = def_logic_monitor_builder ( ) ;
484597
485598 let mut health_monitor = HealthMonitorBuilder :: new ( )
486599 . add_deadline_monitor ( deadline_monitor_tag, deadline_monitor_builder)
487600 . add_heartbeat_monitor ( heartbeat_monitor_tag, heartbeat_monitor_builder)
601+ . add_logic_monitor ( logic_monitor_tag, logic_monitor_builder)
488602 . build ( )
489603 . unwrap ( ) ;
490604
491605 let _deadline_monitor = health_monitor. get_deadline_monitor ( deadline_monitor_tag) . unwrap ( ) ;
492606 let _heartbeat_monitor = health_monitor. get_heartbeat_monitor ( heartbeat_monitor_tag) . unwrap ( ) ;
607+ let _logic_monitor = health_monitor. get_logic_monitor ( logic_monitor_tag) . unwrap ( ) ;
493608
494609 let result = health_monitor. start ( ) ;
495610 assert ! ( result. is_ok( ) ) ;
@@ -499,10 +614,12 @@ mod tests {
499614 fn health_monitor_start_monitors_not_taken ( ) {
500615 let deadline_monitor_builder = DeadlineMonitorBuilder :: new ( ) ;
501616 let heartbeat_monitor_builder = def_heartbeat_monitor_builder ( ) ;
617+ let logic_monitor_builder = def_logic_monitor_builder ( ) ;
502618
503619 let mut health_monitor = HealthMonitorBuilder :: new ( )
504620 . add_deadline_monitor ( MonitorTag :: from ( "deadline_monitor" ) , deadline_monitor_builder)
505621 . add_heartbeat_monitor ( MonitorTag :: from ( "heartbeat_monitor" ) , heartbeat_monitor_builder)
622+ . add_logic_monitor ( MonitorTag :: from ( "logic_monitor" ) , logic_monitor_builder)
506623 . build ( )
507624 . unwrap ( ) ;
508625
@@ -516,10 +633,13 @@ mod tests {
516633 let deadline_monitor_builder = DeadlineMonitorBuilder :: new ( ) ;
517634 let heartbeat_monitor_tag = MonitorTag :: from ( "heartbeat_monitor" ) ;
518635 let heartbeat_monitor_builder = def_heartbeat_monitor_builder ( ) ;
636+ let logic_monitor_tag = MonitorTag :: from ( "logic_monitor" ) ;
637+ let logic_monitor_builder = def_logic_monitor_builder ( ) ;
519638
520639 let mut health_monitor = HealthMonitorBuilder :: new ( )
521640 . add_deadline_monitor ( deadline_monitor_tag, deadline_monitor_builder)
522641 . add_heartbeat_monitor ( heartbeat_monitor_tag, heartbeat_monitor_builder)
642+ . add_logic_monitor ( logic_monitor_tag, logic_monitor_builder)
523643 . build ( )
524644 . unwrap ( ) ;
525645
@@ -532,6 +652,8 @@ mod tests {
532652 assert ! ( get_deadline_monitor_result. is_some( ) ) ;
533653 let get_heartbeat_monitor_result = health_monitor. get_heartbeat_monitor ( heartbeat_monitor_tag) ;
534654 assert ! ( get_heartbeat_monitor_result. is_some( ) ) ;
655+ let get_logic_monitor_result = health_monitor. get_logic_monitor ( logic_monitor_tag) ;
656+ assert ! ( get_logic_monitor_result. is_some( ) ) ;
535657
536658 // Try to start again, this time should be successful.
537659 let start_result = health_monitor. start ( ) ;
0 commit comments