1use std::{
2 fmt::Display,
3 marker::PhantomData,
4 ops::{Deref, DerefMut},
5 ptr::{NonNull, null_mut},
6 sync::atomic::{AtomicBool, AtomicPtr, Ordering},
7};
8
9use derive_more::{Display, Error, From};
10
11#[cfg(test)]
12mod test;
13
14pub struct FfiCell<T: Sync> {
15 ptr: AtomicPtr<T>,
16 in_use: AtomicBool,
17}
18
19impl<T: Sync> FfiCell<T> {
20 pub const fn new() -> Self {
21 Self {
22 ptr: AtomicPtr::new(null_mut()),
23 in_use: AtomicBool::new(false),
24 }
25 }
26
27 #[track_caller]
28 pub fn run<R>(&self, object: &mut T, f: impl FnOnce() -> R) -> R {
29 self.try_run(object, f).unwrap_or_display_err()
30 }
31
32 pub fn try_run<R>(
33 &self,
34 object: &mut T,
35 f: impl FnOnce() -> R,
36 ) -> Result<R, Error> {
37 unsafe {
38 self.try_lend(object)?;
39 }
40 let _reclaim = ScopeGuard::new(|| self.reclaim());
41 Ok(f())
42 }
43
44 #[track_caller]
49 pub unsafe fn lend(&self, ptr: &mut T) {
50 unsafe { self.try_lend(ptr).unwrap_or_display_err() }
51 }
52
53 pub unsafe fn try_lend(&self, ptr: &mut T) -> Result<(), LendError> {
58 if self.in_use.load(Ordering::SeqCst) {
61 return Err(LendError::AlreadyLent);
62 }
63
64 match self.ptr.compare_exchange(
65 null_mut(),
66 ptr,
67 Ordering::SeqCst,
68 Ordering::SeqCst,
69 ) {
70 Ok(_) => Ok(()),
71 Err(_) => Err(LendError::AlreadyHasLoan),
72 }
73 }
74
75 #[track_caller]
76 pub fn borrow(&self) -> impl DerefMut<Target = T> {
77 self.try_borrow().unwrap_or_display_err()
78 }
79
80 pub fn try_borrow(&self) -> Result<impl DerefMut<Target = T>, BorrowError> {
81 if self.in_use.swap(true, Ordering::SeqCst) {
82 Err(BorrowError::AlreadyBorrowed)
83 } else {
84 let ptr = self.ptr.swap(null_mut(), Ordering::SeqCst);
85 match NonNull::new(ptr) {
86 Some(ptr) => Ok(FfiGuard {
87 ptr,
88 cell: self,
89 _marker: PhantomData,
90 }),
91 None => Err(BorrowError::Unavailable),
92 }
93 }
94 }
95
96 #[track_caller]
97 pub fn reclaim(&self) {
98 self.try_reclaim().unwrap_or_display_err()
99 }
100
101 pub fn try_reclaim(&self) -> Result<(), ReclaimError> {
102 if self.in_use.load(Ordering::SeqCst) {
103 Err(ReclaimError::InUse)
104 } else if self.ptr.swap(null_mut(), Ordering::SeqCst).is_null() {
105 unreachable!("missing pointer when not in use")
106 } else {
107 Ok(())
108 }
109 }
110}
111
112impl<T: Sync> Default for FfiCell<T> {
113 fn default() -> Self {
114 Self::new()
115 }
116}
117
118struct FfiGuard<'g, T: Sync> {
119 ptr: NonNull<T>,
120 cell: &'g FfiCell<T>,
121 _marker: PhantomData<&'g ()>,
122}
123
124impl<'g, T: Sync> Deref for FfiGuard<'g, T> {
125 type Target = T;
126
127 fn deref(&self) -> &Self::Target {
128 unsafe { self.ptr.as_ref() }
129 }
130}
131
132impl<'g, T: Sync> DerefMut for FfiGuard<'g, T> {
133 fn deref_mut(&mut self) -> &mut Self::Target {
134 unsafe { self.ptr.as_mut() }
135 }
136}
137
138impl<'g, T: Sync> Drop for FfiGuard<'g, T> {
139 fn drop(&mut self) {
140 self
141 .cell
142 .ptr
143 .compare_exchange(
144 null_mut(),
145 self.ptr.as_ptr(),
146 Ordering::SeqCst,
147 Ordering::SeqCst,
148 )
149 .expect("tried to return lent pointer, but another pointer was there");
150 let was_in_use = self.cell.in_use.swap(false, Ordering::SeqCst);
151 assert!(was_in_use, "object was not in use when it was returned");
152 }
153}
154
155#[non_exhaustive]
156#[derive(Debug, Display, Error, From)]
157pub enum Error {
158 LendError(LendError),
159 BorrowError(BorrowError),
160}
161
162#[non_exhaustive]
163#[derive(Debug, Display, Error)]
164#[display("cannot lend value to ffi-cell because {_variant}")]
165pub enum LendError {
166 #[display("it currently has one and it is already lent out")]
167 AlreadyLent,
168 #[display("it already has one")]
169 AlreadyHasLoan,
170}
171
172#[non_exhaustive]
173#[derive(Debug, Display, Error)]
174#[display("cannot borrow value from ffi-cell because {_variant}")]
175pub enum BorrowError {
176 #[display("the cell does not have a value")]
177 Unavailable,
178 #[display("the cell's value is already lent out")]
179 AlreadyBorrowed,
180}
181
182#[non_exhaustive]
183#[derive(Debug, Display, Error)]
184#[display("cannot reclaim value from ffi-cell because {_variant}")]
185pub enum ReclaimError {
186 #[display("it is currently in use")]
187 InUse,
188}
189
190struct ScopeGuard<F: FnMut()>(F);
191
192impl<F: FnMut()> ScopeGuard<F> {
193 fn new(f: F) -> Self {
194 Self(f)
195 }
196}
197
198impl<F: FnMut()> Drop for ScopeGuard<F> {
199 fn drop(&mut self) {
200 (self.0)()
201 }
202}
203
204trait ResultExt<T> {
205 #[track_caller]
206 fn unwrap_or_display_err(self) -> T;
207}
208
209impl<T, E: Display> ResultExt<T> for Result<T, E> {
210 fn unwrap_or_display_err(self) -> T {
211 match self {
212 Ok(val) => val,
213 Err(err) => panic!("{err}"),
214 }
215 }
216}