Skip to content

Commit 5d04234

Browse files
committed
std::rt: Reduce SleeperList contention
This makes the lock much less contended. In the test I'm running the number of times it's contended goes from ~100000 down to ~1000.
1 parent 5bcb639 commit 5d04234

File tree

2 files changed

+44
-12
lines changed

2 files changed

+44
-12
lines changed

src/libstd/rt/sched.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -469,10 +469,7 @@ impl Scheduler {
469469
// We've made work available. Notify a
470470
// sleeping scheduler.
471471

472-
// XXX: perf. Check for a sleeper without
473-
// synchronizing memory. It's not critical
474-
// that we always find it.
475-
match this.sleeper_list.pop() {
472+
match this.sleeper_list.casual_pop() {
476473
Some(handle) => {
477474
let mut handle = handle;
478475
handle.send(Wake)

src/libstd/rt/sleeper_list.rs

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,33 +15,68 @@ use container::Container;
1515
use vec::OwnedVector;
1616
use option::{Option, Some, None};
1717
use cell::Cell;
18-
use unstable::sync::Exclusive;
18+
use unstable::sync::{UnsafeAtomicRcBox, LittleLock};
1919
use rt::sched::SchedHandle;
2020
use clone::Clone;
2121

2222
pub struct SleeperList {
23-
priv stack: Exclusive<~[SchedHandle]>
23+
priv state: UnsafeAtomicRcBox<State>
24+
}
25+
26+
struct State {
27+
count: uint,
28+
stack: ~[SchedHandle],
29+
lock: LittleLock
2430
}
2531

2632
impl SleeperList {
2733
pub fn new() -> SleeperList {
2834
SleeperList {
29-
stack: Exclusive::new(~[])
35+
state: UnsafeAtomicRcBox::new(State {
36+
count: 0,
37+
stack: ~[],
38+
lock: LittleLock::new()
39+
})
3040
}
3141
}
3242

3343
pub fn push(&mut self, handle: SchedHandle) {
3444
let handle = Cell::new(handle);
3545
unsafe {
36-
self.stack.with(|s| s.push(handle.take()));
46+
let state = self.state.get();
47+
do (*state).lock.lock {
48+
(*state).count += 1;
49+
(*state).stack.push(handle.take());
50+
}
3751
}
3852
}
3953

4054
pub fn pop(&mut self) -> Option<SchedHandle> {
4155
unsafe {
42-
do self.stack.with |s| {
43-
if !s.is_empty() {
44-
Some(s.pop())
56+
let state = self.state.get();
57+
do (*state).lock.lock {
58+
if !(*state).stack.is_empty() {
59+
(*state).count -= 1;
60+
Some((*state).stack.pop())
61+
} else {
62+
None
63+
}
64+
}
65+
}
66+
}
67+
68+
/// A pop that may sometimes miss enqueued elements, but is much faster
69+
/// to give up without doing any synchronization
70+
pub fn casual_pop(&mut self) -> Option<SchedHandle> {
71+
unsafe {
72+
let state = self.state.get();
73+
// NB: Unsynchronized check
74+
if (*state).count == 0 { return None; }
75+
do (*state).lock.lock {
76+
if !(*state).stack.is_empty() {
77+
// NB: count is also protected by the lock
78+
(*state).count -= 1;
79+
Some((*state).stack.pop())
4580
} else {
4681
None
4782
}
@@ -53,7 +88,7 @@ impl SleeperList {
5388
impl Clone for SleeperList {
5489
fn clone(&self) -> SleeperList {
5590
SleeperList {
56-
stack: self.stack.clone()
91+
state: self.state.clone()
5792
}
5893
}
5994
}

0 commit comments

Comments
 (0)