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}