#[cfg(all(feature = "std", not(miri)))]
use std::sync::OnceLock;
#[cfg(all(feature = "std", not(miri)))]
static STD_CACHE: OnceLock<Detected> = OnceLock::new();
#[cold]
#[track_caller]
pub fn set_override(value: Option<Detected>) {
if let Err(err) = try_set_override(value) {
panic!("platform::set_override failed: {err}");
}
}
#[cold]
pub fn try_set_override(value: Option<Detected>) -> Result<(), OverrideError> {
#[cfg(feature = "std")]
{
#[cfg(not(miri))]
{
atomic_cache::try_set_override(value, STD_CACHE.get().is_some())
}
#[cfg(miri)]
{
atomic_cache::try_set_override(value, false)
}
}
#[cfg(all(not(feature = "std"), target_has_atomic = "64"))]
{
atomic_cache::try_set_override(value, false)
}
#[cfg(all(not(feature = "std"), not(target_has_atomic = "64")))]
{
let _ = value;
Err(OverrideError::Unsupported)
}
}
#[cold]
#[track_caller]
pub fn clear_override() {
set_override(None);
}
#[inline]
#[must_use]
pub fn has_override() -> bool {
#[cfg(feature = "std")]
{
atomic_cache::has_override()
}
#[cfg(all(not(feature = "std"), target_has_atomic = "64"))]
{
atomic_cache::has_override()
}
#[cfg(all(not(feature = "std"), not(target_has_atomic = "64")))]
{
false
}
}
#[cold]
#[cfg(not(miri))]
fn detect_with_override() -> Detected {
#[cfg(feature = "std")]
{
if let Some(ov) = atomic_cache::get_override() {
return ov;
}
}
#[cfg(all(not(feature = "std"), target_has_atomic = "64"))]
{
if let Some(ov) = atomic_cache::get_override() {
return ov;
}
}
detect_uncached()
}
#[cfg(any(feature = "std", all(not(feature = "std"), target_has_atomic = "64")))]
mod atomic_cache {
#[cfg(all(not(feature = "std"), target_has_atomic = "64"))]
use core::sync::atomic::AtomicU8;
use core::{
cell::UnsafeCell,
sync::atomic::{AtomicBool, Ordering},
};
use super::*;
#[cfg(all(not(feature = "std"), target_has_atomic = "64"))]
const STATE_UNINIT: u8 = 0;
#[cfg(all(not(feature = "std"), target_has_atomic = "64"))]
const STATE_INITING: u8 = 1;
#[cfg(all(not(feature = "std"), target_has_atomic = "64"))]
const STATE_READY: u8 = 2;
struct Slot<T>(UnsafeCell<T>);
unsafe impl<T: Sync> Sync for Slot<T> {}
impl<T> Slot<T> {
const fn new(value: T) -> Self {
Self(UnsafeCell::new(value))
}
}
#[cfg(all(not(feature = "std"), target_has_atomic = "64"))]
static STATE: AtomicU8 = AtomicU8::new(STATE_UNINIT);
#[cfg(all(not(feature = "std"), target_has_atomic = "64"))]
static CACHED: Slot<Detected> = Slot::new(Detected::portable());
static OVERRIDE_SET: AtomicBool = AtomicBool::new(false);
static OVERRIDE_VALUE: Slot<Option<Detected>> = Slot::new(None);
#[cfg(all(not(feature = "std"), target_has_atomic = "64"))]
pub fn get_or_init(f: fn() -> Detected) -> Detected {
if STATE.load(Ordering::Acquire) == STATE_READY {
return load_cached();
}
match STATE.compare_exchange(STATE_UNINIT, STATE_INITING, Ordering::AcqRel, Ordering::Acquire) {
Ok(_) => {
let result = f();
store_cached(&result);
STATE.store(STATE_READY, Ordering::Release);
result
}
Err(STATE_INITING) => {
while STATE.load(Ordering::Acquire) == STATE_INITING {
core::hint::spin_loop();
}
load_cached()
}
Err(_) => load_cached(),
}
}
#[cfg(all(not(feature = "std"), target_has_atomic = "64"))]
fn load_cached() -> Detected {
unsafe { *CACHED.0.get() }
}
#[cfg(all(not(feature = "std"), target_has_atomic = "64"))]
fn store_cached(det: &Detected) {
unsafe {
*CACHED.0.get() = *det;
}
}
pub fn try_set_override(value: Option<Detected>, already_initialized: bool) -> Result<(), OverrideError> {
#[cfg(all(not(feature = "std"), target_has_atomic = "64"))]
if already_initialized || STATE.load(Ordering::Acquire) == STATE_READY {
return Err(OverrideError::AlreadyInitialized);
}
#[cfg(feature = "std")]
if already_initialized {
return Err(OverrideError::AlreadyInitialized);
}
unsafe {
*OVERRIDE_VALUE.0.get() = value;
}
OVERRIDE_SET.store(value.is_some(), Ordering::Release);
Ok(())
}
pub fn has_override() -> bool {
OVERRIDE_SET.load(Ordering::Acquire)
}
#[cfg(not(miri))]
pub fn get_override() -> Option<Detected> {
if !OVERRIDE_SET.load(Ordering::Acquire) {
return None;
}
unsafe { *OVERRIDE_VALUE.0.get() }
}
}