core::rt: Add a simple channel type for passing buffered messages between Scheduler and Task

Called 'Tube' for lack of anything better.
This commit is contained in:
Brian Anderson 2013-05-04 17:30:31 -07:00
parent 40a9de5ebc
commit 414f3c7d25
4 changed files with 212 additions and 8 deletions

View File

@ -19,6 +19,7 @@
use repr;
use vec;
use cast;
use str;
/// Turns on logging to stdout globally
pub fn console_on() {
@ -57,7 +58,7 @@ pub fn log_type<T>(level: u32, object: &T) {
}
_ => {
// XXX: Bad allocation
let msg = bytes.to_str();
let msg = str::from_bytes(bytes);
newsched_log_str(msg);
}
}

View File

@ -18,7 +18,7 @@
mod sched;
/// Thread-local access to the current Scheduler
mod local_sched;
pub mod local_sched;
/// Synchronous I/O
#[path = "io/mod.rs"]
@ -68,6 +68,10 @@
/// Reference counting
pub mod rc;
/// A simple single-threaded channel type for passing buffered data between
/// scheduler and task context
pub mod tube;
/// Set up a default runtime configuration, given compiler-supplied arguments.
///
/// This is invoked by the `start` _language item_ (unstable::lang) to

182
src/libcore/rt/tube.rs Normal file
View File

@ -0,0 +1,182 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! A very simple unsynchronized channel type for sending buffered data from
//! scheduler context to task context.
//!
//! XXX: This would be safer to use if split into two types like Port/Chan
use option::*;
use clone::Clone;
use super::rc::RC;
use rt::sched::Task;
use rt::{context, TaskContext, SchedulerContext};
use rt::local_sched;
struct TubeState<T> {
blocked_task: Option<~Task>,
buf: ~[T]
}
pub struct Tube<T> {
p: RC<TubeState<T>>
}
impl<T> Tube<T> {
pub fn new() -> Tube<T> {
Tube {
p: RC::new(TubeState {
blocked_task: None,
buf: ~[]
})
}
}
pub fn send(&mut self, val: T) {
rtdebug!("tube send");
assert!(context() == SchedulerContext);
unsafe {
let state = self.p.unsafe_borrow_mut();
(*state).buf.push(val);
if (*state).blocked_task.is_some() {
// There's a waiting task. Wake it up
rtdebug!("waking blocked tube");
let task = (*state).blocked_task.swap_unwrap();
let sched = local_sched::take();
sched.resume_task_immediately(task);
}
}
}
pub fn recv(&mut self) -> T {
assert!(context() == TaskContext);
unsafe {
let state = self.p.unsafe_borrow_mut();
if !(*state).buf.is_empty() {
return (*state).buf.shift();
} else {
// Block and wait for the next message
rtdebug!("blocking on tube recv");
assert!(self.p.refcount() > 1); // There better be somebody to wake us up
assert!((*state).blocked_task.is_none());
let sched = local_sched::take();
do sched.deschedule_running_task_and_then |task| {
(*state).blocked_task = Some(task);
}
rtdebug!("waking after tube recv");
let buf = &mut (*state).buf;
assert!(!buf.is_empty());
return buf.shift();
}
}
}
}
impl<T> Clone for Tube<T> {
fn clone(&self) -> Tube<T> {
Tube { p: self.p.clone() }
}
}
#[cfg(test)]
mod test {
use int;
use cell::Cell;
use rt::local_sched;
use rt::test::*;
use rt::rtio::EventLoop;
use super::*;
#[test]
fn simple_test() {
do run_in_newsched_task {
let mut tube: Tube<int> = Tube::new();
let tube_clone = tube.clone();
let tube_clone_cell = Cell(tube_clone);
let sched = local_sched::take();
do sched.deschedule_running_task_and_then |task| {
let mut tube_clone = tube_clone_cell.take();
tube_clone.send(1);
let sched = local_sched::take();
sched.resume_task_immediately(task);
}
assert!(tube.recv() == 1);
}
}
#[test]
fn blocking_test() {
do run_in_newsched_task {
let mut tube: Tube<int> = Tube::new();
let tube_clone = tube.clone();
let tube_clone = Cell(Cell(Cell(tube_clone)));
let sched = local_sched::take();
do sched.deschedule_running_task_and_then |task| {
let tube_clone = tube_clone.take();
do local_sched::borrow |sched| {
let tube_clone = tube_clone.take();
do sched.event_loop.callback {
let mut tube_clone = tube_clone.take();
// The task should be blocked on this now and
// sending will wake it up.
tube_clone.send(1);
}
}
let sched = local_sched::take();
sched.resume_task_immediately(task);
}
assert!(tube.recv() == 1);
}
}
#[test]
fn many_blocking_test() {
static MAX: int = 100;
do run_in_newsched_task {
let mut tube: Tube<int> = Tube::new();
let tube_clone = tube.clone();
let tube_clone = Cell(tube_clone);
let sched = local_sched::take();
do sched.deschedule_running_task_and_then |task| {
callback_send(tube_clone.take(), 0);
fn callback_send(tube: Tube<int>, i: int) {
if i == 100 { return; }
let tube = Cell(Cell(tube));
do local_sched::borrow |sched| {
let tube = tube.take();
do sched.event_loop.callback {
let mut tube = tube.take();
// The task should be blocked on this now and
// sending will wake it up.
tube.send(i);
callback_send(tube, i + 1);
}
}
}
let sched = local_sched::take();
sched.resume_task_immediately(task);
}
for int::range(0, MAX) |i| {
let j = tube.recv();
assert!(j == i);
}
}
}
}

View File

@ -202,10 +202,12 @@ fn fail_with(cause: &'static str, file: &'static str, line: uint) -> ! {
// FIXME #4427: Temporary until rt::rt_fail_ goes away
pub fn begin_unwind_(msg: *c_char, file: *c_char, line: size_t) -> ! {
use rt::{context, OldTaskContext};
use rt::local_services::unsafe_borrow_local_services;
use option::Option;
use rt::{context, OldTaskContext, TaskContext};
use rt::local_services::{unsafe_borrow_local_services, Unwinder};
match context() {
let context = context();
match context {
OldTaskContext => {
unsafe {
gc::cleanup_stack_for_failure();
@ -214,11 +216,26 @@ pub fn begin_unwind_(msg: *c_char, file: *c_char, line: size_t) -> ! {
}
}
_ => {
// XXX: Need to print the failure message
gc::cleanup_stack_for_failure();
unsafe {
// XXX: Bad re-allocations. fail! needs some refactoring
let msg = str::raw::from_c_str(msg);
let file = str::raw::from_c_str(file);
let outmsg = fmt!("%s at line %i of file %s", msg, line as int, file);
// XXX: Logging doesn't work correctly in non-task context because it
// invokes the local heap
if context == TaskContext {
error!(outmsg);
} else {
rtdebug!("%s", outmsg);
}
gc::cleanup_stack_for_failure();
let local_services = unsafe_borrow_local_services();
match (*local_services).unwinder {
let unwinder: &mut Option<Unwinder> = &mut (*local_services).unwinder;
match *unwinder {
Some(ref mut unwinder) => unwinder.begin_unwind(),
None => abort!("failure without unwinder. aborting process")
}