1 // Copyright 2016 Amanieu d'Antras
2 //
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.
7
8 use core::{
9 ptr,
10 sync::atomic::{AtomicI32, Ordering},
11 };
12 use instant::Instant;
13 use std::thread;
14 use syscall::{
15 call::futex,
16 data::TimeSpec,
17 error::{Error, EAGAIN, EFAULT, EINTR, ETIMEDOUT},
18 flag::{FUTEX_WAIT, FUTEX_WAKE},
19 };
20
21 const UNPARKED: i32 = 0;
22 const PARKED: i32 = 1;
23
24 // Helper type for putting a thread to sleep until some other thread wakes it up
25 pub struct ThreadParker {
26 futex: AtomicI32,
27 }
28
29 impl super::ThreadParkerT for ThreadParker {
30 type UnparkHandle = UnparkHandle;
31
32 const IS_CHEAP_TO_CONSTRUCT: bool = true;
33
34 #[inline]
new() -> ThreadParker35 fn new() -> ThreadParker {
36 ThreadParker {
37 futex: AtomicI32::new(UNPARKED),
38 }
39 }
40
41 #[inline]
prepare_park(&self)42 unsafe fn prepare_park(&self) {
43 self.futex.store(PARKED, Ordering::Relaxed);
44 }
45
46 #[inline]
timed_out(&self) -> bool47 unsafe fn timed_out(&self) -> bool {
48 self.futex.load(Ordering::Relaxed) != UNPARKED
49 }
50
51 #[inline]
park(&self)52 unsafe fn park(&self) {
53 while self.futex.load(Ordering::Acquire) != UNPARKED {
54 self.futex_wait(None);
55 }
56 }
57
58 #[inline]
park_until(&self, timeout: Instant) -> bool59 unsafe fn park_until(&self, timeout: Instant) -> bool {
60 while self.futex.load(Ordering::Acquire) != UNPARKED {
61 let now = Instant::now();
62 if timeout <= now {
63 return false;
64 }
65 let diff = timeout - now;
66 if diff.as_secs() > i64::max_value() as u64 {
67 // Timeout overflowed, just sleep indefinitely
68 self.park();
69 return true;
70 }
71 let ts = TimeSpec {
72 tv_sec: diff.as_secs() as i64,
73 tv_nsec: diff.subsec_nanos() as i32,
74 };
75 self.futex_wait(Some(ts));
76 }
77 true
78 }
79
80 #[inline]
unpark_lock(&self) -> UnparkHandle81 unsafe fn unpark_lock(&self) -> UnparkHandle {
82 // We don't need to lock anything, just clear the state
83 self.futex.store(UNPARKED, Ordering::Release);
84
85 UnparkHandle { futex: self.ptr() }
86 }
87 }
88
89 impl ThreadParker {
90 #[inline]
futex_wait(&self, ts: Option<TimeSpec>)91 fn futex_wait(&self, ts: Option<TimeSpec>) {
92 let ts_ptr = ts
93 .as_ref()
94 .map(|ts_ref| ts_ref as *const _)
95 .unwrap_or(ptr::null());
96 let r = unsafe {
97 futex(
98 self.ptr(),
99 FUTEX_WAIT,
100 PARKED,
101 ts_ptr as usize,
102 ptr::null_mut(),
103 )
104 };
105 match r {
106 Ok(r) => debug_assert_eq!(r, 0),
107 Err(Error { errno }) => {
108 debug_assert!(errno == EINTR || errno == EAGAIN || errno == ETIMEDOUT);
109 }
110 }
111 }
112
113 #[inline]
ptr(&self) -> *mut i32114 fn ptr(&self) -> *mut i32 {
115 &self.futex as *const AtomicI32 as *mut i32
116 }
117 }
118
119 pub struct UnparkHandle {
120 futex: *mut i32,
121 }
122
123 impl super::UnparkHandleT for UnparkHandle {
124 #[inline]
unpark(self)125 unsafe fn unpark(self) {
126 // The thread data may have been freed at this point, but it doesn't
127 // matter since the syscall will just return EFAULT in that case.
128 let r = futex(self.futex, FUTEX_WAKE, PARKED, 0, ptr::null_mut());
129 match r {
130 Ok(num_woken) => debug_assert!(num_woken == 0 || num_woken == 1),
131 Err(Error { errno }) => debug_assert_eq!(errno, EFAULT),
132 }
133 }
134 }
135
136 #[inline]
thread_yield()137 pub fn thread_yield() {
138 thread::yield_now();
139 }
140