@@ -81,7 +81,7 @@ macro_rules! log {
8181 // Note: that issue appears to be specific to jetbrains intellisense however,
8282 // observed same/similar behaviour with rust-analyzer/rustc
8383 use std:: sync:: { Once , OnceLock , Mutex } ;
84- use std:: time:: SystemTime ;
84+ use std:: time:: { SystemTime , Instant } ;
8585
8686 // We wrap the functional body of the macro inside of a closure which
8787 // we immediately trigger. This allows us to use `return` to exit the
@@ -128,19 +128,64 @@ macro_rules! log {
128128 // of that interval.
129129 let throttle = params. get_throttle( ) ;
130130 if throttle > std:: time:: Duration :: ZERO {
131- static LAST_LOG_TIME : OnceLock <Mutex <SystemTime >> = OnceLock :: new( ) ;
132- let last_log_time = LAST_LOG_TIME . get_or_init( || {
133- Mutex :: new( std:: time:: SystemTime :: now( ) )
134- } ) ;
135-
136- if !first_time {
137- let now = std:: time:: SystemTime :: now( ) ;
138- let mut previous = last_log_time. lock( ) . unwrap( ) ;
139- if now >= * previous + throttle {
140- * previous = now;
141- } else {
142- // We are still inside the throttle interval, so just exit here.
143- return ;
131+ match params. get_throttle_clock( ) {
132+ $crate:: ThrottleClock :: SteadyTime => {
133+ static LAST_LOG_STEADY_TIME : OnceLock <Mutex <Instant >> = OnceLock :: new( ) ;
134+ let last_log_time = LAST_LOG_STEADY_TIME . get_or_init( || {
135+ Mutex :: new( Instant :: now( ) )
136+ } ) ;
137+
138+ if !first_time {
139+ let now = Instant :: now( ) ;
140+ let mut previous = last_log_time. lock( ) . unwrap( ) ;
141+ if now >= * previous + throttle {
142+ * previous = now;
143+ } else {
144+ // We are still inside the throttle interval, so just exit here.
145+ return ;
146+ }
147+ }
148+ }
149+ $crate:: ThrottleClock :: SystemTime => {
150+ static LAST_LOG_SYSTEM_TIME : OnceLock <Mutex <SystemTime >> = OnceLock :: new( ) ;
151+ let last_log_time = LAST_LOG_SYSTEM_TIME . get_or_init( || {
152+ Mutex :: new( SystemTime :: now( ) )
153+ } ) ;
154+
155+ if !first_time {
156+ let now = SystemTime :: now( ) ;
157+ let mut previous = last_log_time. lock( ) . unwrap( ) ;
158+ if now >= * previous + throttle {
159+ * previous = now;
160+ } else {
161+ // We are still inside the throttle interval, so just exit here.
162+ return ;
163+ }
164+ }
165+ }
166+ $crate:: ThrottleClock :: Clock ( clock) => {
167+ static LAST_LOG_CLOCK_TIME : OnceLock <Mutex <$crate:: Time >> = OnceLock :: new( ) ;
168+ let last_log_time = LAST_LOG_CLOCK_TIME . get_or_init( || {
169+ Mutex :: new( clock. now( ) )
170+ } ) ;
171+
172+ if !first_time {
173+ let now = clock. now( ) ;
174+ let mut previous = last_log_time. lock( ) . unwrap( ) ;
175+
176+ let new_interval = !now. compare_with(
177+ & ( previous. clone( ) + throttle) ,
178+ |now, interval| now < interval,
179+ )
180+ . is_some_and( |eval| eval) ;
181+
182+ if new_interval {
183+ * previous = now;
184+ } else {
185+ // We are still inside the throttle interval, so just exit here.
186+ return ;
187+ }
188+ }
144189 }
145190 }
146191 }
@@ -592,8 +637,26 @@ mod tests {
592637 assert_eq ! ( last_severity( ) , LogSeverity :: Error ) ;
593638 assert_eq ! ( count_message( "error for custom logger" ) , 2 ) ;
594639
595- reset_logging_output_handler ( ) ;
640+ // Test whether throttling works correctly with a ROS clock
641+ let ( clock, source) = Clock :: with_source ( ) ;
642+ source. set_ros_time_override ( 0 ) ;
643+
644+ for i in 0 ..15 {
645+ log ! (
646+ "logger"
647+ . throttle( Duration :: from_nanos( 10 ) )
648+ . throttle_clock( ThrottleClock :: Clock ( & clock) ) ,
649+ "custom clock throttled message" ,
650+ ) ;
651+ source. set_ros_time_override ( i) ;
652+ }
596653
654+ // The throttle interval is 10ns and the loop shifted the time from 0ns
655+ // to 14ns, triggering the log macro once per nanosecond. That means we
656+ // should see two messages in the log.
657+ assert_eq ! ( count_message( "custom clock throttled message" ) , 2 ) ;
658+
659+ reset_logging_output_handler ( ) ;
597660 Ok ( ( ) )
598661 }
599662
0 commit comments