thread_scoped_ref/
scope.rs

1use std::cell::RefCell;
2use std::ops::Deref;
3
4/// A scope. There's usually one scope per thread.
5///
6/// Note: Usually you don't use this directly. See [`thread_scoped_ref`] (with example)
7/// and [`with`] / [`scoped`].
8///
9/// # Safety
10///
11/// This struct uses unsafe functionality. When calling the `scope` function a reference to the
12/// value is kept as long as the `scope` function is running. The reference to the value
13/// is removed when the scope function ends. To make sure the reference is removed even in the
14/// case of a panic, there's a cleanup struct that performs cleanup when dropped.
15///
16/// [`thread_scoped_ref`]: macro.thread_scoped_ref.html
17/// [`with`]: fn.with.html
18/// [`scoped`]: fn.scoped.html
19pub struct Scope<T>(RefCell<Option<*const T>>)
20where
21    T: ?Sized;
22
23/// Creates a new scope without value.
24impl<T> Default for Scope<T>
25where
26    T: ?Sized,
27{
28    fn default() -> Self {
29        Self(RefCell::new(None))
30    }
31}
32
33impl<T> Scope<T>
34where
35    T: ?Sized,
36{
37    /// Run the given function scoped with given value reference.
38    ///
39    /// Note: Panicking within the function should be ok, the scope is cleaned up properly.
40    #[inline]
41    pub fn scoped<TFn, TRet>(&self, value: &T, fun: TFn) -> TRet
42    where
43        TFn: FnOnce() -> TRet,
44    {
45        // make sure we always remove the value (even when panicking).
46        let mut cleanup_on_drop = CleanupOnDrop {
47            scope: Some(self),
48            previous_value: self.take(),
49        };
50        self.set(Some(value));
51        let fun_result = fun();
52        cleanup_on_drop.cleanup();
53        fun_result
54    }
55
56    /// Runs the given function with the value from the scope (if there's any). If you're not
57    /// inside a scope, there won't be a value (function will receive `None`).
58    #[inline]
59    pub fn with<TFn, TRet>(&self, fun: TFn) -> TRet
60    where
61        TFn: FnOnce(Option<&T>) -> TRet,
62    {
63        let value = self.get();
64        fun(value)
65    }
66
67    #[inline]
68    fn set(&self, value: Option<&T>) {
69        *self.0.borrow_mut() = if let Some(value) = value {
70            Some(value as *const T)
71        } else {
72            None
73        };
74    }
75
76    #[inline]
77    fn get(&self) -> Option<&T> {
78        let self_borrowed = self.0.borrow();
79        if let Some(value) = self_borrowed.deref() {
80            Some(unsafe { &*(*value) })
81        } else {
82            None
83        }
84    }
85
86    #[inline]
87    fn take(&self) -> Option<&T> {
88        let mut self_borrowed = self.0.borrow_mut();
89        if let Some(taken) = self_borrowed.take() {
90            Some(unsafe { &*taken })
91        } else {
92            None
93        }
94    }
95}
96
97struct CleanupOnDrop<'a, T>
98where
99    T: ?Sized,
100{
101    scope: Option<&'a Scope<T>>,
102    previous_value: Option<&'a T>,
103}
104
105impl<'a, T> CleanupOnDrop<'a, T>
106where
107    T: ?Sized,
108{
109    fn cleanup(&mut self) {
110        if let Some(scope) = self.scope.take() {
111            scope.set(self.previous_value);
112        }
113    }
114}
115
116impl<'a, T> Drop for CleanupOnDrop<'a, T>
117where
118    T: ?Sized,
119{
120    fn drop(&mut self) {
121        self.cleanup();
122    }
123}