summaryrefslogtreecommitdiffstats
path: root/vendor/gimli/src/read/lazy.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gimli/src/read/lazy.rs')
-rw-r--r--vendor/gimli/src/read/lazy.rs116
1 files changed, 116 insertions, 0 deletions
diff --git a/vendor/gimli/src/read/lazy.rs b/vendor/gimli/src/read/lazy.rs
new file mode 100644
index 000000000..6138735c8
--- /dev/null
+++ b/vendor/gimli/src/read/lazy.rs
@@ -0,0 +1,116 @@
+pub(crate) use imp::*;
+
+#[cfg(not(feature = "std"))]
+mod imp {
+ use alloc::sync::Arc;
+ use core::sync::atomic::{AtomicPtr, Ordering};
+ use core::{mem, ptr};
+
+ #[derive(Debug, Default)]
+ pub(crate) struct LazyArc<T> {
+ // Only written once with a value obtained from `Arc<T>::into_raw`.
+ // This holds a ref count for the `Arc`, so it is always safe to
+ // clone the `Arc` given a reference to the `LazyArc`.
+ value: AtomicPtr<T>,
+ }
+
+ impl<T> Drop for LazyArc<T> {
+ fn drop(&mut self) {
+ let value_ptr = self.value.load(Ordering::Acquire);
+ if !value_ptr.is_null() {
+ // SAFETY: all writes to `self.value` are pointers obtained from `Arc::into_raw`.
+ drop(unsafe { Arc::from_raw(value_ptr) });
+ }
+ }
+ }
+
+ impl<T> LazyArc<T> {
+ pub(crate) fn get<E, F: FnOnce() -> Result<T, E>>(&self, f: F) -> Result<Arc<T>, E> {
+ // Clone an `Arc` given a pointer obtained from `Arc::into_raw`.
+ // SAFETY: `value_ptr` must be a valid pointer obtained from `Arc<T>::into_raw`.
+ unsafe fn clone_arc_ptr<T>(value_ptr: *const T) -> Arc<T> {
+ let value = Arc::from_raw(value_ptr);
+ let clone = Arc::clone(&value);
+ mem::forget(value);
+ clone
+ }
+
+ // Return the existing value if already computed.
+ // `Ordering::Acquire` is needed so that the content of the loaded `Arc` is
+ // visible to this thread.
+ let value_ptr = self.value.load(Ordering::Acquire);
+ if !value_ptr.is_null() {
+ // SAFETY: all writes to `self.value` are pointers obtained from `Arc::into_raw`.
+ return Ok(unsafe { clone_arc_ptr(value_ptr) });
+ }
+
+ // Race to compute and set the value.
+ let value = f().map(Arc::new)?;
+ let value_ptr = Arc::into_raw(value);
+ match self.value.compare_exchange(
+ ptr::null_mut(),
+ value_ptr as *mut T,
+ // Success: `Ordering::Release` is needed so that the content of the stored `Arc`
+ // is visible to other threads. No ordering is required for the null ptr that is
+ // loaded, but older rust versions (< 1.64) require that its ordering must not
+ // be weaker than the failure ordering, so we use `Ordering::AcqRel`.
+ Ordering::AcqRel,
+ // Failure: `Ordering::Acquire` is needed so that the content of the loaded `Arc`
+ // is visible to this thread.
+ Ordering::Acquire,
+ ) {
+ Ok(_) => {
+ // Return the value we computed.
+ // SAFETY: `value_ptr` was obtained from `Arc::into_raw`.
+ Ok(unsafe { clone_arc_ptr(value_ptr) })
+ }
+ Err(existing_value_ptr) => {
+ // We lost the race, drop unneeded `value_ptr`.
+ // SAFETY: `value_ptr` was obtained from `Arc::into_raw`.
+ drop(unsafe { Arc::from_raw(value_ptr) });
+ // Return the existing value.
+ // SAFETY: all writes to `self.value` are pointers obtained from `Arc::into_raw`.
+ Ok(unsafe { clone_arc_ptr(existing_value_ptr) })
+ }
+ }
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+mod imp {
+ use std::sync::{Arc, Mutex};
+
+ #[derive(Debug, Default)]
+ pub(crate) struct LazyArc<T> {
+ value: Mutex<Option<Arc<T>>>,
+ }
+
+ impl<T> LazyArc<T> {
+ pub(crate) fn get<E, F: FnOnce() -> Result<T, E>>(&self, f: F) -> Result<Arc<T>, E> {
+ let mut lock = self.value.lock().unwrap();
+ if let Some(value) = &*lock {
+ return Ok(value.clone());
+ }
+ let value = f().map(Arc::new)?;
+ *lock = Some(value.clone());
+ Ok(value)
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn lazy_arc() {
+ let lazy = LazyArc::default();
+ let value = lazy.get(|| Err(()));
+ assert_eq!(value, Err(()));
+ let value = lazy.get(|| Ok::<i32, ()>(3)).unwrap();
+ assert_eq!(*value, 3);
+ let value = lazy.get(|| Err(())).unwrap();
+ assert_eq!(*value, 3);
+ }
+}