Skip to content

Remove Select structure; implement inline select! macro #17601

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 65 additions & 15 deletions src/libstd/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -350,8 +350,8 @@ macro_rules! vec(
/// spawn(proc() { tx2.send(calculate_the_answer()) });
///
/// select! (
/// () = rx1.recv() => println!("the long running task finished first"),
/// answer = rx2.recv() => {
/// _ from rx1 => println!("the long running task finished first"),
/// answer from rx2 => {
/// println!("the answer was: {}", answer);
/// }
/// )
Expand All @@ -360,21 +360,71 @@ macro_rules! vec(
/// For more information about select, see the `std::comm::Select` structure.
#[macro_export]
#[experimental]
macro_rules! select {
(
$($name:pat = $rx:ident.$meth:ident() => $code:expr),+
) => ({
use std::comm::Select;
let sel = Select::new();
$( let mut $rx = sel.handle(&$rx); )+
unsafe {
$( $rx.add(); )+
macro_rules! select(
($($name:pat from $rx:expr => $code:expr),+) => {
select!{ $($name from $rx using recv => $code),+ }
};
($($name:pat from $rx:expr using $meth:ident => $code:expr),+) => ({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I originally was somewhat wary of using syntax like this because it's pretty alien with respect to the rest of rust code. That being said it was unfortunate that $expr.$ident() didn't quite work out as I expected.

use std::rt::local::Local;
use std::rt::task::Task;
use std::comm::Packet;

// Is anything already ready to receive? Grab it without waiting.
$(
if (&$rx as &Packet).can_recv() {
let $name = $rx.$meth();
$code
}
)else+
else {
// Start selecting on as many as we need to before getting a bite.
// Keep count of how many, since we need to abort every selection
// that we started.
let mut started_count = 0;
// Restrict lifetime of borrows in `packets`
{
let packets = [ $( &$rx as &Packet, )+ ];

let task: Box<Task> = Local::take();
task.deschedule(packets.len(), |task| {
match packets[started_count].start_selection(task) {
Ok(()) => {
started_count += 1;
Ok(())
}
Err(task) => Err(task)
}
});
}

let mut i = 0;
let ret = $(
// Abort the receivers, stopping at the first ready one to get its data.
if { i += 1; i <= started_count } &&
// If start_selection() failed, abort_selection() will fail too,
// but it still counts as "data available".
($rx.abort_selection() || i == started_count) {
// React to the first
let $name = $rx.$meth();
$code
})else+
else {
unreachable!()
};

// At this point, the first i receivers have been aborted.
// We need to abort the rest:
$(if i > 0 {
i -= 1;
} else {
$rx.abort_selection();
})+
let _ = i; // Shut up `i -= 1 but i is never read` warning
// Return
ret
}
let ret = sel.wait();
$( if ret == $rx.id() { let $name = $rx.$meth(); $code } else )+
{ unreachable!() }
})
}
)

// When testing the standard library, we link to the liblog crate to get the
// logging macros. In doing so, the liblog crate was linked against the real
Expand Down
19 changes: 12 additions & 7 deletions src/libsync/comm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,8 @@
//!
//! loop {
//! select! {
//! val = rx.recv() => println!("Received {}", val),
//! () = timeout.recv() => {
//! val from rx => println!("Received {}", val),
//! _ from timeout => {
//! println!("timed out, total time was more than 10 seconds")
//! break;
//! }
Expand All @@ -162,8 +162,8 @@
//! let timeout = timer.oneshot(Duration::seconds(5));
//!
//! select! {
//! val = rx.recv() => println!("Received {}", val),
//! () = timeout.recv() => {
//! val from rx => println!("Received {}", val),
//! _ from timeout => {
//! println!("timed out, no message received in 5 seconds")
//! break;
//! }
Expand Down Expand Up @@ -332,7 +332,6 @@ use core::cell::UnsafeCell;
use rustrt::local::Local;
use rustrt::task::{Task, BlockedTask};

pub use comm::select::{Select, Handle};
pub use comm::duplex::{DuplexStream, duplex};

macro_rules! test (
Expand Down Expand Up @@ -363,7 +362,6 @@ macro_rules! test (

mod duplex;
mod oneshot;
mod select;
mod shared;
mod stream;
mod sync;
Expand Down Expand Up @@ -956,7 +954,14 @@ impl<T: Send> Receiver<T> {
}
}

impl<T: Send> select::Packet for Receiver<T> {
#[doc(hidden)]
pub trait Packet {
fn can_recv(&self) -> bool;
fn start_selection(&self, task: BlockedTask) -> Result<(), BlockedTask>;
fn abort_selection(&self) -> bool;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was actually very intentional that this was concretely never exposed from this implementation, and it would certainly be nice to keep it that way!


impl<T: Send> Packet for Receiver<T> {
fn can_recv(&self) -> bool {
loop {
let new_port = match *unsafe { self.inner() } {
Expand Down
Loading