55 reason = "Instant fallback requires unsafe to allow users to update the internal value"
66) ]
77
8- use crate :: sync:: atomic:: { AtomicPtr , Ordering } ;
8+ use crate :: sync:: atomic:: { AtomicPtr , AtomicU64 , Ordering } ;
99
1010use core:: {
1111 fmt,
@@ -15,6 +15,9 @@ use core::{
1515
1616static ELAPSED_GETTER : AtomicPtr < ( ) > = AtomicPtr :: new ( unset_getter as * mut _ ) ;
1717
18+ #[ cfg( target_arch = "aarch64" ) ]
19+ static CNTFRQ : AtomicU64 = AtomicU64 :: new ( 0 ) ;
20+
1821/// Fallback implementation of `Instant` suitable for a `no_std` environment.
1922///
2023/// If you are on any of the following target architectures, this is a drop-in replacement:
@@ -166,15 +169,48 @@ fn unset_getter() -> Duration {
166169 }
167170 #[ cfg( target_arch = "aarch64" ) ] => {
168171 // SAFETY: standard technique for getting a nanosecond counter of aarch64
169- let nanos = unsafe {
172+ let ( freq, ticks) = unsafe {
173+ let mut freq = CNTFRQ . load( Ordering :: Relaxed ) ;
174+ if freq == 0 {
175+ core:: arch:: asm!( "mrs {}, cntfrq_el0" , out( reg) freq) ;
176+ if let Err ( existing) = CNTFRQ . compare_exchange( 0 , freq, Ordering :: Release , Ordering :: Relaxed ) {
177+ freq = existing;
178+ }
179+ }
180+
170181 let mut ticks: u64 ;
171182 core:: arch:: asm!( "mrs {}, cntvct_el0" , out( reg) ticks) ;
172- ticks
183+
184+ ( freq, ticks)
173185 } ;
186+
187+ #[ rustfmt:: skip]
188+ let nanos = ( ticks / freq) . saturating_mul( 1_000_000_000 ) +
189+ ( ticks % freq) . saturating_mul( 1_000_000_000 ) / freq;
190+
174191 Duration :: from_nanos( nanos)
175192 }
176193 _ => {
177194 panic!( "An elapsed time getter has not been provided to `Instant`. Please use `Instant::set_elapsed(...)` before calling `Instant::now()`" )
178195 }
179196 }
180197}
198+
199+ #[ cfg( test) ]
200+ mod tests {
201+ use std:: thread:: sleep;
202+
203+ use super :: * ;
204+
205+ #[ test]
206+ fn test_unset_getter ( ) {
207+ let dur1 = unset_getter ( ) ;
208+
209+ sleep ( Duration :: from_millis ( 500 ) ) ;
210+
211+ let dur2 = unset_getter ( ) ;
212+
213+ assert ! ( dur2 > dur1) ;
214+ assert ! ( dur2 - dur1 >= Duration :: from_millis( 500 ) ) ;
215+ }
216+ }
0 commit comments