Skip to content

Commit

Permalink
Revamp TaskBuilder API
Browse files Browse the repository at this point in the history
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]
  • Loading branch information
aturon authored and alexcrichton committed Jun 19, 2014
1 parent 8e9e17d commit a23511a
Show file tree
Hide file tree
Showing 5 changed files with 594 additions and 395 deletions.
78 changes: 67 additions & 11 deletions src/libgreen/lib.rs
Expand Up @@ -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() {
Expand All @@ -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
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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)
}
Expand All @@ -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);

Expand Down Expand Up @@ -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();
}
}
10 changes: 8 additions & 2 deletions src/libnative/lib.rs
Expand Up @@ -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
//! });
//! }
Expand All @@ -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
Expand All @@ -65,6 +69,8 @@ use std::os;
use std::rt;
use std::str;

pub use task::NativeTaskBuilder;

pub mod io;
pub mod task;

Expand Down
34 changes: 33 additions & 1 deletion src/libnative/task.rs
Expand Up @@ -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> {
Expand All @@ -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;

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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());
}
}

0 comments on commit a23511a

Please sign in to comment.