]> git.lizzy.rs Git - rust.git/commitdiff
Remove Runtime trait
authorAaron Turon <aturon@mozilla.com>
Fri, 14 Nov 2014 21:40:34 +0000 (13:40 -0800)
committerAaron Turon <aturon@mozilla.com>
Fri, 21 Nov 2014 01:19:13 +0000 (17:19 -0800)
This commit removes most of the remaining runtime infrastructure related
to the green/native split. In particular, it removes the `Runtime` trait
and instead inlines the native implementation.

Closes #17325

[breaking-change]

src/libnative/task.rs
src/librustrt/lib.rs
src/librustrt/rtio.rs [deleted file]
src/librustrt/task.rs
src/libstd/rt/mod.rs

index 6d640b61b18d3a6dd0c79014d7ecbe6f0ebe6918..02fb5b31c0d1cabe510328f00ffb6088eab770dd 100644 (file)
 use std::rt::thread::Thread;
 use std::rt;
 
-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), stack_guard: uint) -> Box<Task> {
-    let mut task = box Task::new();
-    let mut ops = ops();
-    ops.stack_bounds = stack_bounds;
-    ops.stack_guard = stack_guard;
-    task.put_runtime(ops);
-    return task;
-}
-
-fn ops() -> Box<Ops> {
-    box Ops {
-        lock: unsafe { NativeMutex::new() },
-        awoken: false,
-        // these *should* get overwritten
-        stack_bounds: (0, 0),
-        stack_guard: 0
-    }
-}
-
-/// A spawner for native tasks
-pub struct NativeSpawner;
-
-impl Spawner for NativeSpawner {
-    fn spawn(self, opts: TaskOpts, f: proc():Send) {
-        let TaskOpts { name, stack_size, on_exit } = opts;
-
-        let mut task = box Task::new();
-        task.name = name;
-        task.death.on_exit = on_exit;
-
-        let stack = stack_size.unwrap_or(rt::min_stack());
-        let task = task;
-        let ops = ops();
-
-        // Note that this increment must happen *before* the spawn in order to
-        // guarantee that if this task exits it will always end up waiting for
-        // the spawned task to exit.
-        let token = bookkeeping::increment();
-
-        // Spawning a new OS thread guarantees that __morestack will never get
-        // triggered, but we must manually set up the actual stack bounds once
-        // this function starts executing. This raises the lower limit by a bit
-        // because by the time that this function is executing we've already
-        // consumed at least a little bit of stack (we don't know the exact byte
-        // address at which our stack started).
-        Thread::spawn_stack(stack, proc() {
-            let something_around_the_top_of_the_stack = 1;
-            let addr = &something_around_the_top_of_the_stack as *const int;
-            let my_stack = addr as uint;
-            unsafe {
-                stack::record_os_managed_stack_bounds(my_stack - stack + 1024,
-                                                      my_stack);
-            }
-            let mut ops = ops;
-            ops.stack_guard = rt::thread::current_guard_page();
-            ops.stack_bounds = (my_stack - stack + 1024, my_stack);
-
-            let mut f = Some(f);
-            let mut task = task;
-            task.put_runtime(ops);
-            drop(task.run(|| { f.take().unwrap()() }).destroy());
-            drop(token);
-        })
-    }
-}
-
-/// 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 {
-    lock: NativeMutex,       // native synchronization
-    awoken: bool,      // used to prevent spurious wakeups
-
-    // This field holds the known bounds of the stack in (lo, hi) form. Not all
-    // native tasks necessarily know their precise bounds, hence this is
-    // optional.
-    stack_bounds: (uint, uint),
-
-    stack_guard: uint
-}
-
-impl rt::Runtime for Ops {
-    fn yield_now(self: Box<Ops>, mut cur_task: Box<Task>) {
-        // put the task back in TLS and then invoke the OS thread yield
-        cur_task.put_runtime(self);
-        Local::put(cur_task);
-        Thread::yield_now();
-    }
-
-    fn maybe_yield(self: Box<Ops>, mut cur_task: Box<Task>) {
-        // just put the task back in TLS, on OS threads we never need to
-        // opportunistically yield b/c the OS will do that for us (preemption)
-        cur_task.put_runtime(self);
-        Local::put(cur_task);
-    }
-
-    fn wrap(self: Box<Ops>) -> Box<Any+'static> {
-        self as Box<Any+'static>
-    }
-
-    fn stack_bounds(&self) -> (uint, uint) { self.stack_bounds }
-
-    fn stack_guard(&self) -> Option<uint> {
-        if self.stack_guard != 0 {
-            Some(self.stack_guard)
-        } else {
-            None
-        }
-    }
-
-    fn can_block(&self) -> bool { true }
-
-    // This function gets a little interesting. There are a few safety and
-    // ownership violations going on here, but this is all done in the name of
-    // shared state. Additionally, all of the violations are protected with a
-    // mutex, so in theory there are no races.
-    //
-    // The first thing we need to do is to get a pointer to the task's internal
-    // mutex. This address will not be changing (because the task is allocated
-    // on the heap). We must have this handle separately because the task will
-    // have its ownership transferred to the given closure. We're guaranteed,
-    // however, that this memory will remain valid because *this* is the current
-    // task's execution thread.
-    //
-    // The next weird part is where ownership of the task actually goes. We
-    // relinquish it to the `f` blocking function, but upon returning this
-    // function needs to replace the task back in TLS. There is no communication
-    // from the wakeup thread back to this thread about the task pointer, and
-    // there's really no need to. In order to get around this, we cast the task
-    // to a `uint` which is then used at the end of this function to cast back
-    // to a `Box<Task>` object. Naturally, this looks like it violates
-    // ownership semantics in that there may be two `Box<Task>` objects.
-    //
-    // The fun part is that the wakeup half of this implementation knows to
-    // "forget" the task on the other end. This means that the awakening half of
-    // things silently relinquishes ownership back to this thread, but not in a
-    // way that the compiler can understand. The task's memory is always valid
-    // for both tasks because these operations are all done inside of a mutex.
-    //
-    // You'll also find that if blocking fails (the `f` function hands the
-    // BlockedTask back to us), we will `mem::forget` the handles. The
-    // reasoning for this is the same logic as above in that the task silently
-    // transfers ownership via the `uint`, not through normal compiler
-    // semantics.
-    //
-    // On a mildly unrelated note, it should also be pointed out that OS
-    // condition variables are susceptible to spurious wakeups, which we need to
-    // be ready for. In order to accommodate for this fact, we have an extra
-    // `awoken` field which indicates whether we were actually woken up via some
-    // invocation of `reawaken`. This flag is only ever accessed inside the
-    // lock, so there's no need to make it atomic.
-    fn deschedule(mut self: Box<Ops>,
-                  times: uint,
-                  mut cur_task: Box<Task>,
-                  f: |BlockedTask| -> Result<(), BlockedTask>) {
-        let me = &mut *self as *mut Ops;
-        cur_task.put_runtime(self);
-
-        unsafe {
-            let cur_task_dupe = &mut *cur_task as *mut Task;
-            let task = BlockedTask::block(cur_task);
-
-            if times == 1 {
-                let guard = (*me).lock.lock();
-                (*me).awoken = false;
-                match f(task) {
-                    Ok(()) => {
-                        while !(*me).awoken {
-                            guard.wait();
-                        }
-                    }
-                    Err(task) => { mem::forget(task.wake()); }
-                }
-            } else {
-                let iter = task.make_selectable(times);
-                let guard = (*me).lock.lock();
-                (*me).awoken = false;
-
-                // Apply the given closure to all of the "selectable tasks",
-                // bailing on the first one that produces an error. Note that
-                // care must be taken such that when an error is occurred, we
-                // may not own the task, so we may still have to wait for the
-                // task to become available. In other words, if task.wake()
-                // returns `None`, then someone else has ownership and we must
-                // wait for their signal.
-                match iter.map(f).filter_map(|a| a.err()).next() {
-                    None => {}
-                    Some(task) => {
-                        match task.wake() {
-                            Some(task) => {
-                                mem::forget(task);
-                                (*me).awoken = true;
-                            }
-                            None => {}
-                        }
-                    }
-                }
-                while !(*me).awoken {
-                    guard.wait();
-                }
-            }
-            // re-acquire ownership of the task
-            cur_task = mem::transmute(cur_task_dupe);
-        }
-
-        // put the task back in TLS, and everything is as it once was.
-        Local::put(cur_task);
-    }
-
-    // See the comments on `deschedule` for why the task is forgotten here, and
-    // why it's valid to do so.
-    fn reawaken(mut self: Box<Ops>, mut to_wake: Box<Task>) {
-        unsafe {
-            let me = &mut *self as *mut Ops;
-            to_wake.put_runtime(self);
-            mem::forget(to_wake);
-            let guard = (*me).lock.lock();
-            (*me).awoken = true;
-            guard.signal();
-        }
-    }
-
-    fn spawn_sibling(self: Box<Ops>,
-                     mut cur_task: Box<Task>,
-                     opts: TaskOpts,
-                     f: proc():Send) {
-        cur_task.put_runtime(self);
-        Local::put(cur_task);
-
-        NativeSpawner.spawn(opts, f);
-    }
-}
-
 #[cfg(test)]
 mod tests {
     use std::rt::local::Local;
index fee748e29d9e08e6ee6ddda352f18a97b74468a0..387b430b8f8b9603bba171bdc248b7bd039233f6 100644 (file)
 
 use core::prelude::*;
 
-use alloc::boxed::Box;
-use core::any::Any;
-
-use task::{Task, BlockedTask, TaskOpts};
-
 mod macros;
 
 mod at_exit_imp;
 pub mod local;
 pub mod local_data;
 pub mod mutex;
-pub mod rtio;
 pub mod stack;
 pub mod task;
 pub mod thread;
 pub mod unwind;
 
-/// The interface to the current runtime.
-///
-/// This trait is used as the abstraction between 1:1 and M:N scheduling. The
-/// two independent crates, libnative and libgreen, both have objects which
-/// implement this trait. The goal of this trait is to encompass all the
-/// fundamental differences in functionality between the 1:1 and M:N runtime
-/// modes.
-pub trait Runtime {
-    // Necessary scheduling functions, used for channels and blocking I/O
-    // (sometimes).
-    fn yield_now(self: Box<Self>, cur_task: Box<Task>);
-    fn maybe_yield(self: Box<Self>, cur_task: Box<Task>);
-    fn deschedule(self: Box<Self>,
-                  times: uint,
-                  cur_task: Box<Task>,
-                  f: |BlockedTask| -> Result<(), BlockedTask>);
-    fn reawaken(self: Box<Self>, to_wake: Box<Task>);
-
-    // Miscellaneous calls which are very different depending on what context
-    // you're in.
-    fn spawn_sibling(self: Box<Self>,
-                     cur_task: Box<Task>,
-                     opts: TaskOpts,
-                     f: proc():Send);
-    /// The (low, high) edges of the current stack.
-    fn stack_bounds(&self) -> (uint, uint); // (lo, hi)
-    /// The last writable byte of the stack next to the guard page
-    fn stack_guard(&self) -> Option<uint>;
-    fn can_block(&self) -> bool;
-
-    // FIXME: This is a serious code smell and this should not exist at all.
-    fn wrap(self: Box<Self>) -> Box<Any+'static>;
-}
-
 /// The default error code of the rust runtime if the main task panics instead
 /// of exiting cleanly.
 pub const DEFAULT_ERROR_CODE: int = 101;
diff --git a/src/librustrt/rtio.rs b/src/librustrt/rtio.rs
deleted file mode 100644 (file)
index 86de816..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-// 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.
-
-//! The EventLoop and internal synchronous I/O interface.
-
-use core::prelude::*;
-use alloc::boxed::Box;
-
-pub trait EventLoop {
-    fn run(&mut self);
-    fn callback(&mut self, arg: proc(): Send);
-    fn pausable_idle_callback(&mut self, Box<Callback + Send>)
-                              -> Box<PausableIdleCallback + Send>;
-    fn remote_callback(&mut self, Box<Callback + Send>)
-                       -> Box<RemoteCallback + Send>;
-
-    // last vestige of IoFactory
-    fn has_active_io(&self) -> bool;
-}
-
-pub trait Callback {
-    fn call(&mut self);
-}
-
-pub trait RemoteCallback {
-    /// Trigger the remote callback. Note that the number of times the
-    /// callback is run is not guaranteed. All that is guaranteed is
-    /// that, after calling 'fire', the callback will be called at
-    /// least once, but multiple callbacks may be coalesced and
-    /// callbacks may be called more often requested. Destruction also
-    /// triggers the callback.
-    fn fire(&mut self);
-}
-
-pub trait PausableIdleCallback {
-    fn pause(&mut self);
-    fn resume(&mut self);
-}
index 2c8fca2d5e6d9c49b2019b4a52581272b15fe2a9..34c913c5bcb40e52305350f1e2469c34e9ae5492 100644 (file)
@@ -16,7 +16,7 @@
 use self::TaskState::*;
 
 use alloc::arc::Arc;
-use alloc::boxed::{BoxAny, Box};
+use alloc::boxed::Box;
 use core::any::Any;
 use core::atomic::{AtomicUint, SeqCst};
 use core::iter::Take;
 use core::mem;
 use core::prelude::{Clone, Drop, Err, Iterator, None, Ok, Option, Send, Some};
 use core::prelude::{drop};
-use core::raw;
 
+use bookkeeping;
+use mutex::NativeMutex;
 use local_data;
-use Runtime;
 use local::Local;
+use thread::{mod, Thread};
+use stack;
 use unwind;
 use unwind::Unwinder;
 use collections::str::SendStr;
 
 /// State associated with Rust tasks.
 ///
-/// Rust tasks are primarily built with two separate components. One is this
-/// structure which handles standard services such as TLD, unwinding support,
-/// naming of a task, etc. The second component is the runtime of this task, a
-/// `Runtime` trait object.
-///
-/// The `Runtime` object instructs this task how it can perform critical
-/// operations such as blocking, rescheduling, I/O constructors, etc. The two
-/// halves are separately owned, but one is often found contained in the other.
-/// A task's runtime can be reflected upon with the `maybe_take_runtime` method,
-/// and otherwise its ownership is managed with `take_runtime` and
-/// `put_runtime`.
-///
-/// In general, this structure should not be used. This is meant to be an
-/// unstable internal detail of the runtime itself. From time-to-time, however,
-/// it is useful to manage tasks directly. An example of this would be
-/// interoperating with the Rust runtime from FFI callbacks or such. For this
-/// reason, there are two methods of note with the `Task` structure.
-///
-/// * `run` - This function will execute a closure inside the context of a task.
-///           Failure is caught and handled via the task's on_exit callback. If
-///           this panics, the task is still returned, but it can no longer be
-///           used, it is poisoned.
-///
-/// * `destroy` - This is a required function to call to destroy a task. If a
-///               task falls out of scope without calling `destroy`, its
-///               destructor bomb will go off, aborting the process.
-///
-/// With these two methods, tasks can be re-used to execute code inside of its
-/// context while having a point in the future where destruction is allowed.
-/// More information can be found on these specific methods.
-///
-/// # Example
-///
-/// ```no_run
-/// extern crate native;
-/// use std::uint;
-/// # fn main() {
-///
-/// // Create a task using a native runtime
-/// let task = native::task::new((0, uint::MAX), 0);
-///
-/// // Run some code, catching any possible panic
-/// let task = task.run(|| {
-///     // Run some code inside this task
-///     println!("Hello with a native runtime!");
-/// });
-///
-/// // Run some code again, catching the panic
-/// let task = task.run(|| {
-///     panic!("oh no, what to do!");
-/// });
-///
-/// // Now that the task has panicked, it can never be used again
-/// assert!(task.is_destroyed());
-///
-/// // Deallocate the resources associated with this task
-/// task.destroy();
-/// # }
-/// ```
+/// This structure is currently undergoing major changes, and is
+/// likely to be move/be merged with a `Thread` structure.
 pub struct Task {
     pub storage: LocalStorage,
     pub unwinder: Unwinder,
@@ -101,7 +46,15 @@ pub struct Task {
     pub name: Option<SendStr>,
 
     state: TaskState,
-    imp: Option<Box<Runtime + Send + 'static>>,
+    lock: NativeMutex,       // native synchronization
+    awoken: bool,            // used to prevent spurious wakeups
+
+    // This field holds the known bounds of the stack in (lo, hi) form. Not all
+    // native tasks necessarily know their precise bounds, hence this is
+    // optional.
+    stack_bounds: (uint, uint),
+
+    stack_guard: uint
 }
 
 // Once a task has entered the `Armed` state it must be destroyed via `drop`,
@@ -152,23 +105,60 @@ pub struct BlockedTasks {
 
 impl Task {
     /// Creates a new uninitialized task.
-    ///
-    /// This method cannot be used to immediately invoke `run` because the task
-    /// itself will likely require a runtime to be inserted via `put_runtime`.
-    ///
-    /// Note that you likely don't want to call this function, but rather the
-    /// task creation functions through libnative or libgreen.
-    pub fn new() -> Task {
+    pub fn new(stack_bounds: Option<(uint, uint)>, stack_guard: Option<uint>) -> Task {
         Task {
             storage: LocalStorage(None),
             unwinder: Unwinder::new(),
             death: Death::new(),
             state: New,
             name: None,
-            imp: None,
+            lock: unsafe { NativeMutex::new() },
+            awoken: false,
+            // these *should* get overwritten
+            stack_bounds: stack_bounds.unwrap_or((0, 0)),
+            stack_guard: stack_guard.unwrap_or(0)
         }
     }
 
+    pub fn spawn(opts: TaskOpts, f: proc():Send) {
+        let TaskOpts { name, stack_size, on_exit } = opts;
+
+        let mut task = box Task::new(None, None);
+        task.name = name;
+        task.death.on_exit = on_exit;
+
+        // FIXME: change this back after moving rustrt into std
+        // let stack = stack_size.unwrap_or(rt::min_stack());
+        let stack = stack_size.unwrap_or(2 * 1024 * 1024);
+
+        // Note that this increment must happen *before* the spawn in order to
+        // guarantee that if this task exits it will always end up waiting for
+        // the spawned task to exit.
+        let token = bookkeeping::increment();
+
+        // Spawning a new OS thread guarantees that __morestack will never get
+        // triggered, but we must manually set up the actual stack bounds once
+        // this function starts executing. This raises the lower limit by a bit
+        // because by the time that this function is executing we've already
+        // consumed at least a little bit of stack (we don't know the exact byte
+        // address at which our stack started).
+        Thread::spawn_stack(stack, proc() {
+            let something_around_the_top_of_the_stack = 1;
+            let addr = &something_around_the_top_of_the_stack as *const int;
+            let my_stack = addr as uint;
+            unsafe {
+                stack::record_os_managed_stack_bounds(my_stack - stack + 1024,
+                                                      my_stack);
+            }
+            task.stack_guard = thread::current_guard_page();
+            task.stack_bounds = (my_stack - stack + 1024, my_stack);
+
+            let mut f = Some(f);
+            drop(task.run(|| { f.take().unwrap()() }).destroy());
+            drop(token);
+        })
+    }
+
     /// Consumes ownership of a task, runs some code, and returns the task back.
     ///
     /// This function can be used as an emulated "try/catch" to interoperate
@@ -190,23 +180,6 @@ pub fn new() -> Task {
     ///
     /// It is invalid to call this function with a task that has been previously
     /// destroyed via a failed call to `run`.
-    ///
-    /// # Example
-    ///
-    /// ```no_run
-    /// extern crate native;
-    /// use std::uint;
-    /// # fn main() {
-    ///
-    /// // Create a new native task
-    /// let task = native::task::new((0, uint::MAX), 0);
-    ///
-    /// // Run some code once and then destroy this task
-    /// task.run(|| {
-    ///     println!("Hello with a native runtime!");
-    /// }).destroy();
-    /// # }
-    /// ```
     pub fn run(mut self: Box<Task>, f: ||) -> Box<Task> {
         assert!(!self.is_destroyed(), "cannot re-use a destroyed task");
 
@@ -329,111 +302,136 @@ fn cleanup(self: Box<Task>, result: Result) -> Box<Task> {
     /// Queries whether this can be destroyed or not.
     pub fn is_destroyed(&self) -> bool { self.state == Destroyed }
 
-    /// Inserts a runtime object into this task, transferring ownership to the
-    /// task. It is illegal to replace a previous runtime object in this task
-    /// with this argument.
-    pub fn put_runtime(&mut self, ops: Box<Runtime + Send + 'static>) {
-        assert!(self.imp.is_none());
-        self.imp = Some(ops);
-    }
-
-    /// Removes the runtime from this task, transferring ownership to the
-    /// caller.
-    pub fn take_runtime(&mut self) -> Box<Runtime + Send + 'static> {
-        assert!(self.imp.is_some());
-        self.imp.take().unwrap()
-    }
-
-    /// Attempts to extract the runtime as a specific type. If the runtime does
-    /// not have the provided type, then the runtime is not removed. If the
-    /// runtime does have the specified type, then it is removed and returned
-    /// (transfer of ownership).
-    ///
-    /// It is recommended to only use this method when *absolutely necessary*.
-    /// This function may not be available in the future.
-    pub fn maybe_take_runtime<T: 'static>(&mut self) -> Option<Box<T>> {
-        // This is a terrible, terrible function. The general idea here is to
-        // take the runtime, cast it to Box<Any>, check if it has the right
-        // type, and then re-cast it back if necessary. The method of doing
-        // this is pretty sketchy and involves shuffling vtables of trait
-        // objects around, but it gets the job done.
-        //
-        // FIXME: This function is a serious code smell and should be avoided at
-        //      all costs. I have yet to think of a method to avoid this
-        //      function, and I would be saddened if more usage of the function
-        //      crops up.
-        unsafe {
-            let imp = self.imp.take().unwrap();
-            let vtable = mem::transmute::<_, &raw::TraitObject>(&imp).vtable;
-            match imp.wrap().downcast::<T>() {
-                Ok(t) => Some(t),
-                Err(t) => {
-                    let data = mem::transmute::<_, raw::TraitObject>(t).data;
-                    let obj: Box<Runtime + Send + 'static> =
-                        mem::transmute(raw::TraitObject {
-                            vtable: vtable,
-                            data: data,
-                        });
-                    self.put_runtime(obj);
-                    None
-                }
-            }
-        }
-    }
-
-    /// Spawns a sibling to this task. The newly spawned task is configured with
-    /// the `opts` structure and will run `f` as the body of its code.
-    pub fn spawn_sibling(mut self: Box<Task>,
-                         opts: TaskOpts,
-                         f: proc(): Send) {
-        let ops = self.imp.take().unwrap();
-        ops.spawn_sibling(self, opts, f)
-    }
-
     /// Deschedules the current task, invoking `f` `amt` times. It is not
     /// recommended to use this function directly, but rather communication
     /// primitives in `std::comm` should be used.
+    //
+    // This function gets a little interesting. There are a few safety and
+    // ownership violations going on here, but this is all done in the name of
+    // shared state. Additionally, all of the violations are protected with a
+    // mutex, so in theory there are no races.
+    //
+    // The first thing we need to do is to get a pointer to the task's internal
+    // mutex. This address will not be changing (because the task is allocated
+    // on the heap). We must have this handle separately because the task will
+    // have its ownership transferred to the given closure. We're guaranteed,
+    // however, that this memory will remain valid because *this* is the current
+    // task's execution thread.
+    //
+    // The next weird part is where ownership of the task actually goes. We
+    // relinquish it to the `f` blocking function, but upon returning this
+    // function needs to replace the task back in TLS. There is no communication
+    // from the wakeup thread back to this thread about the task pointer, and
+    // there's really no need to. In order to get around this, we cast the task
+    // to a `uint` which is then used at the end of this function to cast back
+    // to a `Box<Task>` object. Naturally, this looks like it violates
+    // ownership semantics in that there may be two `Box<Task>` objects.
+    //
+    // The fun part is that the wakeup half of this implementation knows to
+    // "forget" the task on the other end. This means that the awakening half of
+    // things silently relinquishes ownership back to this thread, but not in a
+    // way that the compiler can understand. The task's memory is always valid
+    // for both tasks because these operations are all done inside of a mutex.
+    //
+    // You'll also find that if blocking fails (the `f` function hands the
+    // BlockedTask back to us), we will `mem::forget` the handles. The
+    // reasoning for this is the same logic as above in that the task silently
+    // transfers ownership via the `uint`, not through normal compiler
+    // semantics.
+    //
+    // On a mildly unrelated note, it should also be pointed out that OS
+    // condition variables are susceptible to spurious wakeups, which we need to
+    // be ready for. In order to accommodate for this fact, we have an extra
+    // `awoken` field which indicates whether we were actually woken up via some
+    // invocation of `reawaken`. This flag is only ever accessed inside the
+    // lock, so there's no need to make it atomic.
     pub fn deschedule(mut self: Box<Task>,
-                      amt: uint,
+                      times: uint,
                       f: |BlockedTask| -> ::core::result::Result<(), BlockedTask>) {
-        let ops = self.imp.take().unwrap();
-        ops.deschedule(amt, self, f)
+        unsafe {
+            let me = &mut *self as *mut Task;
+            let task = BlockedTask::block(self);
+
+            if times == 1 {
+                let guard = (*me).lock.lock();
+                (*me).awoken = false;
+                match f(task) {
+                    Ok(()) => {
+                        while !(*me).awoken {
+                            guard.wait();
+                        }
+                    }
+                    Err(task) => { mem::forget(task.wake()); }
+                }
+            } else {
+                let iter = task.make_selectable(times);
+                let guard = (*me).lock.lock();
+                (*me).awoken = false;
+
+                // Apply the given closure to all of the "selectable tasks",
+                // bailing on the first one that produces an error. Note that
+                // care must be taken such that when an error is occurred, we
+                // may not own the task, so we may still have to wait for the
+                // task to become available. In other words, if task.wake()
+                // returns `None`, then someone else has ownership and we must
+                // wait for their signal.
+                match iter.map(f).filter_map(|a| a.err()).next() {
+                    None => {}
+                    Some(task) => {
+                        match task.wake() {
+                            Some(task) => {
+                                mem::forget(task);
+                                (*me).awoken = true;
+                            }
+                            None => {}
+                        }
+                    }
+                }
+                while !(*me).awoken {
+                    guard.wait();
+                }
+            }
+            // put the task back in TLS, and everything is as it once was.
+            Local::put(mem::transmute(me));
+        }
     }
 
-    /// Wakes up a previously blocked task, optionally specifying whether the
-    /// current task can accept a change in scheduling. This function can only
-    /// be called on tasks that were previously blocked in `deschedule`.
+    /// Wakes up a previously blocked task. This function can only be
+    /// called on tasks that were previously blocked in `deschedule`.
+    //
+    // See the comments on `deschedule` for why the task is forgotten here, and
+    // why it's valid to do so.
     pub fn reawaken(mut self: Box<Task>) {
-        let ops = self.imp.take().unwrap();
-        ops.reawaken(self);
+        unsafe {
+            let me = &mut *self as *mut Task;
+            mem::forget(self);
+            let guard = (*me).lock.lock();
+            (*me).awoken = true;
+            guard.signal();
+        }
     }
 
     /// Yields control of this task to another task. This function will
     /// eventually return, but possibly not immediately. This is used as an
     /// opportunity to allow other tasks a chance to run.
-    pub fn yield_now(mut self: Box<Task>) {
-        let ops = self.imp.take().unwrap();
-        ops.yield_now(self);
-    }
-
-    /// Similar to `yield_now`, except that this function may immediately return
-    /// without yielding (depending on what the runtime decides to do).
-    pub fn maybe_yield(mut self: Box<Task>) {
-        let ops = self.imp.take().unwrap();
-        ops.maybe_yield(self);
+    pub fn yield_now() {
+        Thread::yield_now();
     }
 
     /// Returns the stack bounds for this task in (lo, hi) format. The stack
     /// bounds may not be known for all tasks, so the return value may be
     /// `None`.
     pub fn stack_bounds(&self) -> (uint, uint) {
-        self.imp.as_ref().unwrap().stack_bounds()
+        self.stack_bounds
     }
 
-    /// Returns whether it is legal for this task to block the OS thread that it
-    /// is running on.
-    pub fn can_block(&self) -> bool {
-        self.imp.as_ref().unwrap().can_block()
+    /// Returns the stack guard for this task, if known.
+    pub fn stack_guard(&self) -> Option<uint> {
+        if self.stack_guard != 0 {
+            Some(self.stack_guard)
+        } else {
+            None
+        }
     }
 
     /// Consume this task, flagging it as a candidate for destruction.
index b97e80d0dc1d06e778719e6138917d86e35ee83e..8701fadf65c04c9389252b3501f95cc64f100157 100644 (file)
@@ -54,6 +54,8 @@
 // FIXME: this should not be here.
 #![allow(missing_docs)]
 
+#![allow(dead_code)]
+
 use failure;
 use rustrt;