]> git.proxmox.com Git - rustc.git/blame - src/vendor/parking_lot_core/src/thread_parker/linux.rs
New upstream version 1.25.0+dfsg1
[rustc.git] / src / vendor / parking_lot_core / src / thread_parker / linux.rs
CommitLineData
ff7c6d11
XL
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
8use std::sync::atomic::{AtomicI32, Ordering};
9use std::time::Instant;
10use libc;
11
12const FUTEX_WAIT: i32 = 0;
13const FUTEX_WAKE: i32 = 1;
14const FUTEX_PRIVATE: i32 = 128;
15
16// Helper type for putting a thread to sleep until some other thread wakes it up
17pub struct ThreadParker {
18 futex: AtomicI32,
19}
20
21impl ThreadParker {
22 pub fn new() -> ThreadParker {
23 ThreadParker {
24 futex: AtomicI32::new(0),
25 }
26 }
27
28 // Prepares the parker. This should be called before adding it to the queue.
29 pub unsafe fn prepare_park(&self) {
30 self.futex.store(1, Ordering::Relaxed);
31 }
32
33 // Checks if the park timed out. This should be called while holding the
34 // queue lock after park_until has returned false.
35 pub unsafe fn timed_out(&self) -> bool {
36 self.futex.load(Ordering::Relaxed) != 0
37 }
38
39 // Parks the thread until it is unparked. This should be called after it has
40 // been added to the queue, after unlocking the queue.
41 pub unsafe fn park(&self) {
42 while self.futex.load(Ordering::Acquire) != 0 {
43 let r = libc::syscall(libc::SYS_futex, &self.futex, FUTEX_WAIT | FUTEX_PRIVATE, 1, 0);
44 debug_assert!(r == 0 || r == -1);
45 if r == -1 {
46 debug_assert!(
47 *libc::__errno_location() == libc::EINTR
48 || *libc::__errno_location() == libc::EAGAIN
49 );
50 }
51 }
52 }
53
54 // Parks the thread until it is unparked or the timeout is reached. This
55 // should be called after it has been added to the queue, after unlocking
56 // the queue. Returns true if we were unparked and false if we timed out.
57 pub unsafe fn park_until(&self, timeout: Instant) -> bool {
58 while self.futex.load(Ordering::Acquire) != 0 {
59 let now = Instant::now();
60 if timeout <= now {
61 return false;
62 }
63 let diff = timeout - now;
64 if diff.as_secs() as libc::time_t as u64 != diff.as_secs() {
65 // Timeout overflowed, just sleep indefinitely
66 self.park();
67 return true;
68 }
69 let ts = libc::timespec {
70 tv_sec: diff.as_secs() as libc::time_t,
71 tv_nsec: diff.subsec_nanos() as libc::c_long,
72 };
73 let r = libc::syscall(libc::SYS_futex, &self.futex, FUTEX_WAIT | FUTEX_PRIVATE, 1, &ts);
74 debug_assert!(r == 0 || r == -1);
75 if r == -1 {
76 debug_assert!(
77 *libc::__errno_location() == libc::EINTR
78 || *libc::__errno_location() == libc::EAGAIN
79 || *libc::__errno_location() == libc::ETIMEDOUT
80 );
81 }
82 }
83 true
84 }
85
86 // Locks the parker to prevent the target thread from exiting. This is
87 // necessary to ensure that thread-local ThreadData objects remain valid.
88 // This should be called while holding the queue lock.
89 pub unsafe fn unpark_lock(&self) -> UnparkHandle {
90 // We don't need to lock anything, just clear the state
91 self.futex.store(0, Ordering::Release);
92
93 UnparkHandle { futex: &self.futex }
94 }
95}
96
97// Handle for a thread that is about to be unparked. We need to mark the thread
98// as unparked while holding the queue lock, but we delay the actual unparking
99// until after the queue lock is released.
100pub struct UnparkHandle {
101 futex: *const AtomicI32,
102}
103
104impl UnparkHandle {
105 // Wakes up the parked thread. This should be called after the queue lock is
106 // released to avoid blocking the queue for too long.
107 pub unsafe fn unpark(self) {
108 // The thread data may have been freed at this point, but it doesn't
109 // matter since the syscall will just return EFAULT in that case.
110 let r = libc::syscall(libc::SYS_futex, self.futex, FUTEX_WAKE | FUTEX_PRIVATE, 1);
111 debug_assert!(r == 0 || r == 1 || r == -1);
112 if r == -1 {
113 debug_assert_eq!(*libc::__errno_location(), libc::EFAULT);
114 }
115 }
116}