1use std::cmp;
2use std::fmt;
3use std::mem;
4use std::mem::ManuallyDrop;
5#[cfg(feature = "future")]
6use std::pin::Pin;
7
8use crate::errors::InvalidThreadAccess;
9use crate::thread_id::{self, ThreadId};
10
11enum FragileValue<T> {
14 Inline(T),
15 #[cfg(feature = "future")]
16 Pinned(Pin<Box<T>>),
17 #[cfg(feature = "future")]
18 Taken,
19}
20
21pub struct Fragile<T> {
36 value: ManuallyDrop<FragileValue<T>>,
40 thread_id: ThreadId,
43}
44
45impl<T> Fragile<T> {
46 pub fn new(value: T) -> Self {
53 Fragile {
54 value: ManuallyDrop::new(FragileValue::Inline(value)),
55 thread_id: thread_id::current(),
56 }
57 }
58
59 pub fn is_valid(&self) -> bool {
63 thread_id::current() == self.thread_id
64 }
65
66 #[inline(always)]
67 #[track_caller]
68 fn assert_thread(&self) {
69 if !self.is_valid() {
70 panic!("trying to access wrapped value in fragile container from incorrect thread.");
71 }
72 }
73
74 fn value(&self) -> &T {
75 match &*self.value {
76 FragileValue::Inline(value) => value,
77 #[cfg(feature = "future")]
78 FragileValue::Pinned(value) => value.as_ref().get_ref(),
79 #[cfg(feature = "future")]
80 FragileValue::Taken => unreachable!("fragile value is missing"),
81 }
82 }
83
84 fn value_mut(&mut self) -> &mut T {
85 match &mut *self.value {
86 FragileValue::Inline(value) => value,
87 #[cfg(feature = "future")]
90 FragileValue::Pinned(value) => unsafe { value.as_mut().get_unchecked_mut() },
91 #[cfg(feature = "future")]
92 FragileValue::Taken => unreachable!("fragile value is missing"),
93 }
94 }
95
96 #[cfg(feature = "future")]
97 #[track_caller]
98 pub(crate) fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> {
99 let this = unsafe { self.get_unchecked_mut() };
103 this.assert_thread();
104
105 if matches!(&*this.value, FragileValue::Inline(_)) {
106 let value = match mem::replace(&mut *this.value, FragileValue::Taken) {
108 FragileValue::Inline(value) => value,
109 _ => unreachable!(),
110 };
111 *this.value = FragileValue::Pinned(Box::pin(value));
112 }
113
114 match &mut *this.value {
115 FragileValue::Pinned(value) => value.as_mut(),
116 _ => unreachable!(),
117 }
118 }
119
120 #[track_caller]
127 pub fn into_inner(self) -> T {
128 self.assert_thread();
129
130 let mut this = ManuallyDrop::new(self);
131
132 match unsafe { ManuallyDrop::take(&mut this.value) } {
135 FragileValue::Inline(value) => value,
136 #[cfg(feature = "future")]
137 FragileValue::Pinned(value) => {
138 *unsafe { Pin::into_inner_unchecked(value) }
141 }
142 #[cfg(feature = "future")]
143 FragileValue::Taken => unreachable!("fragile value is missing"),
144 }
145 }
146
147 pub fn try_into_inner(self) -> Result<T, Self> {
153 if self.is_valid() {
154 Ok(self.into_inner())
155 } else {
156 Err(self)
157 }
158 }
159
160 #[track_caller]
167 pub fn get(&self) -> &T {
168 self.assert_thread();
169 self.value()
170 }
171
172 #[track_caller]
179 pub fn get_mut(&mut self) -> &mut T {
180 self.assert_thread();
181 self.value_mut()
182 }
183
184 pub fn try_get(&self) -> Result<&T, InvalidThreadAccess> {
188 if self.is_valid() {
189 Ok(self.value())
190 } else {
191 Err(InvalidThreadAccess)
192 }
193 }
194
195 pub fn try_get_mut(&mut self) -> Result<&mut T, InvalidThreadAccess> {
199 if self.is_valid() {
200 Ok(self.value_mut())
201 } else {
202 Err(InvalidThreadAccess)
203 }
204 }
205}
206
207impl<T> Drop for Fragile<T> {
208 #[track_caller]
209 fn drop(&mut self) {
210 if !mem::needs_drop::<T>() || self.is_valid() {
213 unsafe { ManuallyDrop::drop(&mut self.value) };
215 } else {
216 panic!("destructor of fragile object ran on wrong thread");
220 }
221 }
222}
223
224impl<T> From<T> for Fragile<T> {
225 #[inline]
226 fn from(t: T) -> Fragile<T> {
227 Fragile::new(t)
228 }
229}
230
231impl<T: Clone> Clone for Fragile<T> {
232 #[inline]
233 #[track_caller]
234 fn clone(&self) -> Fragile<T> {
235 Fragile::new(self.get().clone())
236 }
237}
238
239impl<T: Default> Default for Fragile<T> {
240 #[inline]
241 fn default() -> Fragile<T> {
242 Fragile::new(T::default())
243 }
244}
245
246impl<T: PartialEq> PartialEq for Fragile<T> {
247 #[inline]
248 #[track_caller]
249 fn eq(&self, other: &Fragile<T>) -> bool {
250 *self.get() == *other.get()
251 }
252}
253
254impl<T: Eq> Eq for Fragile<T> {}
255
256impl<T: PartialOrd> PartialOrd for Fragile<T> {
257 #[inline]
258 #[track_caller]
259 fn partial_cmp(&self, other: &Fragile<T>) -> Option<cmp::Ordering> {
260 self.get().partial_cmp(other.get())
261 }
262
263 #[inline]
264 #[track_caller]
265 fn lt(&self, other: &Fragile<T>) -> bool {
266 *self.get() < *other.get()
267 }
268
269 #[inline]
270 #[track_caller]
271 fn le(&self, other: &Fragile<T>) -> bool {
272 *self.get() <= *other.get()
273 }
274
275 #[inline]
276 #[track_caller]
277 fn gt(&self, other: &Fragile<T>) -> bool {
278 *self.get() > *other.get()
279 }
280
281 #[inline]
282 #[track_caller]
283 fn ge(&self, other: &Fragile<T>) -> bool {
284 *self.get() >= *other.get()
285 }
286}
287
288impl<T: Ord> Ord for Fragile<T> {
289 #[inline]
290 #[track_caller]
291 fn cmp(&self, other: &Fragile<T>) -> cmp::Ordering {
292 self.get().cmp(other.get())
293 }
294}
295
296impl<T: fmt::Display> fmt::Display for Fragile<T> {
297 #[track_caller]
298 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
299 fmt::Display::fmt(self.get(), f)
300 }
301}
302
303impl<T: fmt::Debug> fmt::Debug for Fragile<T> {
304 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
305 match self.try_get() {
306 Ok(value) => f.debug_struct("Fragile").field("value", value).finish(),
307 Err(..) => {
308 struct InvalidPlaceholder;
309 impl fmt::Debug for InvalidPlaceholder {
310 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
311 f.write_str("<invalid thread>")
312 }
313 }
314
315 f.debug_struct("Fragile")
316 .field("value", &InvalidPlaceholder)
317 .finish()
318 }
319 }
320 }
321}
322
323unsafe impl<T> Sync for Fragile<T> {}
326
327#[allow(clippy::non_send_fields_in_send_ty)]
330unsafe impl<T> Send for Fragile<T> {}
331
332#[test]
333fn test_basic() {
334 use std::thread;
335 let val = Fragile::new(true);
336 assert_eq!(val.to_string(), "true");
337 assert_eq!(val.get(), &true);
338 assert!(val.try_get().is_ok());
339 thread::spawn(move || {
340 assert!(val.try_get().is_err());
341 })
342 .join()
343 .unwrap();
344}
345
346#[test]
347fn test_mut() {
348 let mut val = Fragile::new(true);
349 *val.get_mut() = false;
350 assert_eq!(val.to_string(), "false");
351 assert_eq!(val.get(), &false);
352}
353
354#[test]
355#[should_panic]
356fn test_access_other_thread() {
357 use std::thread;
358 let val = Fragile::new(true);
359 thread::spawn(move || {
360 val.get();
361 })
362 .join()
363 .unwrap();
364}
365
366#[test]
367fn test_noop_drop_elsewhere() {
368 use std::thread;
369 let val = Fragile::new(true);
370 thread::spawn(move || {
371 val.try_get().ok();
373 })
374 .join()
375 .unwrap();
376}
377
378#[test]
379fn test_panic_on_drop_elsewhere() {
380 use std::sync::atomic::{AtomicBool, Ordering};
381 use std::sync::Arc;
382 use std::thread;
383 let was_called = Arc::new(AtomicBool::new(false));
384 struct X(Arc<AtomicBool>);
385 impl Drop for X {
386 fn drop(&mut self) {
387 self.0.store(true, Ordering::SeqCst);
388 }
389 }
390 let val = Fragile::new(X(was_called.clone()));
391 assert!(thread::spawn(move || {
392 val.try_get().ok();
393 })
394 .join()
395 .is_err());
396 assert!(!was_called.load(Ordering::SeqCst));
397}
398
399#[test]
400fn test_drop_in_tls_destructor() {
401 use std::cell::RefCell;
402 use std::sync::atomic::{AtomicUsize, Ordering};
403 use std::sync::Arc;
404 use std::thread;
405
406 struct X(Arc<AtomicUsize>);
407
408 impl Drop for X {
409 fn drop(&mut self) {
410 self.0.fetch_add(1, Ordering::SeqCst);
411 }
412 }
413
414 thread_local!(static SLOT: RefCell<Option<Fragile<X>>> = RefCell::new(None));
415
416 let drop_count = Arc::new(AtomicUsize::new(0));
417 let thread_drop_count = drop_count.clone();
418
419 thread::spawn(move || {
422 SLOT.with(|slot| *slot.borrow_mut() = Some(Fragile::new(X(thread_drop_count))));
423 })
424 .join()
425 .unwrap();
426
427 assert_eq!(drop_count.load(Ordering::SeqCst), 1);
428}
429
430#[test]
431fn test_rc_sending() {
432 use std::rc::Rc;
433 use std::sync::mpsc::channel;
434 use std::thread;
435
436 let val = Fragile::new(Rc::new(true));
437 let (tx, rx) = channel();
438
439 let thread = thread::spawn(move || {
440 assert!(val.try_get().is_err());
441 let here = val;
442 tx.send(here).unwrap();
443 });
444
445 let rv = rx.recv().unwrap();
446 assert!(**rv.get());
447
448 thread.join().unwrap();
449}