Skip to main content

fuchsia_rcu/
rcu_weak.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::rcu_ptr::RcuPtr;
6use crate::rcu_read_scope::RcuReadScope;
7use crate::state_machine::rcu_drop;
8use std::mem::ManuallyDrop;
9use std::sync::{Arc, Weak};
10
11/// An RCU (Read-Copy-Update) wrapper around a [`Weak`] pointer.
12///
13/// The weak pointer can be read and upgraded from multiple threads concurrently without blocking.
14/// When the weak pointer is replaced, reads may continue to see the old weak pointer for some
15/// period of time.
16#[derive(Debug)]
17pub struct RcuWeak<T: Send + Sync + 'static> {
18    ptr: RcuPtr<T>,
19}
20
21impl<T: Send + Sync + 'static> RcuWeak<T> {
22    /// Create a new RCU wrapper around a [`Weak`] pointer.
23    pub fn new(data: Weak<T>) -> Self {
24        Self { ptr: RcuPtr::new(Self::into_ptr(data)) }
25    }
26
27    /// Try to upgrade the wrapped [`Weak`] pointer to an [`Arc`].
28    ///
29    /// Returns [`None`] if the weak pointer has expired and the referenced object was destroyed or
30    /// if the [`RcuWeak`] is in a transient state during drop.
31    pub fn upgrade(&self) -> Option<Arc<T>> {
32        let scope = RcuReadScope::new();
33        let ptr = self.ptr.read(&scope);
34        // SAFETY: We can pass `ptr` to `rcu_ptr_upgrade` because it was obtained from
35        // `Weak::into_raw`.
36        unsafe { crate::subtle::rcu_ptr_upgrade(ptr) }
37    }
38
39    /// Create a new [`Weak`] pointer to the object referenced by the wrapped Weak pointer.
40    pub fn to_weak(&self) -> Weak<T> {
41        self.with_weak(&RcuReadScope::new(), |w| w.clone())
42    }
43
44    /// Check if the wrapped weak pointer points to the same allocation as `other`.
45    pub fn ptr_eq(&self, other: &Weak<T>) -> bool {
46        let scope = RcuReadScope::new();
47        let ptr = self.ptr.read(&scope).as_ptr();
48        ptr as *const T == other.as_ptr()
49    }
50
51    /// Write a new [`Weak`] pointer to the RCU wrapper.
52    ///
53    /// Concurrent readers may continue to see the old weak pointer until the RCU state machine has
54    /// made sufficient progress to ensure that no concurrent readers are holding read guards.
55    pub fn update(&self, data: Weak<T>) {
56        let ptr = Self::into_ptr(data);
57        // SAFETY: We can pass `Self::into_ptr` to `Self::replace`.
58        unsafe { self.replace(ptr) };
59    }
60
61    /// Gets the number of strong (Arc) pointers pointing to the allocation.
62    pub fn strong_count(&self, scope: &RcuReadScope) -> usize {
63        self.with_weak(scope, |w| w.strong_count())
64    }
65
66    /// Extract the raw pointer from a `Weak` pointer.
67    ///
68    /// The caller is responsible for ensuring that the pointer returned by this function is
69    /// eventually converted back into a `Weak` pointer to balance its weak reference count.
70    fn into_ptr(weak: Weak<T>) -> *mut T {
71        Weak::into_raw(weak) as *mut T
72    }
73
74    /// Replace the Weak pointer in the RCU wrapper with a new pointer.
75    ///
76    /// # Safety
77    ///
78    /// The caller must have obtained the pointer from `Self::into_ptr` or from
79    /// `std::ptr::null_mut`.
80    unsafe fn replace(&self, ptr: *mut T) {
81        let old_ptr = self.ptr.replace(ptr);
82        if !old_ptr.is_null() {
83            let weak = unsafe { Weak::from_raw(old_ptr) };
84            rcu_drop(weak);
85        }
86    }
87
88    /// Executes a closure with a reference to the wrapped [`Weak`] pointer.
89    ///
90    /// The weak pointer is temporarily reconstructed within an RCU read scope
91    /// using [`ManuallyDrop`] to avoid taking ownership of the pointer. If the
92    /// internal pointer is null, a reference to an empty [`Weak`] is provided.
93    fn with_weak<A>(&self, scope: &RcuReadScope, f: impl FnOnce(&Weak<T>) -> A) -> A {
94        let ptr = self.ptr.read(scope).as_ptr();
95        if ptr.is_null() {
96            f(&Weak::new())
97        } else {
98            // SAFETY: The RCU state machine ensures that the pointer is valid for reads until the
99            // `RcuReadScope` is dropped. Temporarily reconstructs the `Weak` pointer using
100            // `ManuallyDrop` and runs `f` on it. This prevents the `Weak` from taking ownership of
101            // the wrapped pointer.
102            unsafe {
103                let weak = ManuallyDrop::new(Weak::from_raw(ptr));
104                f(&*weak)
105            }
106        }
107    }
108}
109
110impl<T: Send + Sync + 'static> Drop for RcuWeak<T> {
111    fn drop(&mut self) {
112        // SAFETY: We can pass `std::ptr::null_mut`.
113        unsafe { self.replace(std::ptr::null_mut()) };
114    }
115}
116
117impl<T: Send + Sync + 'static> Clone for RcuWeak<T> {
118    fn clone(&self) -> Self {
119        Self::new(self.to_weak())
120    }
121}
122
123impl<T: Send + Sync + 'static> From<Weak<T>> for RcuWeak<T> {
124    fn from(weak: Weak<T>) -> Self {
125        Self::new(weak)
126    }
127}
128
129impl<T: Send + Sync + 'static> Default for RcuWeak<T> {
130    fn default() -> Self {
131        Self::new(Weak::new())
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138    use crate::state_machine::rcu_run_callbacks;
139    use std::sync::atomic::{AtomicUsize, Ordering};
140
141    struct DropCounter {
142        drops: Arc<AtomicUsize>,
143    }
144
145    impl DropCounter {
146        pub fn new() -> Arc<Self> {
147            Arc::new(Self { drops: Arc::new(AtomicUsize::new(0)) })
148        }
149    }
150
151    impl Drop for DropCounter {
152        fn drop(&mut self) {
153            self.drops.fetch_add(1, Ordering::Relaxed);
154        }
155    }
156
157    #[test]
158    fn test_rcu_weak_upgrade() {
159        let object = DropCounter::new();
160        let drops = object.drops.clone();
161
162        let weak = Arc::downgrade(&object);
163        let rcu_weak = RcuWeak::from(weak);
164
165        assert!(rcu_weak.upgrade().is_some());
166        assert_eq!(drops.load(Ordering::Relaxed), 0);
167
168        drop(object);
169        assert!(rcu_weak.upgrade().is_none());
170        assert_eq!(drops.load(Ordering::Relaxed), 1);
171    }
172
173    #[test]
174    fn test_rcu_weak_update() {
175        let object1 = DropCounter::new();
176        let drops1 = object1.drops.clone();
177        let rcu_weak = RcuWeak::from(Arc::downgrade(&object1));
178
179        let object2 = DropCounter::new();
180        let drops2 = object2.drops.clone();
181
182        rcu_weak.update(Arc::downgrade(&object2));
183        assert!(rcu_weak.upgrade().map_or(false, |obj| Arc::ptr_eq(&obj, &object2)));
184
185        // Old weak should be dropped eventually.
186        // This doesn't affect the object1 lifetime, but decrements weak count.
187        rcu_run_callbacks();
188
189        drop(object1);
190        assert_eq!(drops1.load(Ordering::Relaxed), 1);
191
192        drop(object2);
193        assert_eq!(drops2.load(Ordering::Relaxed), 1);
194    }
195
196    #[test]
197    fn test_rcu_weak_ptr_eq() {
198        let object1 = DropCounter::new();
199        let weak1 = Arc::downgrade(&object1);
200        let rcu_weak = RcuWeak::from(weak1.clone());
201
202        let object2 = DropCounter::new();
203        let weak2 = Arc::downgrade(&object2);
204
205        assert!(rcu_weak.ptr_eq(&weak1));
206        assert!(!rcu_weak.ptr_eq(&weak2));
207
208        rcu_weak.update(weak2.clone());
209        assert!(!rcu_weak.ptr_eq(&weak1));
210        assert!(rcu_weak.ptr_eq(&weak2));
211    }
212
213    #[test]
214    fn test_rcu_weak_strong_count() {
215        let scope = RcuReadScope::new();
216        let rcu_weak = RcuWeak::<DropCounter>::default();
217        assert_eq!(rcu_weak.strong_count(&scope), 0);
218
219        let object = DropCounter::new();
220        rcu_weak.update(Arc::downgrade(&object));
221        assert_eq!(rcu_weak.strong_count(&scope), 1);
222
223        drop(object);
224        assert_eq!(rcu_weak.strong_count(&scope), 0);
225    }
226}