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,8 @@ 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 (because at least one [`Ref`]
38
+ /// exists).
34
39
pub struct Ref < T : RefCounted + ?Sized > {
35
40
ptr : NonNull < T > ,
36
41
}
@@ -49,8 +54,19 @@ unsafe impl<T: RefCounted + ?Sized + Sync + Send> Sync for Ref<T> {}
49
54
impl < T : RefCounted > Ref < T > {
50
55
/// Constructs a new reference counted instance of `T`.
51
56
pub fn try_new ( contents : T ) -> Result < Self > {
52
- let boxed = Box :: try_new ( contents) ?;
53
- boxed. get_count ( ) . count . store ( 1 , Ordering :: Relaxed ) ;
57
+ Self :: try_new_and_init ( contents, |_| { } )
58
+ }
59
+
60
+ /// Constructs a new reference counted instance of `T` and calls the initialisation function.
61
+ ///
62
+ /// This is useful because it provides a mutable reference to `T` at its final location.
63
+ pub fn try_new_and_init < U : FnOnce ( & mut T ) > ( contents : T , init : U ) -> Result < Self > {
64
+ let mut boxed = Box :: try_new ( contents) ?;
65
+ // INVARIANT: The refcount is initialised to a non-zero value.
66
+ // SAFETY: We just allocated the object, no other references exist to it, so it is safe to
67
+ // initialise the refcount to 1.
68
+ unsafe { rust_helper_refcount_set ( boxed. get_count ( ) . count . get ( ) , 1 ) } ;
69
+ init ( & mut boxed) ;
54
70
let ptr = NonNull :: from ( Box :: leak ( boxed) ) ;
55
71
Ok ( Ref { ptr } )
56
72
}
@@ -62,28 +78,15 @@ impl<T: RefCounted + ?Sized> Ref<T> {
62
78
/// It works by incrementing the current reference count as part of constructing the new
63
79
/// pointer.
64
80
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
- }
81
+ // INVARIANT: C `refcount_inc` saturates the refcount, so it cannot overflow to zero.
82
+ // SAFETY: By the type invariant, there is necessarily a reference to the object, so it is
83
+ // safe to increment the refcount.
84
+ unsafe { rust_helper_refcount_inc ( obj. get_count ( ) . count . get ( ) ) } ;
70
85
Self {
71
86
ptr : NonNull :: from ( obj) ,
72
87
}
73
88
}
74
89
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
90
/// Determines if two reference-counted pointers point to the same underlying instance of `T`.
88
91
pub fn ptr_eq ( a : & Self , b : & Self ) -> bool {
89
92
core:: ptr:: eq ( a. ptr . as_ptr ( ) , b. ptr . as_ptr ( ) )
@@ -126,22 +129,31 @@ impl<T: RefCounted + ?Sized> Clone for Ref<T> {
126
129
}
127
130
}
128
131
132
+ impl < T : RefCounted + ?Sized > AsRef < T > for Ref < T > {
133
+ fn as_ref ( & self ) -> & T {
134
+ // SAFETY: By the type invariant, there is necessarily a reference to the object, so it is
135
+ // safe to dereference it.
136
+ unsafe { self . ptr . as_ref ( ) }
137
+ }
138
+ }
139
+
129
140
impl < T : RefCounted + ?Sized > Drop for Ref < T > {
130
141
fn drop ( & mut self ) {
131
142
{
132
143
// SAFETY: By the type invariant, there is necessarily a reference to the object.
133
144
let obj = unsafe { self . ptr . as_ref ( ) } ;
134
145
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 {
146
+ // INVARIANT: If the refcount reaches zero, there are no other instances of `Ref`, and
147
+ // this instance is being dropped, so the broken invariant is not observable.
148
+ // SAFETY: Also by the type invariant, we are allowed to decrement the refcount. We
149
+ // also need to limit the scope of `obj` as it cannot be accessed after the refcount is
150
+ // decremented.
151
+ let is_zero = unsafe { rust_helper_refcount_dec_and_test ( obj. get_count ( ) . count . get ( ) ) } ;
152
+ if !is_zero {
137
153
return ;
138
154
}
139
155
}
140
156
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
157
// The count reached zero, we must free the memory.
146
158
//
147
159
// SAFETY: The pointer was initialised from the result of `Box::into_raw`.
@@ -154,7 +166,9 @@ impl<T: RefCounted + ?Sized> Drop for Ref<T> {
154
166
/// # Safety
155
167
///
156
168
/// Implementers of [`RefCounted`] must ensure that all of their constructors call
157
- /// [`Ref::try_new`].
169
+ /// [`Ref::try_new`] or [`Ref::try_new_and_init`]. Constructing instances that are not wrapped in
170
+ /// [`Ref`] results in unsafety because one could call [`Ref::new_from`], and the destructor would
171
+ /// drop the underlying object as if it had been allocated through one of [`Ref`] constructors.
158
172
pub unsafe trait RefCounted {
159
173
/// Returns a pointer to the object field holds the reference count.
160
174
fn get_count ( & self ) -> & RefCount ;
@@ -164,21 +178,7 @@ pub unsafe trait RefCounted {
164
178
///
165
179
/// It is meant to be embedded in objects to be reference-counted, with [`RefCounted::get_count`]
166
180
/// returning a reference to it.
181
+ #[ derive( Default ) ]
167
182
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
- }
183
+ count : UnsafeCell < bindings:: refcount_t > ,
184
184
}
0 commit comments