1use core::{cell::UnsafeCell, fmt, mem::ManuallyDrop};
2
3use crate::lock::{rank, RankData, RwLock, RwLockReadGuard, RwLockWriteGuard};
4use crate::resource::DestructibleResourceState;
5
6pub struct SnatchGuard<'a>(RwLockReadGuard<'a, ()>);
8pub struct ExclusiveSnatchGuard<'a>(#[expect(dead_code)] RwLockWriteGuard<'a, ()>);
10
11pub struct SnatchableInner<T> {
18 value: UnsafeCell<T>,
19}
20
21pub type Snatchable<T> = SnatchableInner<Option<T>>;
22
23impl<T> Snatchable<T> {
24 pub fn new(val: T) -> Self {
25 SnatchableInner {
26 value: UnsafeCell::new(Some(val)),
27 }
28 }
29
30 #[allow(dead_code)]
31 pub fn empty() -> Self {
32 SnatchableInner {
33 value: UnsafeCell::new(None),
34 }
35 }
36
37 pub fn get<'a>(&'a self, _guard: &'a SnatchGuard) -> Option<&'a T> {
39 unsafe { (*self.value.get()).as_ref() }
40 }
41
42 pub fn snatch(&self, _guard: &mut ExclusiveSnatchGuard) -> Option<T> {
44 unsafe { (*self.value.get()).take() }
45 }
46
47 pub fn take(&mut self) -> Option<T> {
52 self.value.get_mut().take()
53 }
54}
55
56impl<T> fmt::Debug for SnatchableInner<T> {
59 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
60 write!(f, "<snatchable>")
61 }
62}
63
64unsafe impl<T> Sync for SnatchableInner<T> {}
65
66pub type Snatchable2<T> = SnatchableInner<DestructibleResourceState<T>>;
73
74impl<T> Snatchable2<T> {
75 pub fn new(val: T) -> Self {
76 SnatchableInner {
77 value: UnsafeCell::new(DestructibleResourceState::Valid(val)),
78 }
79 }
80
81 pub fn invalid() -> Self {
82 SnatchableInner {
83 value: UnsafeCell::new(DestructibleResourceState::Invalid),
84 }
85 }
86
87 pub fn get<'a>(&'a self, _guard: &'a SnatchGuard) -> DestructibleResourceState<&'a T> {
89 unsafe { (*self.value.get()).as_ref() }
90 }
91
92 pub fn snatch(&self, _guard: &mut ExclusiveSnatchGuard) -> DestructibleResourceState<T> {
94 unsafe { (*self.value.get()).take() }
95 }
96
97 pub fn take(&mut self) -> DestructibleResourceState<T> {
102 self.value.get_mut().take()
103 }
104}
105
106use trace::LockTrace;
107#[cfg(all(debug_assertions, feature = "std"))]
108mod trace {
109 use core::{cell::Cell, fmt, panic::Location};
110 use std::{backtrace::Backtrace, thread};
111
112 pub(super) struct LockTrace {
113 purpose: &'static str,
114 caller: &'static Location<'static>,
115 backtrace: Backtrace,
116 }
117
118 impl fmt::Display for LockTrace {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 write!(
121 f,
122 "a {} lock at {}\n{}",
123 self.purpose, self.caller, self.backtrace
124 )
125 }
126 }
127
128 impl LockTrace {
129 #[track_caller]
130 pub(super) fn enter(purpose: &'static str) {
131 let new = LockTrace {
132 purpose,
133 caller: Location::caller(),
134 backtrace: Backtrace::capture(),
135 };
136
137 if let Some(prev) = SNATCH_LOCK_TRACE.take() {
138 let current = thread::current();
139 let name = current.name().unwrap_or("<unnamed>");
140 panic!(
141 "thread '{name}' attempted to acquire a snatch lock recursively.\n\
142 - Currently trying to acquire {new}\n\
143 - Previously acquired {prev}",
144 );
145 } else {
146 SNATCH_LOCK_TRACE.set(Some(new));
147 }
148 }
149
150 pub(super) fn exit() {
151 SNATCH_LOCK_TRACE.take();
152 }
153 }
154
155 std::thread_local! {
156 static SNATCH_LOCK_TRACE: Cell<Option<LockTrace>> = const { Cell::new(None) };
157 }
158}
159#[cfg(not(all(debug_assertions, feature = "std")))]
160mod trace {
161 pub(super) struct LockTrace {
162 _private: (),
163 }
164
165 impl LockTrace {
166 pub(super) fn enter(_purpose: &'static str) {}
167 pub(super) fn exit() {}
168 }
169}
170
171pub struct SnatchLock {
173 lock: RwLock<()>,
174}
175
176impl SnatchLock {
177 pub unsafe fn new(rank: rank::LockRank) -> Self {
182 SnatchLock {
183 lock: RwLock::new(rank, ()),
184 }
185 }
186
187 #[track_caller]
189 pub fn read(&self) -> SnatchGuard<'_> {
190 LockTrace::enter("read");
191 SnatchGuard(self.lock.read())
192 }
193
194 #[track_caller]
200 pub fn write(&self) -> ExclusiveSnatchGuard<'_> {
201 LockTrace::enter("write");
202 ExclusiveSnatchGuard(self.lock.write())
203 }
204
205 #[track_caller]
206 pub unsafe fn force_unlock_read(&self, data: RankData) {
207 LockTrace::exit();
211 unsafe { self.lock.force_unlock_read(data) };
212 }
213}
214
215impl SnatchGuard<'_> {
216 pub fn forget(this: Self) -> RankData {
221 let manually_drop = ManuallyDrop::new(this);
223
224 let inner_guard = unsafe { core::ptr::read(&manually_drop.0) };
228
229 RwLockReadGuard::forget(inner_guard)
230 }
231}
232
233impl Drop for SnatchGuard<'_> {
234 fn drop(&mut self) {
235 LockTrace::exit();
236 }
237}
238
239impl Drop for ExclusiveSnatchGuard<'_> {
240 fn drop(&mut self) {
241 LockTrace::exit();
242 }
243}