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        self.to_weak().upgrade()
33    }
34
35    /// Create a new [`Weak`] pointer to the object referenced by the wrapped Weak pointer.
36    pub fn to_weak(&self) -> Weak<T> {
37        let scope = RcuReadScope::new();
38        let ptr = self.ptr.read(&scope).as_ptr();
39        if ptr.is_null() {
40            Weak::new()
41        } else {
42            // SAFETY: The RCU state machine ensures that the pointer is valid for reads until we
43            // drop the `RcuReadScope`. We temporarily reconstruct the `Weak` pointer using
44            // `ManuallyDrop` and clone it to increment the weak reference count safely. This
45            // prevents the `Weak` from taking ownership of the wrapped pointer.
46            unsafe {
47                let weak = ManuallyDrop::new(Weak::from_raw(ptr));
48                (*weak).clone()
49            }
50        }
51    }
52
53    /// Check if the wrapped weak pointer points to the same allocation as `other`.
54    pub fn ptr_eq(&self, other: &Weak<T>) -> bool {
55        let scope = RcuReadScope::new();
56        let ptr = self.ptr.read(&scope).as_ptr();
57        ptr as *const T == other.as_ptr()
58    }
59
60    /// Write a new [`Weak`] pointer to the RCU wrapper.
61    ///
62    /// Concurrent readers may continue to see the old weak pointer until the RCU state machine has
63    /// made sufficient progress to ensure that no concurrent readers are holding read guards.
64    pub fn update(&self, data: Weak<T>) {
65        let ptr = Self::into_ptr(data);
66        // SAFETY: We can pass `Self::into_ptr` to `Self::replace`.
67        unsafe { self.replace(ptr) };
68    }
69
70    /// Extract the raw pointer from a `Weak` pointer.
71    ///
72    /// The caller is responsible for ensuring that the pointer returned by this function is
73    /// eventually converted back into a `Weak` pointer to balance its weak reference count.
74    fn into_ptr(weak: Weak<T>) -> *mut T {
75        Weak::into_raw(weak) as *mut T
76    }
77
78    /// Replace the Weak pointer in the RCU wrapper with a new pointer.
79    ///
80    /// # Safety
81    ///
82    /// The caller must have obtained the pointer from `Self::into_ptr` or from
83    /// `std::ptr::null_mut`.
84    unsafe fn replace(&self, ptr: *mut T) {
85        let old_ptr = self.ptr.replace(ptr);
86        if !old_ptr.is_null() {
87            let weak = unsafe { Weak::from_raw(old_ptr) };
88            rcu_drop(weak);
89        }
90    }
91}
92
93impl<T: Send + Sync + 'static> Drop for RcuWeak<T> {
94    fn drop(&mut self) {
95        // SAFETY: We can pass `std::ptr::null_mut`.
96        unsafe { self.replace(std::ptr::null_mut()) };
97    }
98}
99
100impl<T: Send + Sync + 'static> Clone for RcuWeak<T> {
101    fn clone(&self) -> Self {
102        Self::new(self.to_weak())
103    }
104}
105
106impl<T: Send + Sync + 'static> From<Weak<T>> for RcuWeak<T> {
107    fn from(weak: Weak<T>) -> Self {
108        Self::new(weak)
109    }
110}
111
112impl<T: Send + Sync + 'static> Default for RcuWeak<T> {
113    fn default() -> Self {
114        Self::new(Weak::new())
115    }
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121    use crate::state_machine::rcu_synchronize;
122    use std::sync::atomic::{AtomicUsize, Ordering};
123
124    struct DropCounter {
125        drops: Arc<AtomicUsize>,
126    }
127
128    impl DropCounter {
129        pub fn new() -> Arc<Self> {
130            Arc::new(Self { drops: Arc::new(AtomicUsize::new(0)) })
131        }
132    }
133
134    impl Drop for DropCounter {
135        fn drop(&mut self) {
136            self.drops.fetch_add(1, Ordering::Relaxed);
137        }
138    }
139
140    #[test]
141    fn test_rcu_weak_upgrade() {
142        let object = DropCounter::new();
143        let drops = object.drops.clone();
144
145        let weak = Arc::downgrade(&object);
146        let rcu_weak = RcuWeak::from(weak);
147
148        assert!(rcu_weak.upgrade().is_some());
149        assert_eq!(drops.load(Ordering::Relaxed), 0);
150
151        drop(object);
152        assert!(rcu_weak.upgrade().is_none());
153        assert_eq!(drops.load(Ordering::Relaxed), 1);
154    }
155
156    #[test]
157    fn test_rcu_weak_update() {
158        let object1 = DropCounter::new();
159        let drops1 = object1.drops.clone();
160        let rcu_weak = RcuWeak::from(Arc::downgrade(&object1));
161
162        let object2 = DropCounter::new();
163        let drops2 = object2.drops.clone();
164
165        rcu_weak.update(Arc::downgrade(&object2));
166        assert!(rcu_weak.upgrade().map_or(false, |obj| Arc::ptr_eq(&obj, &object2)));
167
168        // Old weak should be dropped eventually.
169        // This doesn't affect the object1 lifetime, but decrements weak count.
170        rcu_synchronize();
171
172        drop(object1);
173        assert_eq!(drops1.load(Ordering::Relaxed), 1);
174
175        drop(object2);
176        assert_eq!(drops2.load(Ordering::Relaxed), 1);
177    }
178
179    #[test]
180    fn test_rcu_weak_ptr_eq() {
181        let object1 = DropCounter::new();
182        let weak1 = Arc::downgrade(&object1);
183        let rcu_weak = RcuWeak::from(weak1.clone());
184
185        let object2 = DropCounter::new();
186        let weak2 = Arc::downgrade(&object2);
187
188        assert!(rcu_weak.ptr_eq(&weak1));
189        assert!(!rcu_weak.ptr_eq(&weak2));
190
191        rcu_weak.update(weak2.clone());
192        assert!(!rcu_weak.ptr_eq(&weak1));
193        assert!(rcu_weak.ptr_eq(&weak2));
194    }
195}