1
1
use core:: ffi:: { c_char, c_uint, c_void} ;
2
+ use core:: num:: NonZeroU32 ;
2
3
use core:: ptr;
4
+ use core:: sync:: atomic:: { AtomicU32 , Ordering } ;
3
5
use std:: os:: unix:: ffi:: OsStrExt ;
4
6
use std:: path:: PathBuf ;
5
- use std:: sync:: OnceLock ;
6
7
7
8
use super :: OSVersion ;
8
9
use crate :: rc:: { autoreleasepool, Allocated , Retained } ;
@@ -97,14 +98,25 @@ pub(crate) const DEPLOYMENT_TARGET: OSVersion = {
97
98
pub ( crate ) fn current_version ( ) -> OSVersion {
98
99
// Cache the lookup for performance.
99
100
//
100
- // TODO: Maybe just use atomics, a `Once` seems like overkill, it doesn't
101
- // matter if two threads end up racing to read the version?
102
- static CURRENT_VERSION : OnceLock < OSVersion > = OnceLock :: new ( ) ;
101
+ // We assume that 0.0.0 is never gonna be a valid version,
102
+ // and use that as our sentinel value.
103
+ static CURRENT_VERSION : AtomicU32 = AtomicU32 :: new ( 0 ) ;
103
104
104
- * CURRENT_VERSION . get_or_init ( lookup_version)
105
+ // We use relaxed atomics, it doesn't matter if two threads end up racing
106
+ // to read or write the version.
107
+ let version = CURRENT_VERSION . load ( Ordering :: Relaxed ) ;
108
+ OSVersion :: from_u32 ( if version == 0 {
109
+ // TODO: Consider using `std::panic::abort_unwind` here for code-size?
110
+ let version = lookup_version ( ) . get ( ) ;
111
+ CURRENT_VERSION . store ( version, Ordering :: Relaxed ) ;
112
+ version
113
+ } else {
114
+ version
115
+ } )
105
116
}
106
117
107
- fn lookup_version ( ) -> OSVersion {
118
+ #[ cold]
119
+ fn lookup_version ( ) -> NonZeroU32 {
108
120
// Since macOS 10.15, libSystem has provided the undocumented
109
121
// `_availability_version_check` via `libxpc` for doing this version
110
122
// lookup, though it's usage may be a bit dangerous, see:
@@ -114,7 +126,10 @@ fn lookup_version() -> OSVersion {
114
126
// So instead, we use the safer approach of reading from `sysctl`, and
115
127
// if that fails, we fall back to the property list (this is what
116
128
// `_availability_version_check` does internally).
117
- version_from_sysctl ( ) . unwrap_or_else ( version_from_plist)
129
+ let version = version_from_sysctl ( ) . unwrap_or_else ( version_from_plist) ;
130
+ // Use `NonZeroU32` to try to make it clearer to the optimizer that this
131
+ // will never return 0.
132
+ NonZeroU32 :: new ( version. to_u32 ( ) ) . expect ( "version cannot be 0.0.0" )
118
133
}
119
134
120
135
/// Read the version from `kern.osproductversion` or `kern.iossupportversion`.
0 commit comments