Skip to content

Commit a23511a

Browse files
aturonalexcrichton
authored andcommitted
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]
1 parent 8e9e17d commit a23511a

File tree

5 files changed

+594
-395
lines changed

5 files changed

+594
-395
lines changed

src/libgreen/lib.rs

+67-11
Original file line numberDiff line numberDiff line change
@@ -159,16 +159,19 @@
159159
//!
160160
//! # Using a scheduler pool
161161
//!
162+
//! This library adds a `GreenTaskBuilder` trait that extends the methods
163+
//! available on `std::task::TaskBuilder` to allow spawning a green task,
164+
//! possibly pinned to a particular scheduler thread:
165+
//!
162166
//! ```rust
163-
//! use std::rt::task::TaskOpts;
164-
//! use green::{SchedPool, PoolConfig};
165-
//! use green::sched::{PinnedTask, TaskFromFriend};
167+
//! use std::task::TaskBuilder;
168+
//! use green::{SchedPool, PoolConfig, GreenTaskBuilder};
166169
//!
167170
//! let config = PoolConfig::new();
168171
//! let mut pool = SchedPool::new(config);
169172
//!
170173
//! // Spawn tasks into the pool of schedulers
171-
//! pool.spawn(TaskOpts::new(), proc() {
174+
//! TaskBuilder::new().green(&mut pool).spawn(proc() {
172175
//! // this code is running inside the pool of schedulers
173176
//!
174177
//! spawn(proc() {
@@ -181,12 +184,9 @@
181184
//! let mut handle = pool.spawn_sched();
182185
//!
183186
//! // Pin a task to the spawned scheduler
184-
//! let task = pool.task(TaskOpts::new(), proc() { /* ... */ });
185-
//! handle.send(PinnedTask(task));
186-
//!
187-
//! // Schedule a task on this new scheduler
188-
//! let task = pool.task(TaskOpts::new(), proc() { /* ... */ });
189-
//! handle.send(TaskFromFriend(task));
187+
//! TaskBuilder::new().green_pinned(&mut pool, &mut handle).spawn(proc() {
188+
//! /* ... */
189+
//! });
190190
//!
191191
//! // Handles keep schedulers alive, so be sure to drop all handles before
192192
//! // destroying the sched pool
@@ -209,6 +209,8 @@
209209
// NB this does *not* include globs, please keep it that way.
210210
#![feature(macro_rules, phase)]
211211
#![allow(visible_private_types)]
212+
#![allow(deprecated)]
213+
#![feature(default_type_params)]
212214

213215
#[cfg(test)] #[phase(plugin, link)] extern crate log;
214216
#[cfg(test)] extern crate rustuv;
@@ -224,8 +226,9 @@ use std::rt::task::TaskOpts;
224226
use std::rt;
225227
use std::sync::atomics::{SeqCst, AtomicUint, INIT_ATOMIC_UINT};
226228
use std::sync::deque;
229+
use std::task::{TaskBuilder, Spawner};
227230

228-
use sched::{Shutdown, Scheduler, SchedHandle, TaskFromFriend, NewNeighbor};
231+
use sched::{Shutdown, Scheduler, SchedHandle, TaskFromFriend, PinnedTask, NewNeighbor};
229232
use sleeper_list::SleeperList;
230233
use stack::StackPool;
231234
use task::GreenTask;
@@ -444,6 +447,7 @@ impl SchedPool {
444447
/// This is useful to create a task which can then be sent to a specific
445448
/// scheduler created by `spawn_sched` (and possibly pin it to that
446449
/// scheduler).
450+
#[deprecated = "use the green and green_pinned methods of GreenTaskBuilder instead"]
447451
pub fn task(&mut self, opts: TaskOpts, f: proc():Send) -> Box<GreenTask> {
448452
GreenTask::configure(&mut self.stack_pool, opts, f)
449453
}
@@ -454,6 +458,7 @@ impl SchedPool {
454458
/// New tasks are spawned in a round-robin fashion to the schedulers in this
455459
/// pool, but tasks can certainly migrate among schedulers once they're in
456460
/// the pool.
461+
#[deprecated = "use the green and green_pinned methods of GreenTaskBuilder instead"]
457462
pub fn spawn(&mut self, opts: TaskOpts, f: proc():Send) {
458463
let task = self.task(opts, f);
459464

@@ -563,3 +568,54 @@ impl Drop for SchedPool {
563568
}
564569
}
565570
}
571+
572+
/// A spawner for green tasks
573+
pub struct GreenSpawner<'a>{
574+
pool: &'a mut SchedPool,
575+
handle: Option<&'a mut SchedHandle>
576+
}
577+
578+
impl<'a> Spawner for GreenSpawner<'a> {
579+
#[inline]
580+
fn spawn(self, opts: TaskOpts, f: proc():Send) {
581+
let GreenSpawner { pool, handle } = self;
582+
match handle {
583+
None => pool.spawn(opts, f),
584+
Some(h) => h.send(PinnedTask(pool.task(opts, f)))
585+
}
586+
}
587+
}
588+
589+
/// An extension trait adding `green` configuration methods to `TaskBuilder`.
590+
pub trait GreenTaskBuilder {
591+
fn green<'a>(self, &'a mut SchedPool) -> TaskBuilder<GreenSpawner<'a>>;
592+
fn green_pinned<'a>(self, &'a mut SchedPool, &'a mut SchedHandle)
593+
-> TaskBuilder<GreenSpawner<'a>>;
594+
}
595+
596+
impl<S: Spawner> GreenTaskBuilder for TaskBuilder<S> {
597+
fn green<'a>(self, pool: &'a mut SchedPool) -> TaskBuilder<GreenSpawner<'a>> {
598+
self.spawner(GreenSpawner {pool: pool, handle: None})
599+
}
600+
601+
fn green_pinned<'a>(self, pool: &'a mut SchedPool, handle: &'a mut SchedHandle)
602+
-> TaskBuilder<GreenSpawner<'a>> {
603+
self.spawner(GreenSpawner {pool: pool, handle: Some(handle)})
604+
}
605+
}
606+
607+
#[cfg(test)]
608+
mod test {
609+
use std::task::TaskBuilder;
610+
use super::{SchedPool, PoolConfig, GreenTaskBuilder};
611+
612+
#[test]
613+
fn test_green_builder() {
614+
let mut pool = SchedPool::new(PoolConfig::new());
615+
let res = TaskBuilder::new().green(&mut pool).try(proc() {
616+
"Success!".to_string()
617+
});
618+
assert_eq!(res.ok().unwrap(), "Success!".to_string());
619+
pool.shutdown();
620+
}
621+
}

src/libnative/lib.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,13 @@
3232
//! ```rust
3333
//! extern crate native;
3434
//!
35+
//! use std::task::TaskBuilder;
36+
//! use native::NativeTaskBuilder;
37+
//!
3538
//! fn main() {
3639
//! // We're not sure whether this main function is run in 1:1 or M:N mode.
3740
//!
38-
//! native::task::spawn(proc() {
41+
//! TaskBuilder::new().native().spawn(proc() {
3942
//! // this code is guaranteed to be run on a native thread
4043
//! });
4144
//! }
@@ -50,7 +53,8 @@
5053
html_root_url = "http://doc.rust-lang.org/")]
5154
#![deny(unused_result, unused_must_use)]
5255
#![allow(non_camel_case_types)]
53-
#![feature(macro_rules)]
56+
#![allow(deprecated)]
57+
#![feature(default_type_params)]
5458

5559
// NB this crate explicitly does *not* allow glob imports, please seriously
5660
// consider whether they're needed before adding that feature here (the
@@ -65,6 +69,8 @@ use std::os;
6569
use std::rt;
6670
use std::str;
6771

72+
pub use task::NativeTaskBuilder;
73+
6874
pub mod io;
6975
pub mod task;
7076

src/libnative/task.rs

+33-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use std::rt;
2727

2828
use io;
2929
use task;
30+
use std::task::{TaskBuilder, Spawner};
3031

3132
/// Creates a new Task which is ready to execute as a 1:1 task.
3233
pub fn new(stack_bounds: (uint, uint)) -> Box<Task> {
@@ -48,12 +49,14 @@ fn ops() -> Box<Ops> {
4849
}
4950

5051
/// Spawns a function with the default configuration
52+
#[deprecated = "use the native method of NativeTaskBuilder instead"]
5153
pub fn spawn(f: proc():Send) {
5254
spawn_opts(TaskOpts { name: None, stack_size: None, on_exit: None }, f)
5355
}
5456

5557
/// Spawns a new task given the configuration options and a procedure to run
5658
/// inside the task.
59+
#[deprecated = "use the native method of NativeTaskBuilder instead"]
5760
pub fn spawn_opts(opts: TaskOpts, f: proc():Send) {
5861
let TaskOpts { name, stack_size, on_exit } = opts;
5962

@@ -95,6 +98,26 @@ pub fn spawn_opts(opts: TaskOpts, f: proc():Send) {
9598
})
9699
}
97100

101+
/// A spawner for native tasks
102+
pub struct NativeSpawner;
103+
104+
impl Spawner for NativeSpawner {
105+
fn spawn(self, opts: TaskOpts, f: proc():Send) {
106+
spawn_opts(opts, f)
107+
}
108+
}
109+
110+
/// An extension trait adding a `native` configuration method to `TaskBuilder`.
111+
pub trait NativeTaskBuilder {
112+
fn native(self) -> TaskBuilder<NativeSpawner>;
113+
}
114+
115+
impl<S: Spawner> NativeTaskBuilder for TaskBuilder<S> {
116+
fn native(self) -> TaskBuilder<NativeSpawner> {
117+
self.spawner(NativeSpawner)
118+
}
119+
}
120+
98121
// This structure is the glue between channels and the 1:1 scheduling mode. This
99122
// structure is allocated once per task.
100123
struct Ops {
@@ -259,7 +282,8 @@ mod tests {
259282
use std::rt::local::Local;
260283
use std::rt::task::{Task, TaskOpts};
261284
use std::task;
262-
use super::{spawn, spawn_opts, Ops};
285+
use std::task::TaskBuilder;
286+
use super::{spawn, spawn_opts, Ops, NativeTaskBuilder};
263287

264288
#[test]
265289
fn smoke() {
@@ -347,4 +371,12 @@ mod tests {
347371
});
348372
rx.recv();
349373
}
374+
375+
#[test]
376+
fn test_native_builder() {
377+
let res = TaskBuilder::new().native().try(proc() {
378+
"Success!".to_string()
379+
});
380+
assert_eq!(res.ok().unwrap(), "Success!".to_string());
381+
}
350382
}

0 commit comments

Comments
 (0)