7
7
//! underlying object when it reaches zero. It is also safe to use concurrently from multiple
8
8
//! threads.
9
9
//!
10
- //! It is different from the standard library's [`Arc`] in two ways: it does not support weak
11
- //! references, which allows it to be smaller -- a single pointer-sized integer; it allows users to
12
- //! safely increment the reference count from a single reference to the underlying object.
10
+ //! It is different from the standard library's [`Arc`] in at least three ways:
11
+ //! 1. It does not support weak references, which allows it to be smaller: a single pointer-sized
12
+ //! integer;
13
+ //! 2. It allows users to safely increment the reference count from a single reference to the
14
+ //! underlying object. That is, go from `&T` to [`Ref<T>`];
15
+ //! 3. It saturates the reference count instead of aborting when it goes over a threshold.
13
16
//!
14
17
//! [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html
15
18
16
- use crate :: Result ;
19
+ use crate :: { bindings , Result } ;
17
20
use alloc:: boxed:: Box ;
18
- use core:: {
19
- mem:: ManuallyDrop ,
20
- ops:: Deref ,
21
- ptr:: NonNull ,
22
- sync:: atomic:: { fence, AtomicUsize , Ordering } ,
23
- } ;
21
+ use core:: { cell:: UnsafeCell , convert:: AsRef , mem:: ManuallyDrop , ops:: Deref , ptr:: NonNull } ;
22
+
23
+ extern "C" {
24
+ fn rust_helper_refcount_set ( r : * mut bindings:: refcount_t , n : u32 ) ;
25
+ fn rust_helper_refcount_inc ( r : * mut bindings:: refcount_t ) ;
26
+ fn rust_helper_refcount_dec_and_test ( r : * mut bindings:: refcount_t ) -> bool ;
27
+ }
24
28
25
29
/// A reference-counted pointer to an instance of `T`.
26
30
///
@@ -30,7 +34,7 @@ use core::{
30
34
/// # Invariants
31
35
///
32
36
/// The value stored in [`RefCounted::get_count`] corresponds to the number of instances of [`Ref`]
33
- /// that point to that instance of `T`.
37
+ /// that point to that instance of `T`. So it is always non-zero when a [`Ref`] exists.
34
38
pub struct Ref < T : RefCounted + ?Sized > {
35
39
ptr : NonNull < T > ,
36
40
}
@@ -49,8 +53,18 @@ unsafe impl<T: RefCounted + ?Sized + Sync + Send> Sync for Ref<T> {}
49
53
impl < T : RefCounted > Ref < T > {
50
54
/// Constructs a new reference counted instance of `T`.
51
55
pub fn try_new ( contents : T ) -> Result < Self > {
52
- let boxed = Box :: try_new ( contents) ?;
53
- boxed. get_count ( ) . count . store ( 1 , Ordering :: Relaxed ) ;
56
+ Self :: try_new_and_init ( contents, |_| { } )
57
+ }
58
+
59
+ /// Constructs a new reference counted instance of `T` and calls the initialisation function.
60
+ ///
61
+ /// This is useful because it provides a mutable reference to `T` at its final location.
62
+ pub fn try_new_and_init < U : FnOnce ( & mut T ) > ( contents : T , init : U ) -> Result < Self > {
63
+ let mut boxed = Box :: try_new ( contents) ?;
64
+ // SAFETY: We just allocated the object, no other references exist to it, so it is safe to
65
+ // initialise the refcount to 1.
66
+ unsafe { rust_helper_refcount_set ( boxed. get_count ( ) . count . get ( ) , 1 ) } ;
67
+ init ( & mut boxed) ;
54
68
let ptr = NonNull :: from ( Box :: leak ( boxed) ) ;
55
69
Ok ( Ref { ptr } )
56
70
}
@@ -62,28 +76,14 @@ impl<T: RefCounted + ?Sized> Ref<T> {
62
76
/// It works by incrementing the current reference count as part of constructing the new
63
77
/// pointer.
64
78
pub fn new_from ( obj : & T ) -> Self {
65
- let ref_count = obj. get_count ( ) ;
66
- let cur = ref_count. count . fetch_add ( 1 , Ordering :: Relaxed ) ;
67
- if cur == usize:: MAX {
68
- panic ! ( "Reference count overflowed" ) ;
69
- }
79
+ // SAFETY: By the type invariant, there is necessarily a reference to the object, so it is
80
+ // safe to increment the refcount.
81
+ unsafe { rust_helper_refcount_inc ( obj. get_count ( ) . count . get ( ) ) } ;
70
82
Self {
71
83
ptr : NonNull :: from ( obj) ,
72
84
}
73
85
}
74
86
75
- /// Returns a mutable reference to `T` iff the reference count is one. Otherwise returns
76
- /// [`None`].
77
- pub fn get_mut ( & mut self ) -> Option < & mut T > {
78
- // Synchronises with the decrement in `drop`.
79
- if self . get_count ( ) . count . load ( Ordering :: Acquire ) != 1 {
80
- return None ;
81
- }
82
- // SAFETY: Since there is only one reference, we know it isn't possible for another thread
83
- // to concurrently call this.
84
- Some ( unsafe { self . ptr . as_mut ( ) } )
85
- }
86
-
87
87
/// Determines if two reference-counted pointers point to the same underlying instance of `T`.
88
88
pub fn ptr_eq ( a : & Self , b : & Self ) -> bool {
89
89
core:: ptr:: eq ( a. ptr . as_ptr ( ) , b. ptr . as_ptr ( ) )
@@ -126,22 +126,29 @@ impl<T: RefCounted + ?Sized> Clone for Ref<T> {
126
126
}
127
127
}
128
128
129
+ impl < T : RefCounted + ?Sized > AsRef < T > for Ref < T > {
130
+ fn as_ref ( & self ) -> & T {
131
+ // SAFETY: By the type invariant, there is necessarily a reference to the object, so it is
132
+ // safe to dereference it.
133
+ unsafe { self . ptr . as_ref ( ) }
134
+ }
135
+ }
136
+
129
137
impl < T : RefCounted + ?Sized > Drop for Ref < T > {
130
138
fn drop ( & mut self ) {
131
139
{
132
140
// SAFETY: By the type invariant, there is necessarily a reference to the object.
133
141
let obj = unsafe { self . ptr . as_ref ( ) } ;
134
142
135
- // Synchronises with the acquire below or with the acquire in `get_mut`.
136
- if obj. get_count ( ) . count . fetch_sub ( 1 , Ordering :: Release ) != 1 {
143
+ // SAFETY: Also by the type invariant, we are allowed to decrement the refcount. We
144
+ // also need to limit the scope of `obj` as it cannot be accessed after the refcount is
145
+ // decremented.
146
+ let is_zero = unsafe { rust_helper_refcount_dec_and_test ( obj. get_count ( ) . count . get ( ) ) } ;
147
+ if !is_zero {
137
148
return ;
138
149
}
139
150
}
140
151
141
- // Synchronises with the release when decrementing above. This ensures that modifications
142
- // from all previous threads/CPUs are visible to the underlying object's `drop`.
143
- fence ( Ordering :: Acquire ) ;
144
-
145
152
// The count reached zero, we must free the memory.
146
153
//
147
154
// SAFETY: The pointer was initialised from the result of `Box::into_raw`.
@@ -154,7 +161,9 @@ impl<T: RefCounted + ?Sized> Drop for Ref<T> {
154
161
/// # Safety
155
162
///
156
163
/// Implementers of [`RefCounted`] must ensure that all of their constructors call
157
- /// [`Ref::try_new`].
164
+ /// [`Ref::try_new`] or [`Ref::try_new_and_init`]. Constructing instances that are not wrapped in
165
+ /// [`Ref`] results in unsafety because one could call [`Ref::new_from`], and the destructor would
166
+ /// drop the underlying object as if it had been allocated through one of [`Ref`] constructors.
158
167
pub unsafe trait RefCounted {
159
168
/// Returns a pointer to the object field holds the reference count.
160
169
fn get_count ( & self ) -> & RefCount ;
@@ -164,21 +173,7 @@ pub unsafe trait RefCounted {
164
173
///
165
174
/// It is meant to be embedded in objects to be reference-counted, with [`RefCounted::get_count`]
166
175
/// returning a reference to it.
176
+ #[ derive( Default ) ]
167
177
pub struct RefCount {
168
- count : AtomicUsize ,
169
- }
170
-
171
- impl RefCount {
172
- /// Constructs a new instance of [`RefCount`].
173
- pub fn new ( ) -> Self {
174
- Self {
175
- count : AtomicUsize :: new ( 1 ) ,
176
- }
177
- }
178
- }
179
-
180
- impl Default for RefCount {
181
- fn default ( ) -> Self {
182
- Self :: new ( )
183
- }
178
+ count : UnsafeCell < bindings:: refcount_t > ,
184
179
}
0 commit comments