Skip to content

Commit c15ac5c

Browse files
committed
Remove Select structure; implement inline select! macro
The old `Select` structure was very inefficient and required the user to use unsafe code (and manually maintain a "this variable will never move" invariant). Searching through several popular repositories, the only instance I found of it being used was in servo --- to avoid the setup cost of putting `select!` in a loop, which would create a new `Select` on every iteration. This commit deletes the `Select` structure entirely and moves all of its code into the `select!` macro. The benefit of this is that there is no more need for allocations, no more need for unsafe code, and no setup costs (so servo can now use `select!` directly). This also changes the interface to select! to fix rust-lang#12902. Fixes rust-lang#12902. [breaking-change]
1 parent 5d653c1 commit c15ac5c

File tree

5 files changed

+81
-737
lines changed

5 files changed

+81
-737
lines changed

src/libstd/macros.rs

Lines changed: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -350,8 +350,8 @@ macro_rules! vec(
350350
/// spawn(proc() { tx2.send(calculate_the_answer()) });
351351
///
352352
/// select! (
353-
/// () = rx1.recv() => println!("the long running task finished first"),
354-
/// answer = rx2.recv() => {
353+
/// _ from rx1 => println!("the long running task finished first"),
354+
/// answer from rx2 => {
355355
/// println!("the answer was: {}", answer);
356356
/// }
357357
/// )
@@ -360,21 +360,71 @@ macro_rules! vec(
360360
/// For more information about select, see the `std::comm::Select` structure.
361361
#[macro_export]
362362
#[experimental]
363-
macro_rules! select {
364-
(
365-
$($name:pat = $rx:ident.$meth:ident() => $code:expr),+
366-
) => ({
367-
use std::comm::Select;
368-
let sel = Select::new();
369-
$( let mut $rx = sel.handle(&$rx); )+
370-
unsafe {
371-
$( $rx.add(); )+
363+
macro_rules! select(
364+
($($name:pat from $rx:expr => $code:expr),+) => {
365+
select!{ $($name from $rx using recv => $code),+ }
366+
};
367+
($($name:pat from $rx:expr using $meth:ident => $code:expr),+) => ({
368+
use std::rt::local::Local;
369+
use std::rt::task::Task;
370+
use std::comm::Packet;
371+
372+
// Is anything already ready to receive? Grab it without waiting.
373+
$(
374+
if (&$rx as &Packet).can_recv() {
375+
let $name = $rx.$meth();
376+
$code
377+
}
378+
)else+
379+
else {
380+
// Start selecting on as many as we need to before getting a bite.
381+
// Keep count of how many, since we need to abort every selection
382+
// that we started.
383+
let mut started_count = 0;
384+
// Restrict lifetime of borrows in `packets`
385+
{
386+
let packets = [ $( &$rx as &Packet, )+ ];
387+
388+
let task: Box<Task> = Local::take();
389+
task.deschedule(packets.len(), |task| {
390+
match packets[started_count].start_selection(task) {
391+
Ok(()) => {
392+
started_count += 1;
393+
Ok(())
394+
}
395+
Err(task) => Err(task)
396+
}
397+
});
398+
}
399+
400+
let mut i = 0;
401+
let ret = $(
402+
// Abort the receivers, stopping at the first ready one to get its data.
403+
if { i += 1; i <= started_count } &&
404+
// If start_selection() failed, abort_selection() will fail too,
405+
// but it still counts as "data available".
406+
($rx.abort_selection() || i == started_count) {
407+
// React to the first
408+
let $name = $rx.$meth();
409+
$code
410+
})else+
411+
else {
412+
unreachable!()
413+
};
414+
415+
// At this point, the first i receivers have been aborted.
416+
// We need to abort the rest:
417+
$(if i > 0 {
418+
i -= 1;
419+
} else {
420+
$rx.abort_selection();
421+
})+
422+
let _ = i; // Shut up `i -= 1 but i is never read` warning
423+
// Return
424+
ret
372425
}
373-
let ret = sel.wait();
374-
$( if ret == $rx.id() { let $name = $rx.$meth(); $code } else )+
375-
{ unreachable!() }
376426
})
377-
}
427+
)
378428

379429
// When testing the standard library, we link to the liblog crate to get the
380430
// logging macros. In doing so, the liblog crate was linked against the real

src/libsync/comm/mod.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,8 @@
138138
//!
139139
//! loop {
140140
//! select! {
141-
//! val = rx.recv() => println!("Received {}", val),
142-
//! () = timeout.recv() => {
141+
//! val from rx => println!("Received {}", val),
142+
//! _ from timeout => {
143143
//! println!("timed out, total time was more than 10 seconds")
144144
//! break;
145145
//! }
@@ -162,8 +162,8 @@
162162
//! let timeout = timer.oneshot(Duration::seconds(5));
163163
//!
164164
//! select! {
165-
//! val = rx.recv() => println!("Received {}", val),
166-
//! () = timeout.recv() => {
165+
//! val from rx => println!("Received {}", val),
166+
//! _ from timeout => {
167167
//! println!("timed out, no message received in 5 seconds")
168168
//! break;
169169
//! }
@@ -332,7 +332,6 @@ use core::cell::UnsafeCell;
332332
use rustrt::local::Local;
333333
use rustrt::task::{Task, BlockedTask};
334334

335-
pub use comm::select::{Select, Handle};
336335
pub use comm::duplex::{DuplexStream, duplex};
337336

338337
macro_rules! test (
@@ -363,7 +362,6 @@ macro_rules! test (
363362

364363
mod duplex;
365364
mod oneshot;
366-
mod select;
367365
mod shared;
368366
mod stream;
369367
mod sync;
@@ -956,7 +954,14 @@ impl<T: Send> Receiver<T> {
956954
}
957955
}
958956

959-
impl<T: Send> select::Packet for Receiver<T> {
957+
#[doc(hidden)]
958+
pub trait Packet {
959+
fn can_recv(&self) -> bool;
960+
fn start_selection(&self, task: BlockedTask) -> Result<(), BlockedTask>;
961+
fn abort_selection(&self) -> bool;
962+
}
963+
964+
impl<T: Send> Packet for Receiver<T> {
960965
fn can_recv(&self) -> bool {
961966
loop {
962967
let new_port = match *unsafe { self.inner() } {

0 commit comments

Comments
 (0)