odem_rs_core/ptr/irc.rs
1//! The `irc` module provides an intrusive reference-counting smart pointer,
2//! `Irc`, along with traits for implementing intrusively counted types.
3
4use super::LeasedMut;
5use core::{
6 any::{Any, TypeId},
7 borrow::Borrow,
8 cell::Cell,
9 fmt,
10 ops::{Deref, DerefMut},
11 panic::{Location, RefUnwindSafe, UnwindSafe},
12 pin::Pin,
13 ptr::NonNull,
14 sync::atomic::{AtomicUsize, Ordering},
15};
16
17/* ******************************************************************* Traits */
18
19/// Used to do an inexpensive reference-to-[Irc] conversion.
20pub trait AsIrc<T: ?Sized + IntrusivelyCounted> {
21 /// Converts this type into an [owned reference] of the (usually inferred)
22 /// input type.
23 ///
24 /// [owned reference]: Irc
25 fn as_irc(&self) -> Irc<T>;
26}
27
28/// A trait that can be implemented by types that track references using an
29/// internal reference counter.
30///
31/// # Safety
32/// The implementation must guarantee that the `IrcBox` is owned by the
33/// implementor, i.e., will be dropped together with `Self`. This usually means
34/// that the `IrcBox` is embedded in the type implementing this trait.
35pub unsafe trait IntrusivelyCounted {
36 /// Returns a reference to the embedded object implementing [IrcBoxed].
37 /// It is used by [Irc] to opaquely manipulate the reference counter.
38 fn irc_box(&self) -> &IrcBox<dyn IrcBoxed>;
39}
40
41/// A trait allowing an [`Irc`] to increment and decrement the reference count
42/// as well as recycling an unreachable object.
43///
44/// # Safety
45/// The implementor has to ensure that the `acquire` and `release` methods
46/// provided by the trait correctly keep track of the inner reference counts
47/// in order for the `Irc` derived from them to be sound.
48pub unsafe trait IrcBoxed {
49 /// Returns the number of `Irc` currently referencing the `IrcBox`.
50 fn ref_count(&self) -> usize;
51
52 /// Increments the reference counter.
53 fn acquire(&self, _: Private);
54
55 /// Decrements the internal reference counter.
56 fn release(&self, _: Private);
57
58 /// Returns a function pointer that is responsible for recycling if the
59 /// reference counter reaches zero.
60 ///
61 /// The reason for this convoluted mechanism is that the implementing
62 /// [`IrcBox`] may recover the original type and reclaim it using an
63 /// exclusive reference. This cannot be done in the function directly
64 /// because a shared reference to the `IrcBox` exists, precluding the
65 /// existence of mutable references to objects that include the `IrcBox`
66 /// itself.
67 #[inline(always)]
68 fn reclaim(&self, _: Private) -> Option<fn(NonNull<dyn IrcBoxed>)> {
69 None
70 }
71}
72
73/* ************************************************************ Irc Structure */
74
75/// The intrusive version of a [Rc] without support for [Weak] pointer. 'Irc'
76/// stands for 'Intrusively Reference Counted'.
77///
78/// [Rc]: std::rc::Rc
79/// [Weak]: std::rc::Weak
80pub struct Irc<T: ?Sized + IntrusivelyCounted>(NonNull<T>);
81
82impl<T: ?Sized + IntrusivelyCounted> Irc<T> {
83 /// Creates a new intrusively counted pointer from a [`Lease`] value.
84 ///
85 /// Because the value is borrowed exclusively, we can be sure that it
86 /// currently is the only reference to the value. Because of the invariant
87 /// lifetime `'p` associated with the [`Lease`], we can be sure that it
88 /// can *never* be borrowed again.
89 /// Finally, because the value is [pinned] and `T` implements
90 /// [`IntrusivelyCounted`], we can be sure that its inner [`IrcBox`] will
91 /// either be dropped or remain valid indefinitely.
92 ///
93 /// [`Lease`]: super::Lease
94 /// [pinned]: core::pin
95 pub fn new(value: Pin<LeasedMut<'_, T>>) -> Self {
96 // increase the reference count to one
97 value.irc_box().count.acquire(Private(()));
98
99 // strip away the lifetime requirements
100 Irc(NonNull::from(&mut **unsafe {
101 Pin::into_inner_unchecked(value)
102 }))
103
104 // dangling pointers are prevented by the Drop impl of `IrcBox`
105 }
106
107 /// Unsafely creates a new intrusively counted pointer from a pinned value.
108 ///
109 /// Because the value is borrowed exclusively, we can be sure that it
110 /// currently is the only reference to the value. Because the value is
111 /// [pinned](core::pin) and `T` implements [`IntrusivelyCounted`], we can be
112 /// sure that its inner [`IrcBox`] will either be dropped or remain valid
113 /// indefinitely.
114 ///
115 /// # Safety
116 /// We cannot be sure that the caller doesn't hold an external reference
117 /// that the value is borrowed from, that will become active later. This is
118 /// a problem because we allow mutable borrows in `Irc::drop` based on the
119 /// reference count and also through `Irc::as_pin_mut`.
120 ///
121 /// The caller is responsible that the pinned value is not duplicated and
122 /// used after this method has been called. Keeping a reference - mutable or
123 /// not - results in **undefined behavior**.
124 pub unsafe fn new_unchecked(value: Pin<&mut T>) -> Self {
125 // increase the reference count to one
126 value.irc_box().acquire(Private(()));
127
128 // strip away the lifetime requirements
129 Irc(NonNull::from(unsafe { Pin::into_inner_unchecked(value) }))
130
131 // dangling pointers are prevented by the Drop impl of `IrcBox`
132 }
133
134 /// Returns a pinned reference to the inner value.
135 pub fn get_pin(&self) -> Pin<&T> {
136 // SAFETY: the pointer was originally pinned, so reconstructing this
137 // constraint here is safe
138 unsafe { Pin::new_unchecked(self.0.as_ref()) }
139 }
140
141 /// Returns a pinned mutable reference to the inner value if there is only
142 /// one `Irc` in use right now.
143 pub fn get_pin_mut(&mut self) -> Option<Pin<&mut T>> {
144 // SAFETY: the constructors originally required a pinned mutable
145 // reference that left no reference with the caller; therefore restoring
146 // it if we're sure that there is only this `Irc` is safe
147 (self.irc_box().ref_count() == 1).then(|| unsafe { Pin::new_unchecked(self.0.as_mut()) })
148 }
149
150 /// Returns the inner (raw) pointer of the `Irc`.
151 pub fn as_raw(this: &Self) -> NonNull<T> {
152 this.0
153 }
154
155 /// Strips the outer structure from the `Irc`, revealing the inner
156 /// unprotected reference. This operation does not decrease the reference
157 /// counter and should be used in tandem with [`from_raw`] to restore the
158 /// smart pointer at a later time.
159 ///
160 /// [`from_raw`]: Self::from_raw
161 pub fn into_raw(this: Self) -> NonNull<T> {
162 // copy the inner pointer
163 let inner = this.0;
164
165 // forget calling the destructor
166 core::mem::forget(this);
167
168 // return the pointer
169 inner
170 }
171
172 /// Reconstructs the `Irc` from an unprotected reference.
173 ///
174 /// # Safety
175 /// The associated function is marked as unsafe because it is the caller's
176 /// responsibility to ensure that this reference has originally been the result
177 /// of a call to [`into_raw`].
178 ///
179 /// [`into_raw`]: Self::into_raw
180 pub unsafe fn from_raw(inner: NonNull<T>) -> Self {
181 Irc(inner)
182 }
183
184 /// Allows the (limited) projection of a composite [Irc] into an Irc of one
185 /// of its member variables, provided that member variable contains the same
186 /// reference counter.
187 pub fn map<F, R>(this: Self, f: F) -> Irc<R>
188 where
189 F: FnOnce(&T) -> &R,
190 R: ?Sized + IntrusivelyCounted,
191 {
192 // store the pointer to the inner IrcBox
193 let inner = this.irc_box();
194
195 // perform the conversion
196 let value = f(&*this);
197
198 // assert that the IrcBox hasn't changed due to the projection;
199 // this should be optimized away by the compiler
200 assert!(
201 core::ptr::addr_eq(inner, value.irc_box()),
202 "expected the mapping to yield an Irc with the same IrcBox"
203 );
204
205 // construct the new Irc, taking the provenance from `this`
206 let res = Irc(with_provenance(NonNull::from(value), this.0));
207
208 // forget the original Irc
209 core::mem::forget(this);
210
211 res
212 }
213
214 /// Converts the `Irc` into type `V` if it is of this type, or returns the
215 /// old `Irc` if it isn't.
216 pub fn downcast<V>(self) -> Result<Irc<V>, Irc<T>>
217 where
218 T: Any,
219 V: 'static + IntrusivelyCounted,
220 {
221 if (*self).type_id() == TypeId::of::<V>() {
222 let res = Irc(self.0.cast::<V>());
223 core::mem::forget(self);
224 Ok(res)
225 } else {
226 Err(self)
227 }
228 }
229}
230
231impl<T: ?Sized + IntrusivelyCounted> Deref for Irc<T> {
232 type Target = T;
233
234 fn deref(&self) -> &Self::Target {
235 unsafe { self.0.as_ref() }
236 }
237}
238
239impl<T: ?Sized + IntrusivelyCounted> Clone for Irc<T> {
240 fn clone(&self) -> Self {
241 self.irc_box().acquire(Private(()));
242 Irc(self.0)
243 }
244}
245
246impl<T: ?Sized + IntrusivelyCounted> Borrow<T> for Irc<T> {
247 fn borrow(&self) -> &T {
248 self
249 }
250}
251
252impl<T: ?Sized + IntrusivelyCounted> Drop for Irc<T> {
253 fn drop(&mut self) {
254 let irc_box = self.irc_box();
255 irc_box.release(Private(()));
256
257 if irc_box.ref_count() == 0 {
258 let Some(reclaim) = irc_box.reclaim(Private(())) else {
259 return;
260 };
261
262 let irc_box = with_provenance(NonNull::from(&irc_box.count), self.0);
263 reclaim(irc_box);
264 }
265 }
266}
267
268impl<T: ?Sized + IntrusivelyCounted> Unpin for Irc<T> {}
269
270impl<T: ?Sized + IntrusivelyCounted + fmt::Debug> fmt::Debug for Irc<T> {
271 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
272 unsafe { self.0.as_ref() }.fmt(f)
273 }
274}
275
276impl<T: ?Sized + IntrusivelyCounted + fmt::Display> fmt::Display for Irc<T> {
277 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
278 unsafe { self.0.as_ref() }.fmt(f)
279 }
280}
281
282// T: Sync is enough for Irc<T> to be Send & Sync since we're moving references
283unsafe impl<T: Sync + ?Sized + IntrusivelyCounted> Send for Irc<T> {}
284
285unsafe impl<T: Sync + ?Sized + IntrusivelyCounted> Sync for Irc<T> {}
286
287impl<T: RefUnwindSafe + ?Sized + IntrusivelyCounted> UnwindSafe for Irc<T> {}
288
289/* ****************************************** Marker Type for Private Details */
290
291/// Marker type to enable generating private functions in a public trait
292/// interface.
293pub struct Private(());
294
295/* ****************************************************** Provenance Transfer */
296
297/// Transfers the provenance from one non-null pointer to another.
298///
299/// This function assigns the provenance of the `provenance` pointer to the
300/// `target` pointer. Provenance refers to the origin or ownership context of a
301/// pointer, which is crucial for maintaining memory safety and correctness in
302/// low-level operations. See [here] for Rust's strict provenance.
303///
304/// # Safety
305///
306/// This function performs raw pointer manipulation and assumes that the layout
307/// of fat pointers with metadata remains consistent.
308///
309/// [here]: https://doc.rust-lang.org/core/ptr/index.html#strict-provenance
310fn with_provenance<S: ?Sized, T: ?Sized>(
311 mut target: NonNull<T>,
312 provenance: NonNull<S>,
313) -> NonNull<T> {
314 // Create a thin pointer by casting `provenance` to `u8` and setting its
315 // address to that of `target`. This combines the address of `target` with
316 // the provenance of `provenance`.
317 let target_thin_ptr = provenance.cast::<u8>().with_addr(target.addr());
318
319 // Get a mutable reference to the `target` pointer and cast it to a
320 // pointer to `NonNull<u8>`. This allows direct manipulation of the thin
321 // pointer portion of the potentially fat `NonNull<T>`.
322 let ptr_to_fat_ptr = NonNull::from(&mut target).cast::<NonNull<u8>>();
323
324 // Overwrite the thin pointer part of the fat `target` pointer with the new
325 // thin pointer that carries the desired provenance. This operation
326 // preserves the original address while updating its provenance.
327 //
328 // SAFETY: This is safe provided that the layout of fat pointers with
329 // metadata does not change.
330 // TODO: use with_metadata_of() once stable
331 unsafe {
332 ptr_to_fat_ptr.write(target_thin_ptr);
333 }
334
335 // Return the updated `target` pointer with the new provenance.
336 target
337}
338
339/* ********************************************************* IrcBox Structure */
340
341/// Type that has to be stored inside a structure to allow creating [`Irc`] to
342/// it.
343#[derive(Debug)]
344pub struct IrcBox<T: ?Sized + IrcBoxed = Cell<usize>> {
345 /// The location in the code.
346 loc: &'static Location<'static>,
347 /// The inner value containing the reference counters.
348 count: T,
349}
350
351impl<T: IrcBoxed> IrcBox<T> {
352 /// Creates a new `IrcBox` with the inner value.
353 #[track_caller]
354 pub const fn new(count: T) -> Self {
355 IrcBox::with_location(count, Location::caller())
356 }
357
358 /// Creates a new `IrcBox` with a specific [Location].
359 pub const fn with_location(count: T, loc: &'static Location<'static>) -> Self {
360 IrcBox { loc, count }
361 }
362
363 /// Returns [`Location`]-information related to the creation of the `Irc`.
364 pub const fn location(this: &Self) -> &'static Location<'static> {
365 this.loc
366 }
367}
368
369impl<T: IrcBoxed + Default> Default for IrcBox<T> {
370 #[track_caller]
371 fn default() -> Self {
372 Self::new(T::default())
373 }
374}
375
376impl<T: ?Sized + IrcBoxed> Deref for IrcBox<T> {
377 type Target = T;
378
379 fn deref(&self) -> &Self::Target {
380 &self.count
381 }
382}
383
384impl<T: ?Sized + IrcBoxed> DerefMut for IrcBox<T> {
385 fn deref_mut(&mut self) -> &mut Self::Target {
386 &mut self.count
387 }
388}
389
390impl<T: ?Sized + IrcBoxed> Drop for IrcBox<T> {
391 fn drop(&mut self) {
392 // declare a function that cannot unwind when called
393 extern "C" fn abort(loc: &Location<'static>, n: usize) -> ! {
394 panic!(
395 "dropping the value created at '{}' leaves {} reference(s) dangling",
396 loc, n,
397 )
398 }
399
400 // ensure that no references point to this instance
401 match self.count.ref_count() {
402 0 => {}
403 // this panic leads to an immediate abort which is necessary
404 // because running all the destructors in the stack frames above
405 // will perform a ton of illegal memory accesses
406 n => abort(self.loc, n),
407 }
408 }
409}
410
411/* ****************************************************** Non-Thread-Safe Box */
412
413// SAFETY: The reference counter is changed and returned accordingly.
414unsafe impl IrcBoxed for Cell<usize> {
415 #[inline(always)]
416 fn ref_count(&self) -> usize {
417 self.get()
418 }
419
420 #[inline(always)]
421 fn acquire(&self, _: Private) {
422 self.set(self.get() + 1);
423 }
424
425 #[inline(always)]
426 fn release(&self, _: Private) {
427 self.set(self.get() - 1);
428 }
429}
430
431/* ********************************************************** Thread-Safe Box */
432
433// SAFETY: The reference counter is changed and returned accordingly.
434unsafe impl IrcBoxed for AtomicUsize {
435 #[inline(always)]
436 fn ref_count(&self) -> usize {
437 self.load(Ordering::Relaxed)
438 }
439
440 #[inline(always)]
441 fn acquire(&self, _: Private) {
442 self.fetch_add(1, Ordering::Relaxed);
443 }
444
445 #[inline(always)]
446 fn release(&self, _: Private) {
447 self.fetch_sub(1, Ordering::Relaxed);
448 }
449}
450
451/* *************************************************************** Miri Tests */
452
453// #[cfg(all(test, miri))]
454#[cfg(test)]
455mod tests {
456 use super::*;
457 use crate::ptr::Lease;
458 use core::pin::pin;
459
460 /// Tests the correct transfer of provenance during mapping operations, so
461 /// that gaining mutable access to the inner `IrcBox` is still defined when
462 /// the innermost `Irc` projection is dropped.
463 #[test]
464 fn provenance_transfer() {
465 #[derive(Default)]
466 struct Outer {
467 inner: Inner,
468 }
469
470 #[derive(Default)]
471 struct Inner {
472 irc_box: IrcBox<InnerBox>,
473 }
474
475 #[derive(Default)]
476 struct InnerBox {
477 rc: Cell<usize>,
478 term: bool,
479 }
480
481 unsafe impl IrcBoxed for InnerBox {
482 fn ref_count(&self) -> usize {
483 self.rc.get()
484 }
485
486 fn acquire(&self, _: Private) {
487 self.rc.set(self.rc.get() + 1);
488 }
489
490 fn release(&self, _: Private) {
491 self.rc.set(self.rc.get() - 1);
492 }
493
494 fn reclaim(&self, _: Private) -> Option<fn(NonNull<dyn IrcBoxed>)> {
495 // Return the function that performs the actual reclamation
496 Some(|this| unsafe {
497 // This mutable access is what requires correct provenance.
498 // If Irc::map didn't transfer provenance correctly, Miri
499 // might flag this as UB.
500 this.cast::<Self>().as_mut().term = true;
501 })
502 }
503 }
504
505 unsafe impl IntrusivelyCounted for Outer {
506 fn irc_box(&self) -> &IrcBox<dyn IrcBoxed> {
507 &self.inner.irc_box
508 }
509 }
510
511 unsafe impl IntrusivelyCounted for Inner {
512 fn irc_box(&self) -> &IrcBox<dyn IrcBoxed> {
513 &self.irc_box
514 }
515 }
516
517 // 1. Create the data on the stack.
518 let outer: Pin<&mut Lease<'_, Outer>> = pin!(Lease::new(Outer::default()));
519
520 // 2. Create an Irc pointer to the outer structure.
521 let irc1: Irc<Outer> = Irc::new(outer);
522
523 // 3. Project the Irc<Outer> to an Irc<Inner>.
524 let irc2: Irc<Inner> = Irc::map(irc1, |outer| &outer.inner);
525 assert_eq!(irc2.irc_box.ref_count(), 1);
526
527 // 4. Drop the Irc<Inner>. This should be the last reference,
528 // triggering the release and reclamation logic.
529 drop(irc2);
530 }
531
532 /// Tests the correctness of provenance-transfer for an inner `IrcBox`
533 /// accessing the outer structure upon reclaim.
534 #[test]
535 fn self_reference() {
536 #[derive(Default)]
537 struct Outer {
538 inner: Inner,
539 done: bool,
540 }
541
542 #[derive(Default)]
543 struct Inner {
544 irc_box: IrcBox<InnerBox>,
545 }
546
547 #[derive(Default)]
548 struct InnerBox {
549 rc: Cell<usize>,
550 outer_ref: Cell<Option<NonNull<Outer>>>,
551 }
552
553 unsafe impl IrcBoxed for InnerBox {
554 fn ref_count(&self) -> usize {
555 self.rc.get()
556 }
557
558 fn acquire(&self, _: Private) {
559 self.rc.set(self.rc.get() + 1);
560 }
561
562 fn release(&self, _: Private) {
563 self.rc.set(self.rc.get() - 1);
564 }
565
566 fn reclaim(&self, _: Private) -> Option<fn(NonNull<dyn IrcBoxed>)> {
567 Some(|this| unsafe {
568 let mut outer_ref = this
569 .cast::<Self>()
570 .as_ref()
571 .outer_ref
572 .get()
573 .expect("self-reference initialized");
574 outer_ref.as_mut().done = true;
575 })
576 }
577 }
578
579 unsafe impl IntrusivelyCounted for Outer {
580 fn irc_box(&self) -> &IrcBox<dyn IrcBoxed> {
581 &self.inner.irc_box
582 }
583 }
584
585 unsafe impl IntrusivelyCounted for Inner {
586 fn irc_box(&self) -> &IrcBox<dyn IrcBoxed> {
587 &self.irc_box
588 }
589 }
590
591 // Create a composite structure with a member that includes an IrcBox.
592 let outer = pin!(Lease::new(Outer::default()));
593
594 // Create an Irc-pointer to that composite structure.
595 let irc1 = Irc::new(outer);
596
597 // Derive a reference to the outmost instance and store it in the IrcBox.
598 let outer_ref = Irc::as_raw(&irc1);
599 irc1.inner.irc_box.outer_ref.set(Some(outer_ref));
600
601 // Project the Irc onto the inner one, dropping the outer reference.
602 let irc2 = Irc::map(irc1, |inner| &inner.inner);
603
604 // Now drop the inner reference, leading to mut access of Outer.
605 drop(irc2);
606 }
607
608 /// Tests the correctness of provenance transfer for access to an `Outer`
609 /// structure upon reclaim.
610 #[test]
611 fn self_reference2() {
612 #[derive(Default)]
613 struct Outer {
614 inner: Inner,
615 done: bool,
616 }
617
618 #[derive(Default)]
619 struct Inner {
620 irc_box: IrcBox<InnerBox>,
621 }
622
623 #[derive(Default)]
624 struct InnerBox {
625 ref_count: Cell<usize>,
626 outer_ref: Cell<Option<NonNull<Outer>>>,
627 }
628
629 unsafe impl IrcBoxed for InnerBox {
630 fn ref_count(&self) -> usize {
631 self.ref_count.get()
632 }
633
634 fn acquire(&self, _: Private) {
635 self.ref_count.set(self.ref_count.get() + 1);
636 }
637
638 fn release(&self, _: Private) {
639 self.ref_count.set(self.ref_count.get() - 1);
640 }
641
642 fn reclaim(&self, _: Private) -> Option<fn(NonNull<dyn IrcBoxed>)> {
643 Some(|this| unsafe {
644 let mut outer_ref = this
645 .cast::<Self>()
646 .as_ref()
647 .outer_ref
648 .get()
649 .expect("self-reference initialized");
650 outer_ref.as_mut().done = true;
651 })
652 }
653 }
654
655 unsafe impl IntrusivelyCounted for Outer {
656 fn irc_box(&self) -> &IrcBox<dyn IrcBoxed> {
657 &self.inner.irc_box
658 }
659 }
660
661 unsafe impl IntrusivelyCounted for Inner {
662 fn irc_box(&self) -> &IrcBox<dyn IrcBoxed> {
663 &self.irc_box
664 }
665 }
666
667 // Create a composite structure with a member that includes an IrcBox.
668 let mut outer = pin!(Lease::new(Outer::default()));
669
670 // Derive a reference to the outmost instance.
671 let outer_ref = NonNull::from(unsafe { outer.as_mut().project().get_unchecked_mut() });
672
673 // Create an Irc-pointer to that composite structure.
674 let irc1 = Irc::new(outer);
675
676 // Project the Irc onto the inner one, dropping the outer reference.
677 let irc2 = Irc::map(irc1, |inner| &inner.inner);
678 let outer_ref = with_provenance(outer_ref, Irc::as_raw(&irc2));
679
680 // Store it in the IrcBox.
681 irc2.irc_box.outer_ref.set(Some(outer_ref));
682
683 // Now drop the inner reference, leading to mut access of Outer.
684 drop(irc2);
685 }
686}