3
3
use alloc:: boxed:: Box ;
4
4
use alloc:: sync:: Arc ;
5
5
use core:: cell:: UnsafeCell ;
6
- use core:: fmt;
7
6
use core:: ops:: { Deref , DerefMut } ;
8
7
use core:: pin:: Pin ;
9
- use core:: sync:: atomic:: AtomicUsize ;
8
+ use core:: sync:: atomic:: AtomicPtr ;
10
9
use core:: sync:: atomic:: Ordering :: SeqCst ;
10
+ use core:: { fmt, ptr} ;
11
11
#[ cfg( feature = "bilock" ) ]
12
12
use futures_core:: future:: Future ;
13
13
use futures_core:: task:: { Context , Poll , Waker } ;
@@ -41,7 +41,7 @@ pub struct BiLock<T> {
41
41
42
42
#[ derive( Debug ) ]
43
43
struct Inner < T > {
44
- state : AtomicUsize ,
44
+ state : AtomicPtr < Waker > ,
45
45
value : Option < UnsafeCell < T > > ,
46
46
}
47
47
@@ -61,7 +61,10 @@ impl<T> BiLock<T> {
61
61
/// Similarly, reuniting the lock and extracting the inner value is only
62
62
/// possible when `T` is `Unpin`.
63
63
pub fn new ( t : T ) -> ( Self , Self ) {
64
- let arc = Arc :: new ( Inner { state : AtomicUsize :: new ( 0 ) , value : Some ( UnsafeCell :: new ( t) ) } ) ;
64
+ let arc = Arc :: new ( Inner {
65
+ state : AtomicPtr :: new ( ptr:: null_mut ( ) ) ,
66
+ value : Some ( UnsafeCell :: new ( t) ) ,
67
+ } ) ;
65
68
66
69
( Self { arc : arc. clone ( ) } , Self { arc } )
67
70
}
@@ -87,7 +90,8 @@ impl<T> BiLock<T> {
87
90
pub fn poll_lock ( & self , cx : & mut Context < ' _ > ) -> Poll < BiLockGuard < ' _ , T > > {
88
91
let mut waker = None ;
89
92
loop {
90
- match self . arc . state . swap ( 1 , SeqCst ) {
93
+ let n = self . arc . state . swap ( invalid_ptr ( 1 ) , SeqCst ) ;
94
+ match n as usize {
91
95
// Woohoo, we grabbed the lock!
92
96
0 => return Poll :: Ready ( BiLockGuard { bilock : self } ) ,
93
97
@@ -96,27 +100,27 @@ impl<T> BiLock<T> {
96
100
97
101
// A task was previously blocked on this lock, likely our task,
98
102
// so we need to update that task.
99
- n => unsafe {
100
- let mut prev = Box :: from_raw ( n as * mut Waker ) ;
103
+ _ => unsafe {
104
+ let mut prev = Box :: from_raw ( n) ;
101
105
* prev = cx. waker ( ) . clone ( ) ;
102
106
waker = Some ( prev) ;
103
107
} ,
104
108
}
105
109
106
110
// type ascription for safety's sake!
107
111
let me: Box < Waker > = waker. take ( ) . unwrap_or_else ( || Box :: new ( cx. waker ( ) . clone ( ) ) ) ;
108
- let me = Box :: into_raw ( me) as usize ;
112
+ let me = Box :: into_raw ( me) ;
109
113
110
- match self . arc . state . compare_exchange ( 1 , me, SeqCst , SeqCst ) {
114
+ match self . arc . state . compare_exchange ( invalid_ptr ( 1 ) , me, SeqCst , SeqCst ) {
111
115
// The lock is still locked, but we've now parked ourselves, so
112
116
// just report that we're scheduled to receive a notification.
113
117
Ok ( _) => return Poll :: Pending ,
114
118
115
119
// Oops, looks like the lock was unlocked after our swap above
116
120
// and before the compare_exchange. Deallocate what we just
117
121
// allocated and go through the loop again.
118
- Err ( 0 ) => unsafe {
119
- waker = Some ( Box :: from_raw ( me as * mut Waker ) ) ;
122
+ Err ( n ) if n . is_null ( ) => unsafe {
123
+ waker = Some ( Box :: from_raw ( me) ) ;
120
124
} ,
121
125
122
126
// The top of this loop set the previous state to 1, so if we
@@ -125,7 +129,7 @@ impl<T> BiLock<T> {
125
129
// but we're trying to acquire the lock and there's only one
126
130
// other reference of the lock, so it should be impossible for
127
131
// that task to ever block itself.
128
- Err ( n) => panic ! ( "invalid state: {}" , n) ,
132
+ Err ( n) => panic ! ( "invalid state: {}" , n as usize ) ,
129
133
}
130
134
}
131
135
}
@@ -164,7 +168,8 @@ impl<T> BiLock<T> {
164
168
}
165
169
166
170
fn unlock ( & self ) {
167
- match self . arc . state . swap ( 0 , SeqCst ) {
171
+ let n = self . arc . state . swap ( ptr:: null_mut ( ) , SeqCst ) ;
172
+ match n as usize {
168
173
// we've locked the lock, shouldn't be possible for us to see an
169
174
// unlocked lock.
170
175
0 => panic ! ( "invalid unlocked state" ) ,
@@ -174,8 +179,8 @@ impl<T> BiLock<T> {
174
179
175
180
// Another task has parked themselves on this lock, let's wake them
176
181
// up as its now their turn.
177
- n => unsafe {
178
- Box :: from_raw ( n as * mut Waker ) . wake ( ) ;
182
+ _ => unsafe {
183
+ Box :: from_raw ( n) . wake ( ) ;
179
184
} ,
180
185
}
181
186
}
@@ -189,7 +194,7 @@ impl<T: Unpin> Inner<T> {
189
194
190
195
impl < T > Drop for Inner < T > {
191
196
fn drop ( & mut self ) {
192
- assert_eq ! ( self . state. load( SeqCst ) , 0 ) ;
197
+ assert ! ( self . state. load( SeqCst ) . is_null ( ) ) ;
193
198
}
194
199
}
195
200
@@ -277,3 +282,12 @@ impl<'a, T> Future for BiLockAcquire<'a, T> {
277
282
self . bilock . poll_lock ( cx)
278
283
}
279
284
}
285
+
286
+ // Based on core::ptr::invalid_mut. Equivalent to `addr as *mut T`, but is strict-provenance compatible.
287
+ #[ allow( clippy:: useless_transmute) ]
288
+ #[ inline]
289
+ fn invalid_ptr < T > ( addr : usize ) -> * mut T {
290
+ // SAFETY: every valid integer is also a valid pointer (as long as you don't dereference that
291
+ // pointer).
292
+ unsafe { core:: mem:: transmute ( addr) }
293
+ }
0 commit comments