Skip to content

Commit 565b234

Browse files
committed
condvar backoffs for thread with large RT priority delta
1 parent 7397fba commit 565b234

File tree

1 file changed

+45
-5
lines changed

1 file changed

+45
-5
lines changed

src/pi.rs

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::{
2-
sync::atomic::{AtomicI32, AtomicU32, Ordering},
2+
sync::atomic::{AtomicI32, AtomicU32, Ordering, self},
33
thread,
44
time::{Duration, Instant},
55
};
@@ -74,8 +74,12 @@ impl Condvar {
7474
"CRITICAL: too many waiters"
7575
);
7676
let mutex = unsafe { lock_api::MutexGuard::<'_, PiLock, T>::mutex(mutex_guard).raw() };
77-
if mutex.is_locked() {
78-
mutex.perform_unlock();
77+
macro_rules! unlock {
78+
() => {
79+
if mutex.is_locked() {
80+
mutex.perform_unlock();
81+
}
82+
};
7983
}
8084
let fx: &Futex<Private> = self.fx.as_futex();
8185
let result = if let Some(timeout) = timeout {
@@ -84,6 +88,8 @@ impl Condvar {
8488
let Some(remaining) = timeout.checked_sub(now.elapsed()) else {
8589
break WaitTimeoutResult { timed_out: true };
8690
};
91+
unlock!();
92+
//atomic::fence(Ordering::SeqCst);
8793
match fx.wait_for(0, remaining) {
8894
Ok(()) => break WaitTimeoutResult { timed_out: false },
8995
Err(TimedWaitError::TimedOut) => break WaitTimeoutResult { timed_out: true },
@@ -93,6 +99,8 @@ impl Condvar {
9399
}
94100
} else {
95101
loop {
102+
unlock!();
103+
//atomic::fence(Ordering::SeqCst);
96104
match fx.wait(0) {
97105
Ok(()) => break WaitTimeoutResult { timed_out: false },
98106
Err(WaitError::Interrupted) => continue,
@@ -108,25 +116,57 @@ impl Condvar {
108116
/// Notifies one thread waiting on this condvar.
109117
pub fn notify_one(&self) {
110118
let fx: &Futex<Private> = self.fx.as_futex();
119+
let op_start = Instant::now();
120+
let mut backoff = Backoff::new();
111121
while self.waiters.load(Ordering::SeqCst) > 0 && fx.wake(1) == 0 {
112122
// there is a chance that some waiter has not been entered into the futex yet, waiting
113123
// for it in a tiny spin loop
114-
thread::yield_now();
124+
backoff.backoff();
115125
}
126+
println!("notify_one took {:?} ({})", op_start.elapsed(), backoff.c);
116127
}
117128

118129
/// Notifies all threads waiting on this condvar.
119130
pub fn notify_all(&self) {
120131
let fx: &Futex<Private> = self.fx.as_futex();
132+
let mut backoff = Backoff::new();
121133
loop {
122134
let to_wake = self.waiters.load(Ordering::SeqCst);
123135
if to_wake == 0 || fx.wake(to_wake) == to_wake {
124136
break;
125137
}
126138
// there is a chance that some waiter has not been entered into the futex yet, waiting
127139
// for it in a tiny spin loop
128-
thread::yield_now();
140+
backoff.backoff();
141+
}
142+
}
143+
}
144+
145+
struct Backoff {
146+
n: u32,
147+
c: u32,
148+
}
149+
150+
impl Backoff {
151+
fn new() -> Self {
152+
Self { n: 0, c: 0 }
153+
}
154+
155+
fn backoff(&mut self) {
156+
if self.n == 0 {
157+
// first iteration = spin
158+
for _ in 0..10 {
159+
thread::yield_now();
160+
}
161+
} else {
162+
// otherwise sleep
163+
thread::sleep(Duration::from_micros(self.n.into()));
164+
}
165+
if self.n < 100 {
166+
// max sleep time = 100us
167+
self.n += 50;
129168
}
169+
self.c += 1;
130170
}
131171
}
132172

0 commit comments

Comments
 (0)