Skip to content

Commit 0fff8b6

Browse files
committed
std::rt: Reduce MessageQueue contention
It's not a huge win but it does reduce the amount of time spent contesting the message queue when the schedulers are under load
1 parent 5d04234 commit 0fff8b6

File tree

2 files changed

+69
-12
lines changed

2 files changed

+69
-12
lines changed

src/libstd/rt/message_queue.rs

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,66 @@ use kinds::Send;
1616
use vec::OwnedVector;
1717
use cell::Cell;
1818
use option::*;
19-
use unstable::sync::Exclusive;
19+
use unstable::sync::{UnsafeAtomicRcBox, LittleLock};
2020
use clone::Clone;
2121

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

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

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

4054
pub fn pop(&mut self) -> Option<T> {
4155
unsafe {
42-
do self.queue.with |q| {
43-
if !q.is_empty() {
44-
Some(q.shift())
56+
let state = self.state.get();
57+
do (*state).lock.lock {
58+
if !(*state).queue.is_empty() {
59+
(*state).count += 1;
60+
Some((*state).queue.shift())
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<T> {
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).queue.is_empty() {
77+
(*state).count += 1;
78+
Some((*state).queue.shift())
4579
} else {
4680
None
4781
}
@@ -53,7 +87,7 @@ impl<T: Send> MessageQueue<T> {
5387
impl<T: Send> Clone for MessageQueue<T> {
5488
fn clone(&self) -> MessageQueue<T> {
5589
MessageQueue {
56-
queue: self.queue.clone()
90+
state: self.state.clone()
5791
}
5892
}
5993
}

src/libstd/rt/sched.rs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,14 @@ pub struct Scheduler {
8383
idle_callback: Option<~PausibleIdleCallback>
8484
}
8585

86+
/// An indication of how hard to work on a given operation, the difference
87+
/// mainly being whether memory is synchronized or not
88+
#[deriving(Eq)]
89+
enum EffortLevel {
90+
DontTryTooHard,
91+
GiveItYourBest
92+
}
93+
8694
impl Scheduler {
8795

8896
// * Initialization Functions
@@ -237,14 +245,21 @@ impl Scheduler {
237245

238246
// First we check for scheduler messages, these are higher
239247
// priority than regular tasks.
240-
let sched = match sched.interpret_message_queue() {
248+
let sched = match sched.interpret_message_queue(DontTryTooHard) {
241249
Some(sched) => sched,
242250
None => return
243251
};
244252

245253
// This helper will use a randomized work-stealing algorithm
246254
// to find work.
247-
let mut sched = match sched.do_work() {
255+
let sched = match sched.do_work() {
256+
Some(sched) => sched,
257+
None => return
258+
};
259+
260+
// Now, before sleeping we need to find out if there really
261+
// were any messages. Give it your best!
262+
let mut sched = match sched.interpret_message_queue(GiveItYourBest) {
248263
Some(sched) => sched,
249264
None => return
250265
};
@@ -277,10 +292,18 @@ impl Scheduler {
277292
// returns the still-available scheduler. At this point all
278293
// message-handling will count as a turn of work, and as a result
279294
// return None.
280-
fn interpret_message_queue(~self) -> Option<~Scheduler> {
295+
fn interpret_message_queue(~self, effort: EffortLevel) -> Option<~Scheduler> {
281296

282297
let mut this = self;
283-
match this.message_queue.pop() {
298+
299+
let msg = if effort == DontTryTooHard {
300+
// Do a cheap check that may miss messages
301+
this.message_queue.casual_pop()
302+
} else {
303+
this.message_queue.pop()
304+
};
305+
306+
match msg {
284307
Some(PinnedTask(task)) => {
285308
let mut task = task;
286309
task.give_home(Sched(this.make_handle()));

0 commit comments

Comments
 (0)