77
88use crate :: sync:: atomic:: { AtomicPtr , Ordering } ;
99
10+ #[ cfg( target_arch = "aarch64" ) ]
11+ use crate :: sync:: atomic:: AtomicU64 ;
12+
1013use core:: {
1114 fmt,
1215 ops:: { Add , AddAssign , Sub , SubAssign } ,
@@ -15,6 +18,9 @@ use core::{
1518
1619static ELAPSED_GETTER : AtomicPtr < ( ) > = AtomicPtr :: new ( unset_getter as * mut _ ) ;
1720
21+ #[ cfg( target_arch = "aarch64" ) ]
22+ static CNTFRQ : AtomicU64 = AtomicU64 :: new ( 0 ) ;
23+
1824/// Fallback implementation of `Instant` suitable for a `no_std` environment.
1925///
2026/// If you are on any of the following target architectures, this is a drop-in replacement:
@@ -166,15 +172,48 @@ fn unset_getter() -> Duration {
166172 }
167173 #[ cfg( target_arch = "aarch64" ) ] => {
168174 // SAFETY: standard technique for getting a nanosecond counter of aarch64
169- let nanos = unsafe {
175+ let ( freq, ticks) = unsafe {
176+ let mut freq = CNTFRQ . load( Ordering :: Relaxed ) ;
177+ if freq == 0 {
178+ core:: arch:: asm!( "mrs {}, cntfrq_el0" , out( reg) freq) ;
179+ if let Err ( existing) = CNTFRQ . compare_exchange( 0 , freq, Ordering :: Release , Ordering :: Relaxed ) {
180+ freq = existing;
181+ }
182+ }
183+
170184 let mut ticks: u64 ;
171185 core:: arch:: asm!( "mrs {}, cntvct_el0" , out( reg) ticks) ;
172- ticks
186+
187+ ( freq, ticks)
173188 } ;
189+
190+ #[ rustfmt:: skip]
191+ let nanos = ( ticks / freq) . saturating_mul( 1_000_000_000 ) +
192+ ( ticks % freq) . saturating_mul( 1_000_000_000 ) / freq;
193+
174194 Duration :: from_nanos( nanos)
175195 }
176196 _ => {
177197 panic!( "An elapsed time getter has not been provided to `Instant`. Please use `Instant::set_elapsed(...)` before calling `Instant::now()`" )
178198 }
179199 }
180200}
201+
202+ #[ cfg( test) ]
203+ mod tests {
204+ use std:: thread:: sleep;
205+
206+ use super :: * ;
207+
208+ #[ test]
209+ fn test_unset_getter ( ) {
210+ let dur1 = unset_getter ( ) ;
211+
212+ sleep ( Duration :: from_millis ( 500 ) ) ;
213+
214+ let dur2 = unset_getter ( ) ;
215+
216+ assert ! ( dur2 > dur1) ;
217+ assert ! ( dur2 - dur1 >= Duration :: from_millis( 500 ) ) ;
218+ }
219+ }
0 commit comments