Revamp TaskBuilder API
This patch consolidates and cleans up the task spawning APIs: * Removes the problematic `future_result` method from `std::task::TaskBuilder`, and adds a `try_future` that both spawns the task and returns a future representing its eventual result (or failure). * Removes the public `opts` field from `TaskBuilder`, instead adding appropriate builder methods to configure the task. * Adds extension traits to libgreen and libnative that add methods to `TaskBuilder` for spawning the task as a green or native thread. Previously, there was no way to benefit from the `TaskBuilder` functionality and also set the scheduler to spawn within. With this change, all task spawning scenarios are supported through the `TaskBuilder` interface. Closes #3725. [breaking-change]
This commit is contained in:
parent
8e9e17d188
commit
a23511a65d
5 changed files with 619 additions and 420 deletions
|
@ -159,16 +159,19 @@
|
|||
//!
|
||||
//! # Using a scheduler pool
|
||||
//!
|
||||
//! This library adds a `GreenTaskBuilder` trait that extends the methods
|
||||
//! available on `std::task::TaskBuilder` to allow spawning a green task,
|
||||
//! possibly pinned to a particular scheduler thread:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use std::rt::task::TaskOpts;
|
||||
//! use green::{SchedPool, PoolConfig};
|
||||
//! use green::sched::{PinnedTask, TaskFromFriend};
|
||||
//! use std::task::TaskBuilder;
|
||||
//! use green::{SchedPool, PoolConfig, GreenTaskBuilder};
|
||||
//!
|
||||
//! let config = PoolConfig::new();
|
||||
//! let mut pool = SchedPool::new(config);
|
||||
//!
|
||||
//! // Spawn tasks into the pool of schedulers
|
||||
//! pool.spawn(TaskOpts::new(), proc() {
|
||||
//! TaskBuilder::new().green(&mut pool).spawn(proc() {
|
||||
//! // this code is running inside the pool of schedulers
|
||||
//!
|
||||
//! spawn(proc() {
|
||||
|
@ -181,12 +184,9 @@
|
|||
//! let mut handle = pool.spawn_sched();
|
||||
//!
|
||||
//! // Pin a task to the spawned scheduler
|
||||
//! let task = pool.task(TaskOpts::new(), proc() { /* ... */ });
|
||||
//! handle.send(PinnedTask(task));
|
||||
//!
|
||||
//! // Schedule a task on this new scheduler
|
||||
//! let task = pool.task(TaskOpts::new(), proc() { /* ... */ });
|
||||
//! handle.send(TaskFromFriend(task));
|
||||
//! TaskBuilder::new().green_pinned(&mut pool, &mut handle).spawn(proc() {
|
||||
//! /* ... */
|
||||
//! });
|
||||
//!
|
||||
//! // Handles keep schedulers alive, so be sure to drop all handles before
|
||||
//! // destroying the sched pool
|
||||
|
@ -209,6 +209,8 @@
|
|||
// NB this does *not* include globs, please keep it that way.
|
||||
#![feature(macro_rules, phase)]
|
||||
#![allow(visible_private_types)]
|
||||
#![allow(deprecated)]
|
||||
#![feature(default_type_params)]
|
||||
|
||||
#[cfg(test)] #[phase(plugin, link)] extern crate log;
|
||||
#[cfg(test)] extern crate rustuv;
|
||||
|
@ -224,8 +226,9 @@ use std::rt::task::TaskOpts;
|
|||
use std::rt;
|
||||
use std::sync::atomics::{SeqCst, AtomicUint, INIT_ATOMIC_UINT};
|
||||
use std::sync::deque;
|
||||
use std::task::{TaskBuilder, Spawner};
|
||||
|
||||
use sched::{Shutdown, Scheduler, SchedHandle, TaskFromFriend, NewNeighbor};
|
||||
use sched::{Shutdown, Scheduler, SchedHandle, TaskFromFriend, PinnedTask, NewNeighbor};
|
||||
use sleeper_list::SleeperList;
|
||||
use stack::StackPool;
|
||||
use task::GreenTask;
|
||||
|
@ -444,6 +447,7 @@ impl SchedPool {
|
|||
/// This is useful to create a task which can then be sent to a specific
|
||||
/// scheduler created by `spawn_sched` (and possibly pin it to that
|
||||
/// scheduler).
|
||||
#[deprecated = "use the green and green_pinned methods of GreenTaskBuilder instead"]
|
||||
pub fn task(&mut self, opts: TaskOpts, f: proc():Send) -> Box<GreenTask> {
|
||||
GreenTask::configure(&mut self.stack_pool, opts, f)
|
||||
}
|
||||
|
@ -454,6 +458,7 @@ impl SchedPool {
|
|||
/// New tasks are spawned in a round-robin fashion to the schedulers in this
|
||||
/// pool, but tasks can certainly migrate among schedulers once they're in
|
||||
/// the pool.
|
||||
#[deprecated = "use the green and green_pinned methods of GreenTaskBuilder instead"]
|
||||
pub fn spawn(&mut self, opts: TaskOpts, f: proc():Send) {
|
||||
let task = self.task(opts, f);
|
||||
|
||||
|
@ -563,3 +568,54 @@ impl Drop for SchedPool {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A spawner for green tasks
|
||||
pub struct GreenSpawner<'a>{
|
||||
pool: &'a mut SchedPool,
|
||||
handle: Option<&'a mut SchedHandle>
|
||||
}
|
||||
|
||||
impl<'a> Spawner for GreenSpawner<'a> {
|
||||
#[inline]
|
||||
fn spawn(self, opts: TaskOpts, f: proc():Send) {
|
||||
let GreenSpawner { pool, handle } = self;
|
||||
match handle {
|
||||
None => pool.spawn(opts, f),
|
||||
Some(h) => h.send(PinnedTask(pool.task(opts, f)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An extension trait adding `green` configuration methods to `TaskBuilder`.
|
||||
pub trait GreenTaskBuilder {
|
||||
fn green<'a>(self, &'a mut SchedPool) -> TaskBuilder<GreenSpawner<'a>>;
|
||||
fn green_pinned<'a>(self, &'a mut SchedPool, &'a mut SchedHandle)
|
||||
-> TaskBuilder<GreenSpawner<'a>>;
|
||||
}
|
||||
|
||||
impl<S: Spawner> GreenTaskBuilder for TaskBuilder<S> {
|
||||
fn green<'a>(self, pool: &'a mut SchedPool) -> TaskBuilder<GreenSpawner<'a>> {
|
||||
self.spawner(GreenSpawner {pool: pool, handle: None})
|
||||
}
|
||||
|
||||
fn green_pinned<'a>(self, pool: &'a mut SchedPool, handle: &'a mut SchedHandle)
|
||||
-> TaskBuilder<GreenSpawner<'a>> {
|
||||
self.spawner(GreenSpawner {pool: pool, handle: Some(handle)})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::task::TaskBuilder;
|
||||
use super::{SchedPool, PoolConfig, GreenTaskBuilder};
|
||||
|
||||
#[test]
|
||||
fn test_green_builder() {
|
||||
let mut pool = SchedPool::new(PoolConfig::new());
|
||||
let res = TaskBuilder::new().green(&mut pool).try(proc() {
|
||||
"Success!".to_string()
|
||||
});
|
||||
assert_eq!(res.ok().unwrap(), "Success!".to_string());
|
||||
pool.shutdown();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,10 +32,13 @@
|
|||
//! ```rust
|
||||
//! extern crate native;
|
||||
//!
|
||||
//! use std::task::TaskBuilder;
|
||||
//! use native::NativeTaskBuilder;
|
||||
//!
|
||||
//! fn main() {
|
||||
//! // We're not sure whether this main function is run in 1:1 or M:N mode.
|
||||
//!
|
||||
//! native::task::spawn(proc() {
|
||||
//! TaskBuilder::new().native().spawn(proc() {
|
||||
//! // this code is guaranteed to be run on a native thread
|
||||
//! });
|
||||
//! }
|
||||
|
@ -50,7 +53,8 @@
|
|||
html_root_url = "http://doc.rust-lang.org/")]
|
||||
#![deny(unused_result, unused_must_use)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![feature(macro_rules)]
|
||||
#![allow(deprecated)]
|
||||
#![feature(default_type_params)]
|
||||
|
||||
// NB this crate explicitly does *not* allow glob imports, please seriously
|
||||
// consider whether they're needed before adding that feature here (the
|
||||
|
@ -65,6 +69,8 @@ use std::os;
|
|||
use std::rt;
|
||||
use std::str;
|
||||
|
||||
pub use task::NativeTaskBuilder;
|
||||
|
||||
pub mod io;
|
||||
pub mod task;
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ use std::rt;
|
|||
|
||||
use io;
|
||||
use task;
|
||||
use std::task::{TaskBuilder, Spawner};
|
||||
|
||||
/// Creates a new Task which is ready to execute as a 1:1 task.
|
||||
pub fn new(stack_bounds: (uint, uint)) -> Box<Task> {
|
||||
|
@ -48,12 +49,14 @@ fn ops() -> Box<Ops> {
|
|||
}
|
||||
|
||||
/// Spawns a function with the default configuration
|
||||
#[deprecated = "use the native method of NativeTaskBuilder instead"]
|
||||
pub fn spawn(f: proc():Send) {
|
||||
spawn_opts(TaskOpts { name: None, stack_size: None, on_exit: None }, f)
|
||||
}
|
||||
|
||||
/// Spawns a new task given the configuration options and a procedure to run
|
||||
/// inside the task.
|
||||
#[deprecated = "use the native method of NativeTaskBuilder instead"]
|
||||
pub fn spawn_opts(opts: TaskOpts, f: proc():Send) {
|
||||
let TaskOpts { name, stack_size, on_exit } = opts;
|
||||
|
||||
|
@ -95,6 +98,26 @@ pub fn spawn_opts(opts: TaskOpts, f: proc():Send) {
|
|||
})
|
||||
}
|
||||
|
||||
/// A spawner for native tasks
|
||||
pub struct NativeSpawner;
|
||||
|
||||
impl Spawner for NativeSpawner {
|
||||
fn spawn(self, opts: TaskOpts, f: proc():Send) {
|
||||
spawn_opts(opts, f)
|
||||
}
|
||||
}
|
||||
|
||||
/// An extension trait adding a `native` configuration method to `TaskBuilder`.
|
||||
pub trait NativeTaskBuilder {
|
||||
fn native(self) -> TaskBuilder<NativeSpawner>;
|
||||
}
|
||||
|
||||
impl<S: Spawner> NativeTaskBuilder for TaskBuilder<S> {
|
||||
fn native(self) -> TaskBuilder<NativeSpawner> {
|
||||
self.spawner(NativeSpawner)
|
||||
}
|
||||
}
|
||||
|
||||
// This structure is the glue between channels and the 1:1 scheduling mode. This
|
||||
// structure is allocated once per task.
|
||||
struct Ops {
|
||||
|
@ -259,7 +282,8 @@ mod tests {
|
|||
use std::rt::local::Local;
|
||||
use std::rt::task::{Task, TaskOpts};
|
||||
use std::task;
|
||||
use super::{spawn, spawn_opts, Ops};
|
||||
use std::task::TaskBuilder;
|
||||
use super::{spawn, spawn_opts, Ops, NativeTaskBuilder};
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
|
@ -347,4 +371,12 @@ mod tests {
|
|||
});
|
||||
rx.recv();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_native_builder() {
|
||||
let res = TaskBuilder::new().native().try(proc() {
|
||||
"Success!".to_string()
|
||||
});
|
||||
assert_eq!(res.ok().unwrap(), "Success!".to_string());
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
26
src/test/run-pass/task-stderr.rs
Normal file
26
src/test/run-pass/task-stderr.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
use std::io::{ChanReader, ChanWriter};
|
||||
use std::task::build;
|
||||
|
||||
fn main() {
|
||||
let (tx, rx) = channel();
|
||||
let mut reader = ChanReader::new(rx);
|
||||
let stderr = ChanWriter::new(tx);
|
||||
|
||||
let res = build().stderr(box stderr as Box<Writer + Send>).try(proc() -> () {
|
||||
fail!("Hello, world!")
|
||||
});
|
||||
assert!(res.is_err());
|
||||
|
||||
let output = reader.read_to_str().unwrap();
|
||||
assert!(output.as_slice().contains("Hello, world!"));
|
||||
}
|
Loading…
Add table
Reference in a new issue