mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-14 02:49:40 +00:00
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
@ -159,16 +159,19 @@
|
|||||||
//!
|
//!
|
||||||
//! # Using a scheduler pool
|
//! # 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
|
//! ```rust
|
||||||
//! use std::rt::task::TaskOpts;
|
//! use std::task::TaskBuilder;
|
||||||
//! use green::{SchedPool, PoolConfig};
|
//! use green::{SchedPool, PoolConfig, GreenTaskBuilder};
|
||||||
//! use green::sched::{PinnedTask, TaskFromFriend};
|
|
||||||
//!
|
//!
|
||||||
//! let config = PoolConfig::new();
|
//! let config = PoolConfig::new();
|
||||||
//! let mut pool = SchedPool::new(config);
|
//! let mut pool = SchedPool::new(config);
|
||||||
//!
|
//!
|
||||||
//! // Spawn tasks into the pool of schedulers
|
//! // 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
|
//! // this code is running inside the pool of schedulers
|
||||||
//!
|
//!
|
||||||
//! spawn(proc() {
|
//! spawn(proc() {
|
||||||
@ -181,12 +184,9 @@
|
|||||||
//! let mut handle = pool.spawn_sched();
|
//! let mut handle = pool.spawn_sched();
|
||||||
//!
|
//!
|
||||||
//! // Pin a task to the spawned scheduler
|
//! // Pin a task to the spawned scheduler
|
||||||
//! let task = pool.task(TaskOpts::new(), proc() { /* ... */ });
|
//! TaskBuilder::new().green_pinned(&mut pool, &mut handle).spawn(proc() {
|
||||||
//! handle.send(PinnedTask(task));
|
//! /* ... */
|
||||||
//!
|
//! });
|
||||||
//! // Schedule a task on this new scheduler
|
|
||||||
//! let task = pool.task(TaskOpts::new(), proc() { /* ... */ });
|
|
||||||
//! handle.send(TaskFromFriend(task));
|
|
||||||
//!
|
//!
|
||||||
//! // Handles keep schedulers alive, so be sure to drop all handles before
|
//! // Handles keep schedulers alive, so be sure to drop all handles before
|
||||||
//! // destroying the sched pool
|
//! // destroying the sched pool
|
||||||
@ -209,6 +209,8 @@
|
|||||||
// NB this does *not* include globs, please keep it that way.
|
// NB this does *not* include globs, please keep it that way.
|
||||||
#![feature(macro_rules, phase)]
|
#![feature(macro_rules, phase)]
|
||||||
#![allow(visible_private_types)]
|
#![allow(visible_private_types)]
|
||||||
|
#![allow(deprecated)]
|
||||||
|
#![feature(default_type_params)]
|
||||||
|
|
||||||
#[cfg(test)] #[phase(plugin, link)] extern crate log;
|
#[cfg(test)] #[phase(plugin, link)] extern crate log;
|
||||||
#[cfg(test)] extern crate rustuv;
|
#[cfg(test)] extern crate rustuv;
|
||||||
@ -224,8 +226,9 @@ use std::rt::task::TaskOpts;
|
|||||||
use std::rt;
|
use std::rt;
|
||||||
use std::sync::atomics::{SeqCst, AtomicUint, INIT_ATOMIC_UINT};
|
use std::sync::atomics::{SeqCst, AtomicUint, INIT_ATOMIC_UINT};
|
||||||
use std::sync::deque;
|
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 sleeper_list::SleeperList;
|
||||||
use stack::StackPool;
|
use stack::StackPool;
|
||||||
use task::GreenTask;
|
use task::GreenTask;
|
||||||
@ -444,6 +447,7 @@ impl SchedPool {
|
|||||||
/// This is useful to create a task which can then be sent to a specific
|
/// 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 created by `spawn_sched` (and possibly pin it to that
|
||||||
/// scheduler).
|
/// scheduler).
|
||||||
|
#[deprecated = "use the green and green_pinned methods of GreenTaskBuilder instead"]
|
||||||
pub fn task(&mut self, opts: TaskOpts, f: proc():Send) -> Box<GreenTask> {
|
pub fn task(&mut self, opts: TaskOpts, f: proc():Send) -> Box<GreenTask> {
|
||||||
GreenTask::configure(&mut self.stack_pool, opts, f)
|
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
|
/// 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
|
/// pool, but tasks can certainly migrate among schedulers once they're in
|
||||||
/// the pool.
|
/// the pool.
|
||||||
|
#[deprecated = "use the green and green_pinned methods of GreenTaskBuilder instead"]
|
||||||
pub fn spawn(&mut self, opts: TaskOpts, f: proc():Send) {
|
pub fn spawn(&mut self, opts: TaskOpts, f: proc():Send) {
|
||||||
let task = self.task(opts, f);
|
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
|
//! ```rust
|
||||||
//! extern crate native;
|
//! extern crate native;
|
||||||
//!
|
//!
|
||||||
|
//! use std::task::TaskBuilder;
|
||||||
|
//! use native::NativeTaskBuilder;
|
||||||
|
//!
|
||||||
//! fn main() {
|
//! fn main() {
|
||||||
//! // We're not sure whether this main function is run in 1:1 or M:N mode.
|
//! // 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
|
//! // this code is guaranteed to be run on a native thread
|
||||||
//! });
|
//! });
|
||||||
//! }
|
//! }
|
||||||
@ -50,7 +53,8 @@
|
|||||||
html_root_url = "http://doc.rust-lang.org/")]
|
html_root_url = "http://doc.rust-lang.org/")]
|
||||||
#![deny(unused_result, unused_must_use)]
|
#![deny(unused_result, unused_must_use)]
|
||||||
#![allow(non_camel_case_types)]
|
#![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
|
// NB this crate explicitly does *not* allow glob imports, please seriously
|
||||||
// consider whether they're needed before adding that feature here (the
|
// consider whether they're needed before adding that feature here (the
|
||||||
@ -65,6 +69,8 @@ use std::os;
|
|||||||
use std::rt;
|
use std::rt;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
|
pub use task::NativeTaskBuilder;
|
||||||
|
|
||||||
pub mod io;
|
pub mod io;
|
||||||
pub mod task;
|
pub mod task;
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ use std::rt;
|
|||||||
|
|
||||||
use io;
|
use io;
|
||||||
use task;
|
use task;
|
||||||
|
use std::task::{TaskBuilder, Spawner};
|
||||||
|
|
||||||
/// Creates a new Task which is ready to execute as a 1:1 task.
|
/// Creates a new Task which is ready to execute as a 1:1 task.
|
||||||
pub fn new(stack_bounds: (uint, uint)) -> Box<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
|
/// Spawns a function with the default configuration
|
||||||
|
#[deprecated = "use the native method of NativeTaskBuilder instead"]
|
||||||
pub fn spawn(f: proc():Send) {
|
pub fn spawn(f: proc():Send) {
|
||||||
spawn_opts(TaskOpts { name: None, stack_size: None, on_exit: None }, f)
|
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
|
/// Spawns a new task given the configuration options and a procedure to run
|
||||||
/// inside the task.
|
/// inside the task.
|
||||||
|
#[deprecated = "use the native method of NativeTaskBuilder instead"]
|
||||||
pub fn spawn_opts(opts: TaskOpts, f: proc():Send) {
|
pub fn spawn_opts(opts: TaskOpts, f: proc():Send) {
|
||||||
let TaskOpts { name, stack_size, on_exit } = opts;
|
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
|
// This structure is the glue between channels and the 1:1 scheduling mode. This
|
||||||
// structure is allocated once per task.
|
// structure is allocated once per task.
|
||||||
struct Ops {
|
struct Ops {
|
||||||
@ -259,7 +282,8 @@ mod tests {
|
|||||||
use std::rt::local::Local;
|
use std::rt::local::Local;
|
||||||
use std::rt::task::{Task, TaskOpts};
|
use std::rt::task::{Task, TaskOpts};
|
||||||
use std::task;
|
use std::task;
|
||||||
use super::{spawn, spawn_opts, Ops};
|
use std::task::TaskBuilder;
|
||||||
|
use super::{spawn, spawn_opts, Ops, NativeTaskBuilder};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn smoke() {
|
fn smoke() {
|
||||||
@ -347,4 +371,12 @@ mod tests {
|
|||||||
});
|
});
|
||||||
rx.recv();
|
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…
Reference in New Issue
Block a user