1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
mod safety_boundary {
use std::{cell::Cell, ptr::NonNull};
pub(super) struct Impl<T: ?Sized>(Cell<Option<NonNull<T>>>);
impl<T: ?Sized> Impl<T> {
#[inline]
pub(super) const fn new() -> Self {
Self(Cell::new(None))
}
#[inline]
pub(super) const fn opt_static_ref(v: Option<&'static T>) -> Self {
Self(Cell::new(if let Some(v) = v {
// SAFETY: v is a valid reference for lifetime 'static
unsafe { Some(NonNull::new_unchecked(v as *const T as *mut T)) }
} else {
None
}))
}
/// set `self` to `value` for the duration of the `f()` call.
#[inline]
pub(super) fn set_opt<F: FnOnce() -> R, R>(&self, value: Option<&T>, f: F) -> R {
struct ResetOnDrop<'a, T: ?Sized> {
this: &'a Impl<T>,
old: Option<NonNull<T>>,
}
impl<T: ?Sized> Drop for ResetOnDrop<'_, T> {
fn drop(&mut self) {
self.this.0.set(self.old);
}
}
// reset to old value before exiting this function ensuring `self`
// is not set to `value` when its lifetime is expired
let _reset_on_drop = ResetOnDrop {
this: self,
old: self.0.replace(value.map(NonNull::from)),
};
f()
}
#[inline]
pub(super) fn get_ptr(&self) -> Option<NonNull<T>> {
self.0.get()
}
/// get the reference in `self` for the duration of the `f(...)` call.
#[inline]
pub(super) fn with_opt<F: for<'a> FnOnce(Option<&'a T>) -> R, R>(&self, f: F) -> R {
// SAFETY:
// `self.0` is only `Some` when inside some `set_opt` call or when set
// to some `&'static T`, which ensures that the pointer is live and valid.
//
// the reference we give away has its lifetime scoped to this
// function call which ensures that it won't escape
unsafe { f(self.0.get().map(|v| &*v.as_ptr())) }
}
}
}
/// holds a `Cell<Option<&'scoped T>>` where `'scoped` is erased. This is useful for holding references in TLS.
pub struct ScopedRef<T: ?Sized>(safety_boundary::Impl<T>);
impl<T: ?Sized> ScopedRef<T> {
/// create a new empty [`ScopedRef`]
#[inline]
pub const fn new() -> Self {
Self(safety_boundary::Impl::new())
}
#[inline]
pub const fn opt_static_ref(v: Option<&'static T>) -> Self {
Self(safety_boundary::Impl::opt_static_ref(v))
}
#[inline]
pub const fn static_ref(v: &'static T) -> Self {
Self::opt_static_ref(Some(v))
}
/// set `self` to `value` for the duration of the `f()` call.
#[inline]
pub fn set_opt<F: FnOnce() -> R, R>(&self, value: Option<&T>, f: F) -> R {
self.0.set_opt(value, f)
}
/// set `self` to `value` for the duration of the `f()` call.
#[inline]
pub fn set<F: FnOnce() -> R, R>(&self, value: &T, f: F) -> R {
self.0.set_opt(Some(value), f)
}
#[inline]
pub fn is_some(&self) -> bool {
self.0.get_ptr().is_some()
}
#[inline]
pub fn is_none(&self) -> bool {
self.0.get_ptr().is_none()
}
/// get the reference in `self` for the duration of the `f(...)` call. panics if no reference is set.
#[inline]
pub fn with<F: FnOnce(&T) -> R, R>(&self, f: F) -> R {
self.0.with_opt(
#[inline]
|v| f(v.expect("called ScopedRef::with on an empty ScopedRef")),
)
}
/// get the reference in `self` for the duration of the `f(...)` call.
#[inline]
pub fn with_opt<F: FnOnce(Option<&T>) -> R, R>(&self, f: F) -> R {
self.0.with_opt(f)
}
}
impl<T: ?Sized> Default for ScopedRef<T> {
fn default() -> Self {
Self::new()
}
}