1 // Copyright 2016 Amanieu d'Antras
3 // Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4 // http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5 // http://opensource.org/licenses/MIT>, at your option. This file may not be
6 // copied, modified, or distributed except according to those terms.
11 mem
::{self, MaybeUninit}
,
12 sync
::atomic
::{AtomicU32, Ordering}
,
14 use std
::{convert::TryFrom, thread, time::Instant}
;
18 static __pthread_thread_id
: abi
::tid
;
26 pub fn new() -> Self {
28 lock
: AtomicU32
::new(abi
::LOCK_UNLOCKED
.0),
35 unsafe fn try_lock(&self) -> Option
<LockGuard
> {
36 // Attempt to acquire the lock.
37 if let Err(old
) = self.lock
.compare_exchange(
39 __pthread_thread_id
.0 | abi
::LOCK_WRLOCKED
.0,
43 // Failure. Crash upon recursive acquisition.
45 old
& !abi
::LOCK_KERNEL_MANAGED
.0,
46 __pthread_thread_id
.0 | abi
::LOCK_WRLOCKED
.0,
47 "Attempted to recursive write-lock a lock",
51 Some(LockGuard { lock: &self.lock }
)
57 /// This method is unsafe because the `LockGuard` has a raw pointer into this `Lock`
58 /// that it will access on drop to unlock the lock. So make sure the `LockGuard` goes
59 /// out of scope before the `Lock` it came from moves or goes out of scope.
60 pub unsafe fn lock(&self) -> LockGuard
{
61 self.try_lock().unwrap_or_else(|| {
62 // Call into the kernel to acquire a write lock.
63 let subscription
= abi
::subscription
{
64 type_
: abi
::eventtype
::LOCK_WRLOCK
,
65 union: abi
::subscription_union
{
66 lock
: abi
::subscription_lock
{
68 lock_scope
: abi
::scope
::PRIVATE
,
73 let mut event
= MaybeUninit
::<abi
::event
>::uninit();
74 let mut nevents
: usize = 0;
75 let ret
= abi
::poll(&subscription
, event
.as_mut_ptr(), 1, &mut nevents
);
76 debug_assert_eq
!(ret
, abi
::errno
::SUCCESS
);
77 debug_assert_eq
!(event
.assume_init().error
, abi
::errno
::SUCCESS
);
79 LockGuard { lock: &self.lock }
83 fn ptr(&self) -> *mut abi
::lock
{
84 &self.lock
as *const AtomicU32
as *mut abi
::lock
89 lock
: *const AtomicU32
,
93 fn ptr(&self) -> *mut abi
::lock
{
94 self.lock
as *mut abi
::lock
98 impl Drop
for LockGuard
{
100 let lock
= unsafe { &*self.lock }
;
102 lock
.load(Ordering
::Relaxed
) & !abi
::LOCK_KERNEL_MANAGED
.0,
103 unsafe { __pthread_thread_id.0 }
| abi
::LOCK_WRLOCKED
.0,
104 "This lock is not write-locked by this thread"
109 unsafe { __pthread_thread_id.0 }
| abi
::LOCK_WRLOCKED
.0,
110 abi
::LOCK_UNLOCKED
.0,
116 // Lock is managed by kernelspace. Call into the kernel
117 // to unblock waiting threads.
118 let ret
= unsafe { abi::lock_unlock(self.lock as *mut abi::lock, abi::scope::PRIVATE) }
;
119 debug_assert_eq
!(ret
, abi
::errno
::SUCCESS
);
129 pub fn new() -> Self {
131 condvar
: AtomicU32
::new(abi
::CONDVAR_HAS_NO_WAITERS
.0),
135 pub fn wait(&self, lock
: &LockGuard
) {
137 let subscription
= abi
::subscription
{
138 type_
: abi
::eventtype
::CONDVAR
,
139 union: abi
::subscription_union
{
140 condvar
: abi
::subscription_condvar
{
142 condvar_scope
: abi
::scope
::PRIVATE
,
144 lock_scope
: abi
::scope
::PRIVATE
,
149 let mut event
= MaybeUninit
::<abi
::event
>::uninit();
150 let mut nevents
: usize = 0;
152 let ret
= abi
::poll(&subscription
, event
.as_mut_ptr(), 1, &mut nevents
);
153 debug_assert_eq
!(ret
, abi
::errno
::SUCCESS
);
154 debug_assert_eq
!(event
.assume_init().error
, abi
::errno
::SUCCESS
);
158 /// Waits for a signal on the condvar.
159 /// Returns false if it times out before anyone notified us.
160 pub fn wait_timeout(&self, lock
: &LockGuard
, timeout
: abi
::timestamp
) -> bool
{
162 let subscriptions
= [
164 type_
: abi
::eventtype
::CONDVAR
,
165 union: abi
::subscription_union
{
166 condvar
: abi
::subscription_condvar
{
168 condvar_scope
: abi
::scope
::PRIVATE
,
170 lock_scope
: abi
::scope
::PRIVATE
,
176 type_
: abi
::eventtype
::CLOCK
,
177 union: abi
::subscription_union
{
178 clock
: abi
::subscription_clock
{
179 clock_id
: abi
::clockid
::MONOTONIC
,
187 let mut events
= MaybeUninit
::<[abi
::event
; 2]>::uninit();
188 let mut nevents
: usize = 0;
191 subscriptions
.as_ptr(),
192 events
.as_mut_ptr() as *mut _
,
196 debug_assert_eq
!(ret
, abi
::errno
::SUCCESS
);
197 let events
= events
.assume_init();
198 for i
in 0..nevents
{
199 debug_assert_eq
!(events
[i
].error
, abi
::errno
::SUCCESS
);
200 if events
[i
].type_
== abi
::eventtype
::CONDVAR
{
208 pub fn notify(&self) {
209 let ret
= unsafe { abi::condvar_signal(self.ptr(), abi::scope::PRIVATE, 1) }
;
210 debug_assert_eq
!(ret
, abi
::errno
::SUCCESS
);
213 fn ptr(&self) -> *mut abi
::condvar
{
214 &self.condvar
as *const AtomicU32
as *mut abi
::condvar
218 // Helper type for putting a thread to sleep until some other thread wakes it up
219 pub struct ThreadParker
{
220 should_park
: Cell
<bool
>,
225 impl super::ThreadParkerT
for ThreadParker
{
226 type UnparkHandle
= UnparkHandle
;
228 const IS_CHEAP_TO_CONSTRUCT
: bool
= true;
230 fn new() -> ThreadParker
{
232 should_park
: Cell
::new(false),
234 condvar
: Condvar
::new(),
238 unsafe fn prepare_park(&self) {
239 self.should_park
.set(true);
242 unsafe fn timed_out(&self) -> bool
{
243 // We need to grab the lock here because another thread may be
244 // concurrently executing UnparkHandle::unpark, which is done without
245 // holding the queue lock.
246 let _guard
= self.lock
.lock();
247 self.should_park
.get()
250 unsafe fn park(&self) {
251 let guard
= self.lock
.lock();
252 while self.should_park
.get() {
253 self.condvar
.wait(&guard
);
257 unsafe fn park_until(&self, timeout
: Instant
) -> bool
{
258 let guard
= self.lock
.lock();
259 while self.should_park
.get() {
260 if let Some(duration_left
) = timeout
.checked_duration_since(Instant
::now()) {
261 if let Ok(nanos_left
) = abi
::timestamp
::try_from(duration_left
.as_nanos()) {
262 self.condvar
.wait_timeout(&guard
, nanos_left
);
264 // remaining timeout overflows an abi::timestamp. Sleep indefinitely
265 self.condvar
.wait(&guard
);
275 unsafe fn unpark_lock(&self) -> UnparkHandle
{
276 let _lock_guard
= self.lock
.lock();
285 pub struct UnparkHandle
{
286 thread_parker
: *const ThreadParker
,
287 _lock_guard
: LockGuard
,
290 impl super::UnparkHandleT
for UnparkHandle
{
291 unsafe fn unpark(self) {
292 (*self.thread_parker
).should_park
.set(false);
294 // We notify while holding the lock here to avoid races with the target
295 // thread. In particular, the thread could exit after we unlock the
296 // mutex, which would make the condvar access invalid memory.
297 (*self.thread_parker
).condvar
.notify();
302 pub fn thread_yield() {