1use core::sync::atomic::{AtomicI32, Ordering};
8
9#[repr(C)]
17pub struct RefCounted {
18 ref_count: AtomicI32,
19}
20
21#[cfg(debug_assertions)]
22const PRE_ADOPT_SENTINEL: i32 = 0xC0000000u32 as i32;
23
24impl RefCounted {
25 pub const fn new() -> Self {
30 #[cfg(debug_assertions)]
31 let initial_count = PRE_ADOPT_SENTINEL;
32 #[cfg(not(debug_assertions))]
33 let initial_count = 1;
34
35 RefCounted { ref_count: AtomicI32::new(initial_count) }
36 }
37
38 pub(crate) fn adopt(&self) {
40 #[cfg(debug_assertions)]
41 {
42 let expected = PRE_ADOPT_SENTINEL;
43 let res =
44 self.ref_count.compare_exchange(expected, 1, Ordering::AcqRel, Ordering::Acquire);
45 assert!(res.is_ok(), "Double adopt or adopt on invalid object");
46 }
47 #[cfg(not(debug_assertions))]
48 {
49 self.ref_count.store(1, Ordering::Release);
50 }
51 }
52
53 pub(crate) fn add_ref(&self) {
55 let rc = self.ref_count.fetch_add(1, Ordering::Relaxed);
56 assert!(rc >= 1, "AddRef on un-adopted or destroyed object");
57 }
58
59 #[must_use = "Release must be checked to determine if the object should be deleted"]
61 pub(crate) fn release(&self) -> bool {
62 let rc = self.ref_count.fetch_sub(1, Ordering::Release);
63 assert!(rc >= 1, "Release on un-adopted or destroyed object");
64 if rc == 1 {
65 core::sync::atomic::fence(Ordering::Acquire);
66 return true;
67 }
68 false
69 }
70
71 pub fn ref_count_debug(&self) -> i32 {
73 self.ref_count.load(Ordering::Relaxed)
74 }
75}
76
77pub trait HasRefCount {
81 fn ref_count(&self) -> &RefCounted;
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 #[test]
90 fn test_new() {
91 let rc = RefCounted::new();
92 #[cfg(debug_assertions)]
93 assert_eq!(rc.ref_count_debug(), PRE_ADOPT_SENTINEL);
94 #[cfg(not(debug_assertions))]
95 assert_eq!(rc.ref_count_debug(), 1);
96 }
97
98 #[test]
99 fn test_add_ref() {
100 let rc = RefCounted::new();
101 rc.adopt();
102 rc.add_ref();
103 assert_eq!(rc.ref_count_debug(), 2);
104 }
105
106 #[test]
107 fn test_release() {
108 let rc = RefCounted::new();
109 rc.adopt();
110 assert!(rc.release()); assert_eq!(rc.ref_count_debug(), 0);
112 }
113
114 #[test]
115 #[should_panic(expected = "AddRef on un-adopted or destroyed object")]
116 fn test_add_ref_panic() {
117 let rc = RefCounted::new();
118 rc.adopt();
119 assert!(rc.release()); rc.add_ref(); }
122
123 #[test]
124 #[should_panic(expected = "Release on un-adopted or destroyed object")]
125 fn test_release_panic() {
126 let rc = RefCounted::new();
127 rc.adopt();
128 assert!(rc.release()); let _ = rc.release(); }
131}