@@ -34,7 +34,8 @@ use crate::{
3434 Publisher , PublisherOptions , PublisherState , RclrsError , Service , IntoAsyncServiceCallback ,
3535 IntoNodeServiceCallback , ServiceOptions , ServiceState , Subscription , IntoAsyncSubscriptionCallback ,
3636 IntoNodeSubscriptionCallback , SubscriptionOptions , SubscriptionState , TimeSource , ToLogParams ,
37- ENTITY_LIFECYCLE_MUTEX , IntoWorkerOptions , Worker , WorkerState ,
37+ ENTITY_LIFECYCLE_MUTEX , IntoWorkerOptions , Worker , WorkerState , IntoTimerOptions , AnyTimerCallback ,
38+ Timer , IntoNodeTimerRepeatingCallback , IntoNodeTimerOneshotCallback , TimerState ,
3839} ;
3940
4041/// A processing unit that can communicate with other nodes.
@@ -244,7 +245,7 @@ impl NodeState {
244245 options : impl IntoWorkerOptions < Payload > ,
245246 ) -> Worker < Payload >
246247 where
247- Payload : ' static + Send ,
248+ Payload : ' static + Send + Sync ,
248249 {
249250 let options = options. into_worker_options ( ) ;
250251 let commands = self . commands . create_worker_commands ( Box :: new ( options. payload ) ) ;
@@ -555,6 +556,70 @@ impl NodeState {
555556 )
556557 }
557558
559+ /// Create a [`Timer`] with a repeating callback.
560+ ///
561+ /// See also:
562+ /// * [`Self::create_timer_oneshot`]
563+ /// * [`Self::create_timer_inert`]
564+ pub fn create_timer_repeating < ' a , Args > (
565+ & self ,
566+ options : impl IntoTimerOptions < ' a > ,
567+ callback : impl IntoNodeTimerRepeatingCallback < Args > ,
568+ ) -> Result < Timer , RclrsError > {
569+ self . create_timer ( options, callback. into_node_timer_repeating_callback ( ) )
570+ }
571+
572+ /// Create a [`Timer`] whose callback will be triggered once after the period
573+ /// of the timer has elapsed. After that you will need to use
574+ /// [`Timer::set_callback`] or a related method or else nothing will happen
575+ /// the following times that the `Timer` elapses.
576+ ///
577+ /// See also:
578+ /// * [`Self::create_timer_repeating`]
579+ /// * [`Self::create_time_inert`]
580+ pub fn create_timer_oneshot < ' a , Args > (
581+ & self ,
582+ options : impl IntoTimerOptions < ' a > ,
583+ callback : impl IntoNodeTimerOneshotCallback < Args > ,
584+ ) -> Result < Timer , RclrsError > {
585+ self . create_timer ( options, callback. into_node_timer_oneshot_callback ( ) )
586+ }
587+
588+ /// Create a [`Timer`] without a callback. Nothing will happen when this
589+ /// `Timer` elapses until you use [`Timer::set_callback`] or a related method.
590+ ///
591+ /// See also:
592+ /// * [`Self::create_timer_repeating`]
593+ /// * [`Self::create_timer_oneshot`]
594+ pub fn create_timer_inert < ' a > (
595+ & self ,
596+ options : impl IntoTimerOptions < ' a > ,
597+ ) -> Result < Timer , RclrsError > {
598+ self . create_timer ( options, AnyTimerCallback :: Inert )
599+ }
600+
601+ /// Used internally to create a [`Timer`].
602+ ///
603+ /// Downstream users should instead use:
604+ /// * [`Self::create_timer_repeating`]
605+ /// * [`Self::create_timer_oneshot`]
606+ /// * [`Self::create_timer_inert`]
607+ fn create_timer < ' a > (
608+ & self ,
609+ options : impl IntoTimerOptions < ' a > ,
610+ callback : AnyTimerCallback < Node > ,
611+ ) -> Result < Timer , RclrsError > {
612+ let options = options. into_timer_options ( ) ;
613+ let clock = options. clock . as_clock ( self ) ;
614+ TimerState :: create (
615+ options. period ,
616+ clock,
617+ callback,
618+ self . commands . async_worker_commands ( ) ,
619+ & self . handle . context_handle ,
620+ )
621+ }
622+
558623 /// Returns the ROS domain ID that the node is using.
559624 ///
560625 /// The domain ID controls which nodes can send messages to each other, see the [ROS 2 concept article][1].
@@ -748,6 +813,11 @@ unsafe impl Send for rcl_node_t {}
748813mod tests {
749814 use crate :: { test_helpers:: * , * } ;
750815
816+ use std:: {
817+ time:: Duration ,
818+ sync:: { Arc , atomic:: { AtomicU64 , Ordering } } ,
819+ } ;
820+
751821 #[ test]
752822 fn traits ( ) {
753823 assert_send :: < NodeState > ( ) ;
@@ -794,6 +864,68 @@ mod tests {
794864 Ok ( ( ) )
795865 }
796866
867+ #[ test]
868+ fn test_create_timer ( ) -> Result < ( ) , RclrsError > {
869+ dbg ! ( ) ;
870+ let mut executor = Context :: default ( ) . create_basic_executor ( ) ;
871+ let node = executor. create_node ( "node_with_timer" ) ?;
872+
873+ let repeat_counter = Arc :: new ( AtomicU64 :: new ( 0 ) ) ;
874+ let repeat_counter_check = Arc :: clone ( & repeat_counter) ;
875+ let _repeating_timer = node. create_timer_repeating (
876+ Duration :: from_millis ( 1 ) ,
877+ move || {
878+ dbg ! ( ) ;
879+ repeat_counter. fetch_add ( 1 , Ordering :: AcqRel ) ;
880+ } ,
881+ ) ?;
882+
883+ let oneshot_counter = Arc :: new ( AtomicU64 :: new ( 0 ) ) ;
884+ let oneshot_counter_check = Arc :: clone ( & oneshot_counter) ;
885+ let _oneshot_timer = node. create_timer_oneshot (
886+ Duration :: from_millis ( 1 )
887+ . node_time ( ) ,
888+ move || {
889+ dbg ! ( ) ;
890+ oneshot_counter. fetch_add ( 1 , Ordering :: AcqRel ) ;
891+ } ,
892+ ) ?;
893+
894+ let oneshot_resetting_counter = Arc :: new ( AtomicU64 :: new ( 0 ) ) ;
895+ let oneshot_resetting_counter_check = Arc :: clone ( & oneshot_resetting_counter) ;
896+ let _oneshot_resetting_timer = node. create_timer_oneshot (
897+ Duration :: from_millis ( 1 ) ,
898+ move |timer : & Timer | {
899+ dbg ! ( ) ;
900+ recursive_oneshot ( timer, oneshot_resetting_counter) ;
901+ } ,
902+ ) ;
903+
904+ dbg ! ( ) ;
905+ executor. spin ( SpinOptions :: new ( ) . timeout ( Duration :: from_millis ( 10 ) ) ) ;
906+
907+ // We give a little leeway to the exact count since timers won't always
908+ // be triggered perfectly. The important thing is that it was
909+ // successfully called repeatedly.
910+ assert ! ( repeat_counter_check. load( Ordering :: Acquire ) > 5 ) ;
911+ assert ! ( oneshot_resetting_counter_check. load( Ordering :: Acquire ) > 5 ) ;
912+
913+ // This should only have been triggered exactly once
914+ assert_eq ! ( oneshot_counter_check. load( Ordering :: Acquire ) , 1 ) ;
915+
916+ Ok ( ( ) )
917+ }
918+
919+ fn recursive_oneshot (
920+ timer : & Timer ,
921+ counter : Arc < AtomicU64 > ,
922+ ) {
923+ counter. fetch_add ( 1 , Ordering :: AcqRel ) ;
924+ timer. set_oneshot ( move |timer : & Timer | {
925+ recursive_oneshot ( timer, counter) ;
926+ } ) ;
927+ }
928+
797929 #[ test]
798930 fn test_logger_name ( ) -> Result < ( ) , RclrsError > {
799931 // Use helper to create 2 nodes for us
0 commit comments