ref_portals/rc.rs
1//! Single-threaded anchors and portals.
2//! These don't implement `Send` or `Sync`, but are more efficient for use cases where that's not needed.
3
4use {
5 crate::{ANCHOR_DROPPED, ANCHOR_POISONED, ANCHOR_STILL_IN_USE},
6 log::error,
7 std::{
8 borrow::Borrow,
9 cell::{Ref, RefCell, RefMut},
10 fmt::Debug,
11 marker::PhantomData,
12 mem::ManuallyDrop,
13 ops::{Deref, DerefMut},
14 panic::{RefUnwindSafe, UnwindSafe},
15 ptr::NonNull,
16 rc::{Rc, Weak},
17 sync::Mutex, // Only to deadlock.
18 thread,
19 },
20 wyz::pipe::*,
21};
22
23/// Poison helper for `!Send` mutable anchors.
24#[derive(Debug)]
25struct Poisonable<T> {
26 pointer: T,
27 poisoned: bool,
28}
29
30/// An `!Send` immutable anchor.
31/// Use this to capture shared references in a single-threaded environment.
32///
33/// # Deadlocks
34///
35/// On drop, if any associated `Portal`s exist:
36///
37/// ```rust
38/// # use {assert_deadlock::assert_deadlock, std::time::Duration};
39/// use ref_portals::rc::Anchor;
40///
41/// let mut x = "Scoped".to_owned();
42/// let anchor = Anchor::new(&mut x);
43/// let portal = anchor.portal();
44///
45/// assert_deadlock!(drop(anchor), Duration::from_secs(1));
46/// ```
47#[derive(Debug)]
48#[repr(transparent)]
49pub struct Anchor<'a, T: ?Sized> {
50 /// Internal pointer to the target of the captured reference.
51 reference: ManuallyDrop<Rc<NonNull<T>>>,
52
53 /// Act as sharing borrower.
54 _phantom: PhantomData<&'a T>,
55}
56
57/// An `!Send` mutable anchor with overlapping immutable borrows.
58/// Use this to capture mutable references in a single-threaded environment.
59///
60/// # Deadlocks
61///
62/// Iff there is a currently active borrow, then dropping this anchor will cause a deadlock as last resort measure to prevent UB:
63///
64/// ```rust
65/// # use {assert_deadlock::assert_deadlock, std::time::Duration};
66/// use ref_portals::rc::RwAnchor;
67///
68/// let mut x = "Scoped".to_owned();
69/// let anchor = RwAnchor::new(&mut x);
70/// let portal = anchor.portal();
71/// let _guard = portal.borrow();
72///
73/// assert_deadlock!(drop(anchor), Duration::from_secs(1));
74/// ```
75///
76/// # Panics
77///
78/// On drop, if any associated `RwPortal`s exist:
79///
80/// ```rust
81/// # use assert_panic::assert_panic;
82/// use ref_portals::rc::RwAnchor;
83///
84/// let mut x = "Scoped".to_owned();
85/// let anchor = RwAnchor::new(&mut x);
86/// Box::leak(Box::new(anchor.portal()));
87///
88/// assert_panic!(
89/// drop(anchor),
90/// &str,
91/// "Anchor still in use (at least one portal exists)",
92/// );
93/// ```
94///
95/// Otherwise, on drop, iff the anchor has been poisoned:
96///
97/// ```rust
98/// # use assert_panic::assert_panic;
99/// use ref_portals::rc::RwAnchor;
100///
101/// let mut x = "Scoped".to_owned();
102/// let anchor = RwAnchor::new(&mut x);
103/// {
104/// let portal = anchor.portal();
105/// assert_panic!({
106/// let guard = portal.borrow_mut();
107/// panic!()
108/// });
109/// }
110///
111/// assert_panic!(
112/// drop(anchor),
113/// &str,
114/// "Anchor poisoned",
115/// );
116/// ```
117#[derive(Debug)]
118#[repr(transparent)]
119pub struct RwAnchor<'a, T: ?Sized> {
120 /// Internal pointer to the target of the captured reference.
121 reference: ManuallyDrop<Rc<RefCell<Poisonable<NonNull<T>>>>>,
122
123 /// Act as exclusive borrower.
124 _phantom: PhantomData<&'a mut T>,
125}
126
127impl<'a, T: ?Sized> Anchor<'a, T> {
128 /// Creates a new `Anchor` instance, capturing `reference`.
129 pub fn new(reference: &'a T) -> Anchor<'a, T> {
130 Self {
131 reference: ManuallyDrop::new(Rc::new(reference.into())),
132 _phantom: PhantomData,
133 }
134 }
135
136 /// Creates an infallible portal of indefinite lifetime associated with this anchor.
137 ///
138 /// # Example
139 ///
140 /// ```rust
141 /// # use ref_portals::rc::Anchor;
142 /// #
143 /// let x = "Scoped".to_owned();
144 /// let anchor = Anchor::new(&x);
145 /// let self_owned: Box<dyn Fn() + 'static> = Box::new({
146 /// let portal = anchor.portal();
147 /// move || println!("{}", *portal)
148 /// });
149 ///
150 /// self_owned(); // Scoped
151 /// ```
152 ///
153 #[inline]
154 pub fn portal(&self) -> Portal<T> {
155 self.reference.pipe_deref(Rc::clone).pipe(Portal)
156 }
157
158 /// Creates a weak portal of indefinite lifetime associated with this anchor.
159 /// Dropping an anchor doesn't panic if only weak portals exist.
160 #[inline]
161 pub fn weak_portal(&self) -> WeakPortal<T> {
162 Portal::downgrade(&self.portal())
163 }
164}
165
166impl<'a, T: ?Sized> RwAnchor<'a, T> {
167 /// Creates a new `RwAnchor` instance, capturing `reference`.
168 pub fn new(reference: &'a mut T) -> Self {
169 Self {
170 reference: ManuallyDrop::new(Rc::new(RefCell::new(Poisonable {
171 pointer: reference.into(),
172 poisoned: false,
173 }))),
174 _phantom: PhantomData,
175 }
176 }
177
178 /// Creates a fallible portal with unbounded lifetime supporting overlapping reads.
179 ///
180 /// # Example
181 ///
182 /// ```rust
183 /// # use ref_portals::rc::RwAnchor;
184 /// #
185 /// let mut x = "Scoped".to_owned();
186 /// let anchor = RwAnchor::new(&mut x);
187 /// let self_owned: Box<dyn Fn() + 'static> = Box::new({
188 /// let portal = anchor.portal();
189 /// move || {
190 /// println!("{}", *portal.borrow());
191 /// *portal.borrow_mut() = "Replacement".to_owned();
192 /// }
193 /// });
194 ///
195 /// self_owned(); // Scoped
196 /// drop(self_owned);
197 /// drop(anchor);
198 /// println!("{}", x); // Replacement
199 /// ```
200 ///
201 #[inline]
202 pub fn portal(&self) -> RwPortal<T> {
203 self.reference.pipe_deref(Rc::clone).pipe(RwPortal)
204 }
205
206 #[inline]
207 pub fn weak_portal(&self) -> WeakRwPortal<T> {
208 self.portal().downgrade()
209 }
210}
211
212impl<'a, T: ?Sized> Drop for Anchor<'a, T> {
213 //TODO: Deadlock if active borrows exist.
214 fn drop(&mut self) {
215 unsafe {
216 //SAFETY: Dropping.
217 ManuallyDrop::take(&mut self.reference)
218 }
219 .pipe(Rc::try_unwrap)
220 .unwrap_or_else(|_pointer| {
221 // Immutable portals are always active borrows, so we need to deadlock immediately here,
222 // since a reference could have been sent to another thread.
223 error!("!Send `Anchor` dropped while at least one Portal still exists. Deadlocking thread to prevent UB.");
224 let deadlock_mutex = Mutex::new(());
225 let _deadlock_guard = deadlock_mutex.lock().unwrap();
226 let _never = deadlock_mutex.lock();
227 // Congratulations.
228 unreachable!()
229 });
230 }
231}
232
233//TODO: Test deadlock.
234impl<'a, T: ?Sized> Drop for RwAnchor<'a, T> {
235 /// Executes the destructor for this type. [Read more](https://doc.rust-lang.org/nightly/core/ops/drop/trait.Drop.html#tymethod.drop)
236 ///
237 /// # Panics
238 ///
239 /// If any associated `RwPortal`s exist or, otherwise, iff the anchor has been poisoned:
240 ///
241 /// ```rust
242 /// # use assert_panic::assert_panic;
243 /// use ref_portals::rc::RwAnchor;
244 ///
245 /// let mut x = "Scoped".to_owned();
246 /// let anchor = RwAnchor::new(&mut x);
247 /// let portal = anchor.portal();
248 /// assert_panic!({
249 /// // Poison anchor.
250 /// let _guard = portal.borrow_mut();
251 /// panic!()
252 /// });
253 ///
254 /// assert_panic!(
255 /// drop(anchor),
256 /// &str,
257 /// "Anchor still in use (at least one portal exists)",
258 /// );
259 /// ```
260 fn drop(&mut self) {
261 unsafe {
262 //SAFETY: Dropping.
263 ManuallyDrop::take(&mut self.reference)
264 }
265 .pipe(Rc::try_unwrap)
266 .unwrap_or_else(|reference| {
267 reference
268 .try_borrow_mut()
269 .unwrap_or_else(|_| {
270 // So at this point we know that something else has taken out a borrow of the poisonable value,
271 // and we know that that borrow will never be released because all the types leading there are `!Send`,
272 // and we also don't know whether that's only used on this one thread because a derived reference could have been sent elsewhere.
273 // Meaning this is the only way to prevent UB here:
274 error!("!Send `RwAnchor` dropped while borrowed from. Deadlocking thread to prevent UB.");
275 let deadlock_mutex = Mutex::new(());
276 let _deadlock_guard = deadlock_mutex.lock().unwrap();
277 let _never = deadlock_mutex.lock();
278 // Congratulations.
279 unreachable!()
280 })
281 .poisoned = true;
282 panic!(ANCHOR_STILL_IN_USE)
283 })
284 .into_inner() // Not fallible.
285 .poisoned
286 .pipe(|poisoned| {
287 if poisoned {
288 panic!(ANCHOR_POISONED)
289 }
290 })
291 }
292}
293
294/// # Safety:
295///
296/// ```rust
297/// # use {assert_deadlock::assert_deadlock, std::time::Duration};
298/// use ref_portals::rc::Anchor;
299///
300/// let mut x = "Scoped".to_owned();
301/// let anchor = Anchor::new(&mut x);
302/// let portal = anchor.portal();
303///
304/// assert_deadlock!(
305/// drop(anchor),
306/// Duration::from_secs(1),
307/// );
308/// ```
309impl<'a, T: ?Sized> UnwindSafe for Anchor<'a, T> where T: RefUnwindSafe {}
310
311/// # Safety:
312///
313/// ```rust
314/// # use assert_panic::assert_panic;
315/// use ref_portals::rc::RwAnchor;
316///
317/// let mut x = "Scoped".to_owned();
318/// let anchor = RwAnchor::new(&mut x);
319/// let portal = anchor.portal();
320///
321/// assert_panic!(
322/// drop(anchor),
323/// &str,
324/// "Anchor still in use (at least one portal exists)",
325/// );
326/// assert_panic!(
327/// { portal.borrow_mut(); },
328/// &str,
329/// "Anchor poisoned",
330/// );
331/// ```
332impl<'a, T: ?Sized> UnwindSafe for RwAnchor<'a, T> where T: RefUnwindSafe {}
333
334/// An `!Send` immutable portal.
335/// Dereference it directly with `*` or `.deref()`.
336#[derive(Debug)]
337#[must_use]
338#[repr(transparent)]
339pub struct Portal<T: ?Sized>(Rc<NonNull<T>>);
340
341/// An `!Send` mutable portal with overlapping immutable borrows.
342/// Acquire a guard by calling `.borrow()` or `.borrow_mut()`.
343#[derive(Debug)]
344#[must_use]
345#[repr(transparent)]
346pub struct RwPortal<T: ?Sized>(Rc<RefCell<Poisonable<NonNull<T>>>>);
347
348impl<T: ?Sized> Portal<T> {
349 /// Creates a weak portal associated with the same anchor as `portal`.
350 /// Dropping an anchor doesn't panic if only weak portals exist.
351 #[inline]
352 pub fn downgrade(portal: &Self) -> WeakPortal<T> {
353 Rc::downgrade(&portal.0).pipe(WeakPortal)
354 }
355}
356
357impl<T: ?Sized> Deref for Portal<T> {
358 type Target = T;
359 #[inline]
360 fn deref(&self) -> &Self::Target {
361 let pointer = self.0.deref();
362 unsafe {
363 //SAFETY: Valid as long as self.0 is.
364 pointer.as_ref()
365 }
366 }
367}
368
369impl<T: ?Sized> Borrow<T> for Portal<T> {
370 #[inline]
371 fn borrow(&self) -> &T {
372 &*self
373 }
374}
375
376impl<T: ?Sized> RwPortal<T> {
377 /// Creates a weak portal associated with the same anchor as this one.
378 /// Dropping an anchor doesn't panic if only weak portals exist.
379 #[inline]
380 pub fn downgrade(&self) -> WeakRwPortal<T> {
381 Rc::downgrade(&self.0).pipe(WeakRwPortal)
382 }
383
384 #[inline]
385 pub fn borrow<'a>(&'a self) -> impl Deref<Target = T> + 'a {
386 let guard = self.0.as_ref().borrow();
387 if guard.poisoned {
388 panic!(ANCHOR_POISONED)
389 }
390 PortalRef(guard)
391 }
392
393 #[inline]
394 pub fn borrow_mut<'a>(&'a self) -> impl DerefMut<Target = T> + 'a {
395 let guard = self.0.as_ref().borrow_mut();
396 if guard.poisoned {
397 panic!(ANCHOR_POISONED)
398 }
399 PortalRefMut(guard)
400 }
401}
402
403impl<T: ?Sized> Clone for Portal<T> {
404 #[inline]
405 fn clone(&self) -> Self {
406 self.0.pipe_ref(Rc::clone).pipe(Self)
407 }
408}
409
410impl<T: ?Sized> Clone for RwPortal<T> {
411 #[inline]
412 fn clone(&self) -> Self {
413 self.0.pipe_ref(Rc::clone).pipe(Self)
414 }
415}
416
417//TODO: Docs, test.
418impl<T: ?Sized> RefUnwindSafe for RwPortal<T> where T: RefUnwindSafe {}
419
420//TODO: Docs, test.
421impl<T: ?Sized> UnwindSafe for RwPortal<T> where T: RefUnwindSafe {}
422
423#[derive(Debug)]
424#[must_use]
425#[repr(transparent)]
426pub struct WeakPortal<T: ?Sized>(Weak<NonNull<T>>);
427
428#[derive(Debug)]
429#[must_use]
430#[repr(transparent)]
431pub struct WeakRwPortal<T: ?Sized>(Weak<RefCell<Poisonable<NonNull<T>>>>);
432
433impl<T: ?Sized> WeakPortal<T> {
434 #[inline]
435 pub fn try_upgrade(&self) -> Option<Portal<T>> {
436 self.0.upgrade().map(Portal)
437 }
438
439 #[inline]
440 pub fn upgrade(&self) -> Portal<T> {
441 self.try_upgrade().expect(ANCHOR_DROPPED)
442 }
443}
444
445impl<T: ?Sized> WeakRwPortal<T> {
446 #[inline]
447 pub fn try_upgrade(&self) -> Option<RwPortal<T>> {
448 self.0.upgrade().map(RwPortal)
449 }
450
451 #[inline]
452 pub fn upgrade(&self) -> RwPortal<T> {
453 self.try_upgrade().expect(ANCHOR_DROPPED)
454 }
455}
456
457impl<T: ?Sized> Clone for WeakPortal<T> {
458 #[inline]
459 fn clone(&self) -> Self {
460 self.0.pipe_ref(Weak::clone).pipe(Self)
461 }
462}
463
464impl<T: ?Sized> Clone for WeakRwPortal<T> {
465 #[inline]
466 fn clone(&self) -> Self {
467 self.0.pipe_ref(Weak::clone).pipe(Self)
468 }
469}
470
471#[repr(transparent)]
472struct PortalRef<'a, T: 'a + ?Sized>(Ref<'a, Poisonable<NonNull<T>>>);
473
474#[repr(transparent)]
475struct PortalRefMut<'a, T: 'a + ?Sized>(RefMut<'a, Poisonable<NonNull<T>>>);
476
477impl<'a, T: ?Sized> Deref for PortalRef<'a, T> {
478 type Target = T;
479 #[inline]
480 fn deref(&self) -> &Self::Target {
481 let pointer = &self.0.deref().pointer;
482 unsafe {
483 //SAFETY: Valid as long as self.0 is. Can't be created from a read-only anchor.
484 pointer.as_ref()
485 }
486 }
487}
488
489impl<'a, T: ?Sized> Deref for PortalRefMut<'a, T> {
490 type Target = T;
491 #[inline]
492 fn deref(&self) -> &Self::Target {
493 let pointer = &self.0.deref().pointer;
494 unsafe {
495 //SAFETY: Valid as long as self.0 is. Can't be created from a read-only anchor.
496 pointer.as_ref()
497 }
498 }
499}
500
501impl<'a, T: ?Sized> DerefMut for PortalRefMut<'a, T> {
502 #[inline]
503 fn deref_mut(&mut self) -> &mut Self::Target {
504 let pointer = &mut self.0.deref_mut().pointer;
505 unsafe {
506 //SAFETY: Valid as long as self.0 is. Can't be created from a read-only anchor.
507 pointer.as_mut()
508 }
509 }
510}
511
512impl<'a, T: ?Sized> Drop for PortalRefMut<'a, T> {
513 #[inline]
514 fn drop(&mut self) {
515 if thread::panicking() {
516 self.0.poisoned = true;
517 }
518 }
519}
520
521#[cfg(test)]
522mod tests {
523 use super::*;
524
525 fn _auto_trait_assertions() {
526 // Anything that necessitates changes in this method is a breaking change.
527 use {assert_impl::assert_impl, core::any::Any};
528
529 assert_impl!(
530 !Send: Anchor<'_, ()>,
531 RwAnchor<'_, ()>,
532 Portal<()>,
533 RwPortal<()>,
534 PortalRef<'_, ()>,
535 PortalRefMut<'_, ()>,
536 );
537
538 assert_impl!(
539 !Sync: Anchor<'_, ()>,
540 RwAnchor<'_, ()>,
541 Portal<()>,
542 RwPortal<()>,
543 PortalRef<'_, ()>,
544 PortalRefMut<'_, ()>,
545 );
546
547 assert_impl!(
548 !UnwindSafe: Anchor<'_, dyn UnwindSafe>,
549 RwAnchor<'_, dyn UnwindSafe>,
550 Portal<dyn UnwindSafe>,
551 RwPortal<dyn UnwindSafe>,
552 );
553 assert_impl!(
554 UnwindSafe: Anchor<'_, dyn RefUnwindSafe>,
555 RwAnchor<'_, dyn RefUnwindSafe>,
556 Portal<dyn RefUnwindSafe>,
557 RwPortal<dyn RefUnwindSafe>,
558 );
559 assert_impl!(!UnwindSafe: PortalRef<'_, ()>, PortalRefMut<'_, ()>);
560
561 assert_impl!(!RefUnwindSafe: RwPortal<dyn UnwindSafe>);
562 assert_impl!(RefUnwindSafe: RwPortal<dyn RefUnwindSafe>);
563 assert_impl!(
564 //TODO: Should any of these by more RefUnwindSafe?
565 !RefUnwindSafe: Anchor<'_, ()>,
566 RwAnchor<'_, ()>,
567 Portal<()>,
568 PortalRef<'_, ()>,
569 PortalRefMut<'_, ()>,
570 );
571
572 assert_impl!(
573 Unpin: Anchor<'_, dyn Any>,
574 RwAnchor<'_, dyn Any>,
575 Portal<dyn Any>,
576 RwPortal<dyn Any>,
577 PortalRef<'_, dyn Any>,
578 PortalRefMut<'_, dyn Any>,
579 )
580 }
581
582 fn _impl_trait_assertions() {
583 use {assert_impl::assert_impl, core::any::Any};
584
585 assert_impl!(
586 Clone: Portal<dyn Any>,
587 RwPortal<dyn Any>,
588 WeakPortal<dyn Any>,
589 WeakRwPortal<dyn Any>,
590 );
591
592 assert_impl!(Deref<Target = dyn Any>: Portal<dyn Any>);
593 assert_impl!(Borrow<dyn Any>: Portal<dyn Any>);
594 }
595 //TODO
596}