Skip to content

Commit fd76552

Browse files
committed
std: use an event flag based thread parker on SOLID
1 parent 97d48be commit fd76552

File tree

5 files changed

+214
-6
lines changed

5 files changed

+214
-6
lines changed

library/std/src/sys/itron/abi.rs

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,32 @@ pub type ER = int_t;
3030
/// Error code type, `ID` on success
3131
pub type ER_ID = int_t;
3232

33+
/// Service call operational mode
34+
pub type MODE = uint_t;
35+
36+
/// OR waiting condition for an eventflag
37+
pub const TWF_ORW: MODE = 0x01;
38+
39+
/// Object attributes
40+
pub type ATR = uint_t;
41+
42+
/// FIFO wait order
43+
pub const TA_FIFO: ATR = 0;
44+
/// Only one task is allowed to be in the waiting state for the eventflag
45+
pub const TA_WSGL: ATR = 0;
46+
/// The eventflag’s bit pattern is cleared when a task is released from the
47+
/// waiting state for that eventflag.
48+
pub const TA_CLR: ATR = 0x04;
49+
50+
/// Bit pattern of an eventflag
51+
pub type FLGPTN = uint_t;
52+
3353
/// Task or interrupt priority
3454
pub type PRI = int_t;
3555

3656
/// The special value of `PRI` representing the current task's priority.
3757
pub const TPRI_SELF: PRI = 0;
3858

39-
/// Object attributes
40-
pub type ATR = uint_t;
41-
4259
/// Use the priority inheritance protocol
4360
#[cfg(target_os = "solid_asp3")]
4461
pub const TA_INHERIT: ATR = 0x02;
@@ -90,6 +107,13 @@ pub struct T_CSEM {
90107
pub maxsem: uint_t,
91108
}
92109

110+
#[derive(Clone, Copy)]
111+
#[repr(C)]
112+
pub struct T_CFLG {
113+
pub flgatr: ATR,
114+
pub iflgptn: FLGPTN,
115+
}
116+
93117
#[derive(Clone, Copy)]
94118
#[repr(C)]
95119
pub struct T_CMTX {
@@ -139,6 +163,24 @@ extern "C" {
139163
pub fn sns_dsp() -> bool_t;
140164
#[link_name = "__asp3_get_tim"]
141165
pub fn get_tim(p_systim: *mut SYSTIM) -> ER;
166+
#[link_name = "__asp3_acre_flg"]
167+
pub fn acre_flg(pk_cflg: *const T_CFLG) -> ER_ID;
168+
#[link_name = "__asp3_del_flg"]
169+
pub fn del_flg(flgid: ID) -> ER;
170+
#[link_name = "__asp3_set_flg"]
171+
pub fn set_flg(flgid: ID, setptn: FLGPTN) -> ER;
172+
#[link_name = "__asp3_clr_flg"]
173+
pub fn clr_flg(flgid: ID, clrptn: FLGPTN) -> ER;
174+
#[link_name = "__asp3_wai_flg"]
175+
pub fn wai_flg(flgid: ID, waiptn: FLGPTN, wfmode: MODE, p_flgptn: *mut FLGPTN) -> ER;
176+
#[link_name = "__asp3_twai_flg"]
177+
pub fn twai_flg(
178+
flgid: ID,
179+
waiptn: FLGPTN,
180+
wfmode: MODE,
181+
p_flgptn: *mut FLGPTN,
182+
tmout: TMO,
183+
) -> ER;
142184
#[link_name = "__asp3_acre_mtx"]
143185
pub fn acre_mtx(pk_cmtx: *const T_CMTX) -> ER_ID;
144186
#[link_name = "__asp3_del_mtx"]
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
use crate::mem::MaybeUninit;
2+
use crate::time::Duration;
3+
4+
use super::{
5+
abi,
6+
error::{expect_success, fail},
7+
time::with_tmos,
8+
};
9+
10+
const CLEAR: abi::FLGPTN = 0;
11+
const RAISED: abi::FLGPTN = 1;
12+
13+
/// A thread parking primitive that is not susceptible to race conditions,
14+
/// but provides no atomic ordering guarantees and allows only one `raise` per wait.
15+
pub struct WaitFlag {
16+
flag: abi::ID,
17+
}
18+
19+
impl WaitFlag {
20+
/// Creates a new wait flag.
21+
pub fn new() -> WaitFlag {
22+
let flag = expect_success(
23+
unsafe {
24+
abi::acre_flg(&abi::T_CFLG {
25+
flgatr: abi::TA_FIFO | abi::TA_WSGL | abi::TA_CLR,
26+
iflgptn: CLEAR,
27+
})
28+
},
29+
&"acre_flg",
30+
);
31+
32+
WaitFlag { flag }
33+
}
34+
35+
/// Wait for the wait flag to be raised.
36+
pub fn wait(&self) {
37+
let mut token = MaybeUninit::uninit();
38+
expect_success(
39+
unsafe { abi::wai_flg(self.flag, RAISED, abi::TWF_ORW, token.as_mut_ptr()) },
40+
&"wai_flg",
41+
);
42+
}
43+
44+
/// Wait for the wait flag to be raised or the timeout to occur.
45+
pub fn wait_timeout(&self, dur: Duration) {
46+
let mut token = MaybeUninit::uninit();
47+
let er = with_tmos(dur, |tmout| unsafe {
48+
abi::twai_flg(self.flag, RAISED, abi::TWF_ORW, token.as_mut_ptr(), tmout)
49+
});
50+
if er != abi::E_OK && er != abi::E_TMOUT {
51+
fail(er, &"twai_flg");
52+
}
53+
}
54+
55+
/// Raise the wait flag.
56+
///
57+
/// Calls to this function should be balanced with the number of successful waits.
58+
pub fn raise(&self) {
59+
expect_success(unsafe { abi::set_flg(self.flag, RAISED) }, &"set_flg");
60+
}
61+
}
62+
63+
impl Drop for WaitFlag {
64+
fn drop(&mut self) {
65+
expect_success(unsafe { abi::del_flg(self.flag) }, &"del_flg");
66+
}
67+
}

library/std/src/sys/solid/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ mod itron {
1515
pub mod thread;
1616
pub(super) mod time;
1717
use super::unsupported;
18+
pub mod wait_flag;
1819
}
1920

2021
pub mod alloc;
@@ -43,6 +44,7 @@ pub mod memchr;
4344
pub mod thread_local_dtor;
4445
pub mod thread_local_key;
4546
pub mod time;
47+
pub use self::itron::wait_flag;
4648

4749
mod rwlock;
4850

library/std/src/sys_common/thread_parker/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ cfg_if::cfg_if! {
99
))] {
1010
mod futex;
1111
pub use futex::Parker;
12-
} else if #[cfg(windows)] {
13-
pub use crate::sys::thread_parker::Parker;
14-
} else if #[cfg(target_family = "unix")] {
12+
} else if #[cfg(target_os = "solid_asp3")] {
13+
mod wait_flag;
14+
pub use wait_flag::Parker;
15+
} else if #[cfg(any(windows, target_family = "unix"))] {
1516
pub use crate::sys::thread_parker::Parker;
1617
} else {
1718
mod generic;
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
//! A wait-flag-based thread parker.
2+
//!
3+
//! Some operating systems provide low-level parking primitives like wait counts,
4+
//! event flags or semaphores which are not susceptible to race conditions (meaning
5+
//! the wakeup can occure before the wait operation). To implement the `std` thread
6+
//! parker on top of these primitives, we only have to ensure that parking is fast
7+
//! when the thread token is available, the atomic ordering guarantees are maintained
8+
//! and spurious wakeups are minimized.
9+
//!
10+
//! To achieve this, this parker uses an atomic variable with three states: `EMPTY`,
11+
//! `PARKED` and `NOTIFIED`:
12+
//! * `EMPTY` means the token has not been made available, but the thread is not
13+
//! currently waiting on it.
14+
//! * `PARKED` means the token is not available and the thread is parked.
15+
//! * `NOTIFIED` means the token is available.
16+
//!
17+
//! `park` and `park_timeout` change the state from `EMPTY` to `PARKED` and from
18+
//! `NOTIFIED` to `EMPTY`. If the state was `NOTIFIED`, the thread was unparked and
19+
//! execution can continue without calling into the OS. If the state was `EMPTY`,
20+
//! the token is not available and the thread waits on the primitive (here called
21+
//! "wait flag").
22+
//!
23+
//! `unpark` changes the state to `NOTIFIED`. If the state was `PARKED`, the thread
24+
//! is or will be sleeping on the wait flag, so we raise it. Only the first thread
25+
//! to call `unpark` will raise the wait flag, so spurious wakeups are avoided
26+
//! (this is especially important for semaphores).
27+
28+
use crate::pin::Pin;
29+
use crate::sync::atomic::AtomicI8;
30+
use crate::sync::atomic::Ordering::SeqCst;
31+
use crate::sys::wait_flag::WaitFlag;
32+
use crate::time::Duration;
33+
34+
const EMPTY: i8 = 0;
35+
const PARKED: i8 = -1;
36+
const NOTIFIED: i8 = 1;
37+
38+
pub struct Parker {
39+
state: AtomicI8,
40+
wait_flag: WaitFlag,
41+
}
42+
43+
impl Parker {
44+
/// Construct a parker for the current thread. The UNIX parker
45+
/// implementation requires this to happen in-place.
46+
pub unsafe fn new(parker: *mut Parker) {
47+
parker.write(Parker { state: AtomicI8::new(EMPTY), wait_flag: WaitFlag::new() })
48+
}
49+
50+
// This implementation doesn't require `unsafe` and `Pin`, but other implementations do.
51+
pub unsafe fn park(self: Pin<&Self>) {
52+
// The state values are chosen so that this subtraction changes
53+
// `NOTIFIED` to `EMPTY` and `EMPTY` to `PARKED`.
54+
let state = self.state.fetch_sub(1, SeqCst);
55+
match state {
56+
EMPTY => (),
57+
NOTIFIED => return,
58+
_ => panic!("inconsistent park state"),
59+
}
60+
61+
self.wait_flag.wait();
62+
63+
// We need to do a load here to use `Acquire` ordering.
64+
self.state.swap(EMPTY, SeqCst);
65+
}
66+
67+
// This implementation doesn't require `unsafe` and `Pin`, but other implementations do.
68+
pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
69+
let state = self.state.fetch_sub(1, SeqCst);
70+
match state {
71+
EMPTY => (),
72+
NOTIFIED => return,
73+
_ => panic!("inconsistent park state"),
74+
}
75+
76+
self.wait_flag.wait_timeout(dur);
77+
let state = self.state.swap(EMPTY, SeqCst);
78+
if state == NOTIFIED {
79+
// The token was made available after the timeout occurred, but before
80+
// we reset the state, so we need to reset the wait flag to avoid
81+
// spurious wakeups. This wait has no timeout, but we know it will
82+
// return quickly, as the unparking thread will definitely raise the
83+
// flag if it has not already done so.
84+
self.wait_flag.wait();
85+
}
86+
}
87+
88+
// This implementation doesn't require `Pin`, but other implementations do.
89+
pub fn unpark(self: Pin<&Self>) {
90+
let state = self.state.swap(NOTIFIED, SeqCst);
91+
92+
if state == PARKED {
93+
self.wait_flag.raise();
94+
}
95+
}
96+
}

0 commit comments

Comments
 (0)