@@ -45,6 +45,10 @@ impl<C: RawHandleImpl> GcHandleList<C> {
45
45
last_free_slot : AtomicPtr :: new ( null_mut ( ) ) ,
46
46
}
47
47
}
48
+ /// Append the specified slot to this list
49
+ ///
50
+ /// The specified slot must be logically owned
51
+ /// and not already part of this list
48
52
unsafe fn append_free_slot ( & self , slot : * mut HandleSlot < C > ) {
49
53
// Verify it's actually free...
50
54
debug_assert_eq ! (
@@ -63,14 +67,27 @@ impl<C: RawHandleImpl> GcHandleList<C> {
63
67
*/
64
68
( * slot) . freed . prev_free_slot
65
69
. store ( last_free, Ordering :: Release ) ;
66
- let actual_last_free = self . last_free_slot . compare_and_swap (
70
+ /*
71
+ * We really dont want surprise failures because we're going to
72
+ * have to redo the above store if that happens.
73
+ * Likewise we want acquire ordering so we don't fail unnecessarily
74
+ * on retry.
75
+ * In theory this is premature optimization, but I really want to
76
+ * make this as straightforward as possible.
77
+ * Maybe we should look into the efficiency of this on ARM?
78
+ */
79
+ match self . last_free_slot . compare_exchange (
67
80
last_free, slot,
68
- Ordering :: AcqRel
69
- ) ;
70
- if actual_last_free == last_free {
71
- return // Success
72
- } else {
73
- last_free = actual_last_free;
81
+ Ordering :: AcqRel ,
82
+ Ordering :: Acquire
83
+ ) {
84
+ Ok ( actual) => {
85
+ debug_assert_eq ! ( actual, last_free) ;
86
+ return ; // Success
87
+ } ,
88
+ Err ( actual) => {
89
+ last_free = actual;
90
+ }
74
91
}
75
92
}
76
93
}
@@ -96,29 +113,36 @@ impl<C: RawHandleImpl> GcHandleList<C> {
96
113
* If this CAS succeeds, we have ownership.
97
114
* Otherwise another thread beat us and
98
115
* we must try again.
116
+ *
117
+ * Avoid relaxed ordering and compare_exchange_weak
118
+ * to make this straightforward.
99
119
*/
100
- let actual_slot = self . last_free_slot . compare_and_swap (
120
+ match self . last_free_slot . compare_exchange (
101
121
slot, prev,
102
- Ordering :: AcqRel
103
- ) ;
104
- if actual_slot == slot {
105
- // Verify it's actually free...
106
- debug_assert_eq ! (
122
+ Ordering :: AcqRel ,
123
+ Ordering :: Acquire
124
+ ) {
125
+ Ok ( actual_slot) => {
126
+ debug_assert_eq ! ( actual_slot, slot) ;
127
+ // Verify it's actually free...
128
+ debug_assert_eq ! (
129
+ ( * slot) . valid. value
130
+ . load( Ordering :: SeqCst ) ,
131
+ std:: ptr:: null_mut( )
132
+ ) ;
133
+ /*
134
+ * We own the slot, initialize it to point to
135
+ * the provided pointer. The user is responsible
136
+ * for any remaining initialization.
137
+ */
107
138
( * slot) . valid . value
108
- . load( Ordering :: SeqCst ) ,
109
- std:: ptr:: null_mut( )
110
- ) ;
111
- /*
112
- * We own the slot, initialize it to point to
113
- * the provided pointer. The user is responsible
114
- * for any remaining initialization.
115
- */
116
- ( * slot) . valid . value
117
- . store ( value, Ordering :: Release ) ;
118
- return & ( * slot) . valid ;
119
- } else {
120
- // Try again
121
- slot = actual_slot;
139
+ . store ( value, Ordering :: Release ) ;
140
+ return & ( * slot) . valid ;
141
+ } ,
142
+ Err ( actual_slot) => {
143
+ // Try again
144
+ slot = actual_slot;
145
+ }
122
146
}
123
147
}
124
148
// Empty free list
@@ -173,20 +197,25 @@ impl<C: RawHandleImpl> GcHandleList<C> {
173
197
last_alloc : AtomicUsize :: new ( 0 ) ,
174
198
prev : AtomicPtr :: new ( prev_bucket) ,
175
199
} ) ) ;
176
- let actual_bucket = self . last_bucket . compare_and_swap (
177
- prev_bucket, allocated_bucket, Ordering :: SeqCst
178
- ) ;
179
- if actual_bucket == prev_bucket {
180
- Ok ( & * actual_bucket)
181
- } else {
182
- /*
183
- * Someone else beat us too creating the bucket.
184
- *
185
- * Free the bucket we've created and return
186
- * their bucket
187
- */
188
- drop ( Box :: from_raw ( allocated_bucket) ) ;
189
- Err ( & * actual_bucket)
200
+ match self . last_bucket . compare_exchange (
201
+ prev_bucket, allocated_bucket,
202
+ Ordering :: SeqCst ,
203
+ Ordering :: SeqCst
204
+ ) {
205
+ Ok ( actual_bucket) => {
206
+ assert_eq ! ( actual_bucket, prev_bucket) ;
207
+ Ok ( & * actual_bucket)
208
+ } ,
209
+ Err ( actual_bucket) => {
210
+ /*
211
+ * Someone else beat us to creating the bucket.
212
+ *
213
+ * Free the bucket we've created and return
214
+ * their bucket
215
+ */
216
+ drop ( Box :: from_raw ( allocated_bucket) ) ;
217
+ Err ( & * actual_bucket)
218
+ }
190
219
}
191
220
}
192
221
/// Trace the [GcHandle] using the specified closure.
@@ -267,10 +296,11 @@ impl<C: RawHandleImpl> GcHandleBucket<C> {
267
296
for ( i, slot) in self . slots . iter ( ) . enumerate ( )
268
297
. skip ( last_alloc) {
269
298
// TODO: All these fences must be horrible on ARM
270
- if slot. valid . value . compare_and_swap (
299
+ if slot. valid . value . compare_exchange (
271
300
std:: ptr:: null_mut ( ) , value,
272
- Ordering :: AcqRel
273
- ) . is_null ( ) {
301
+ Ordering :: AcqRel ,
302
+ Ordering :: Relaxed
303
+ ) . is_ok ( ) {
274
304
// We acquired ownership!
275
305
self . last_alloc . fetch_max ( i, Ordering :: AcqRel ) ;
276
306
return Some ( & * slot) ;
@@ -469,16 +499,30 @@ impl<T: GcSafe, C: RawHandleImpl> Clone for GcHandle<T, C> {
469
499
. is_null( ) ,
470
500
"Pointer is invalid"
471
501
) ;
502
+ let mut old_refcnt = inner. refcnt . load ( Ordering :: Relaxed ) ;
472
503
loop {
473
- let old_refcnt = inner. refcnt . load ( Ordering :: Relaxed ) ;
474
504
assert_ne ! (
475
505
old_refcnt, isize :: max_value( ) as usize ,
476
506
"Reference count overflow"
477
507
) ;
478
- if inner. refcnt . compare_and_swap (
508
+ /*
509
+ * NOTE: Relaxed is sufficient for failure since we have no
510
+ * expectations about the new state. Weak exchange is okay
511
+ * since we retry in a loop.
512
+ *
513
+ * NOTE: We do **not** use fetch_add because we are afraid
514
+ * of refcount overflow. We should possibly consider it
515
+ */
516
+ match inner. refcnt . compare_exchange_weak (
479
517
old_refcnt, old_refcnt + 1 ,
480
- Ordering :: AcqRel
481
- ) == old_refcnt { break } ;
518
+ Ordering :: AcqRel ,
519
+ Ordering :: Relaxed
520
+ ) {
521
+ Ok ( _) => break ,
522
+ Err ( val) => {
523
+ old_refcnt = val;
524
+ }
525
+ }
482
526
}
483
527
GcHandle {
484
528
inner : self . inner ,
0 commit comments