Skip to content

Commit bd673ba

Browse files
committed
Add parking_lot_core module
1 parent fbf2a92 commit bd673ba

File tree

17 files changed

+2536
-0
lines changed

17 files changed

+2536
-0
lines changed

src/libstd/sys/cloudabi/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ pub mod stdio;
3232
pub mod thread;
3333
#[path = "../unix/thread_local.rs"]
3434
pub mod thread_local;
35+
#[path = "../unix/thread_parker/pthread.rs"]
36+
pub mod thread_parker;
3537
pub mod time;
3638

3739
mod abi;

src/libstd/sys/redox/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ pub mod stdio;
4242
pub mod syscall;
4343
pub mod thread;
4444
pub mod thread_local;
45+
#[path = "../unix/thread_parker/pthread.rs"]
46+
pub mod thread_parker;
4547
pub mod time;
4648

4749
#[cfg(not(test))]

src/libstd/sys/unix/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ pub mod rwlock;
6666
pub mod stack_overflow;
6767
pub mod thread;
6868
pub mod thread_local;
69+
pub mod thread_parker;
6970
pub mod time;
7071
pub mod stdio;
7172

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
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 libc;
9+
use ptr;
10+
use sync::atomic::{AtomicI32, Ordering};
11+
use time::Instant;
12+
use super::tv_nsec_t;
13+
14+
const FUTEX_WAIT: i32 = 0;
15+
const FUTEX_WAKE: i32 = 1;
16+
const FUTEX_PRIVATE: i32 = 128;
17+
18+
// Helper type for putting a thread to sleep until some other thread wakes it up
19+
pub struct ThreadParker {
20+
futex: AtomicI32,
21+
}
22+
23+
impl ThreadParker {
24+
pub const IS_CHEAP_TO_CONSTRUCT: bool = true;
25+
26+
pub fn new() -> ThreadParker {
27+
ThreadParker {
28+
futex: AtomicI32::new(0),
29+
}
30+
}
31+
32+
// Prepares the parker. This should be called before adding it to the queue.
33+
pub fn prepare_park(&self) {
34+
self.futex.store(1, Ordering::Relaxed);
35+
}
36+
37+
// Checks if the park timed out. This should be called while holding the
38+
// queue lock after park_until has returned false.
39+
pub fn timed_out(&self) -> bool {
40+
self.futex.load(Ordering::Relaxed) != 0
41+
}
42+
43+
// Parks the thread until it is unparked. This should be called after it has
44+
// been added to the queue, after unlocking the queue.
45+
pub fn park(&self) {
46+
while self.futex.load(Ordering::Acquire) != 0 {
47+
self.futex_wait(None);
48+
}
49+
}
50+
51+
// Parks the thread until it is unparked or the timeout is reached. This
52+
// should be called after it has been added to the queue, after unlocking
53+
// the queue. Returns true if we were unparked and false if we timed out.
54+
pub fn park_until(&self, timeout: Instant) -> bool {
55+
while self.futex.load(Ordering::Acquire) != 0 {
56+
let now = Instant::now();
57+
if timeout <= now {
58+
return false;
59+
}
60+
let diff = timeout - now;
61+
if diff.as_secs() as libc::time_t as u64 != diff.as_secs() {
62+
// Timeout overflowed, just sleep indefinitely
63+
self.park();
64+
return true;
65+
}
66+
let ts = libc::timespec {
67+
tv_sec: diff.as_secs() as libc::time_t,
68+
tv_nsec: diff.subsec_nanos() as tv_nsec_t,
69+
};
70+
self.futex_wait(Some(ts));
71+
}
72+
true
73+
}
74+
75+
#[inline]
76+
fn futex_wait(&self, ts: Option<libc::timespec>) {
77+
let ts_ptr = ts
78+
.as_ref()
79+
.map(|ts_ref| ts_ref as *const _)
80+
.unwrap_or(ptr::null());
81+
let r = unsafe {
82+
libc::syscall(
83+
libc::SYS_futex,
84+
&self.futex,
85+
FUTEX_WAIT | FUTEX_PRIVATE,
86+
1,
87+
ts_ptr,
88+
)
89+
};
90+
debug_assert!(r == 0 || r == -1);
91+
if r == -1 {
92+
unsafe {
93+
debug_assert!(
94+
*libc::__errno_location() == libc::EINTR
95+
|| *libc::__errno_location() == libc::EAGAIN
96+
|| (ts.is_some() && *libc::__errno_location() == libc::ETIMEDOUT)
97+
);
98+
}
99+
}
100+
}
101+
102+
// Locks the parker to prevent the target thread from exiting. This is
103+
// necessary to ensure that thread-local ThreadData objects remain valid.
104+
// This should be called while holding the queue lock.
105+
pub fn unpark_lock(&self) -> UnparkHandle {
106+
// We don't need to lock anything, just clear the state
107+
self.futex.store(0, Ordering::Release);
108+
109+
UnparkHandle { futex: &self.futex }
110+
}
111+
}
112+
113+
// Handle for a thread that is about to be unparked. We need to mark the thread
114+
// as unparked while holding the queue lock, but we delay the actual unparking
115+
// until after the queue lock is released.
116+
pub struct UnparkHandle {
117+
futex: *const AtomicI32,
118+
}
119+
120+
impl UnparkHandle {
121+
// Wakes up the parked thread. This should be called after the queue lock is
122+
// released to avoid blocking the queue for too long.
123+
pub fn unpark(self) {
124+
// The thread data may have been freed at this point, but it doesn't
125+
// matter since the syscall will just return EFAULT in that case.
126+
let r =
127+
unsafe { libc::syscall(libc::SYS_futex, self.futex, FUTEX_WAKE | FUTEX_PRIVATE, 1) };
128+
debug_assert!(r == 0 || r == 1 || r == -1);
129+
if r == -1 {
130+
debug_assert_eq!(unsafe { *libc::__errno_location() }, libc::EFAULT);
131+
}
132+
}
133+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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+
// x32 Linux uses a non-standard type for tv_nsec in timespec.
9+
// See https://sourceware.org/bugzilla/show_bug.cgi?id=16437
10+
#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
11+
#[allow(non_camel_case_types)]
12+
type tv_nsec_t = i64;
13+
#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
14+
#[allow(non_camel_case_types)]
15+
type tv_nsec_t = libc::c_long;
16+
17+
#[cfg(target_os = "linux")]
18+
mod futex;
19+
#[cfg(target_os = "linux")]
20+
pub use self::futex::*;
21+
22+
23+
#[cfg(not(target_os = "linux"))]
24+
mod pthread;
25+
#[cfg(not(target_os = "linux"))]
26+
pub use self::pthread::*;

0 commit comments

Comments
 (0)