]> git.lizzy.rs Git - rust.git/commitdiff
auto merge of #10070 : alexcrichton/rust/fewer-missiles, r=brson
authorbors <bors@rust-lang.org>
Sat, 26 Oct 2013 07:06:09 +0000 (00:06 -0700)
committerbors <bors@rust-lang.org>
Sat, 26 Oct 2013 07:06:09 +0000 (00:06 -0700)
This optimizes the `home_for_io` code path by requiring fewer scheduler
operations in some situtations.

When moving to your home scheduler, this no longer forces a context switch if
you're already on the home scheduler. Instead, the homing code now simply pins
you to your current scheduler (making it so you can't be stolen away). If you're
not on your home scheduler, then we context switch away, sending you to your
home scheduler.

When the I/O operation is done, then we also no longer forcibly trigger a
context switch. Instead, the action is cased on whether the task is homed or
not. If a task does not have a home, then the task is re-flagged as not having a
home and no context switch is performed. If a task is homed to the current
scheduler, then we don't do anything, and if the task is homed to a foreign
scheduler, then it's sent along its merry way.

I verified that there are about a third as many `write` syscalls done in print
operations now. Libuv uses write to implement async handles, and the homing
before and after each I/O operation was triggering a write on these async
handles. Additionally, using the terrible benchmark of printing 10k times in a
loop, this drives the runtime from 0.6s down to 0.3s (yay!).

27 files changed:
.gitmodules
src/libextra/arc.rs
src/libextra/comm.rs
src/librustc/middle/borrowck/gather_loans/mod.rs
src/librustc/middle/trans/base.rs
src/librustc/middle/trans/glue.rs
src/libstd/ptr.rs
src/libstd/rt/basic.rs [new file with mode: 0644]
src/libstd/rt/io/buffered.rs
src/libstd/rt/io/mod.rs
src/libstd/rt/io/stdio.rs
src/libstd/rt/logging.rs
src/libstd/rt/mod.rs
src/libstd/rt/rtio.rs
src/libstd/rt/sched.rs
src/libstd/rt/task.rs
src/libstd/rt/test.rs
src/libstd/rt/uv/uvio.rs
src/libstd/rt/uv/uvll.rs
src/libstd/select.rs
src/libstd/task/mod.rs
src/libstd/unstable/sync.rs
src/rt/rust_builtin.cpp
src/rt/rust_uv.cpp
src/rt/rustrt.def.in
src/test/run-fail/addr-of-bot.rs [new file with mode: 0644]
src/test/run-pass/field-destruction-order.rs [new file with mode: 0644]

index 97e327ebfa4f859d31da2c6f4458e2aaba2469e6..7e997334cecaf1b42baebddc4a17905603b8c9b7 100644 (file)
@@ -8,4 +8,4 @@
        branch = master
 [submodule "src/gyp"]
        path = src/gyp
-       url = https://git.chromium.org/external/gyp.git
+       url = https://github.com/brson/gyp.git
index 1ce52d1d2784a61b82cca5443b47e64109bcf4e5..df67b1c9cc1df6a3119ec48ea2c3263f62a2b6fa 100644 (file)
@@ -117,10 +117,12 @@ pub struct Arc<T> { priv x: UnsafeArc<T> }
  */
 impl<T:Freeze+Send> Arc<T> {
     /// Create an atomically reference counted wrapper.
+    #[inline]
     pub fn new(data: T) -> Arc<T> {
         Arc { x: UnsafeArc::new(data) }
     }
 
+    #[inline]
     pub fn get<'a>(&'a self) -> &'a T {
         unsafe { &*self.x.get_immut() }
     }
@@ -148,6 +150,7 @@ impl<T:Freeze + Send> Clone for Arc<T> {
     * object. However, one of the `arc` objects can be sent to another task,
     * allowing them to share the underlying data.
     */
+    #[inline]
     fn clone(&self) -> Arc<T> {
         Arc { x: self.x.clone() }
     }
@@ -167,6 +170,7 @@ pub struct MutexArc<T> { priv x: UnsafeArc<MutexArcInner<T>> }
 
 impl<T:Send> Clone for MutexArc<T> {
     /// Duplicate a mutex-protected Arc. See arc::clone for more details.
+    #[inline]
     fn clone(&self) -> MutexArc<T> {
         // NB: Cloning the underlying mutex is not necessary. Its reference
         // count would be exactly the same as the shared state's.
@@ -349,6 +353,7 @@ pub struct RWArc<T> {
 
 impl<T:Freeze + Send> Clone for RWArc<T> {
     /// Duplicate a rwlock-protected Arc. See arc::clone for more details.
+    #[inline]
     fn clone(&self) -> RWArc<T> {
         RWArc { x: self.x.clone() }
     }
index 4a3801827a21120f83ede468b37bca0e3b5ffc7b..5cc5c140fd5d1503afb21a4941039df056815a5c 100644 (file)
@@ -136,7 +136,7 @@ pub fn rendezvous<T: Send>() -> (SyncPort<T>, SyncChan<T>) {
 #[cfg(test)]
 mod test {
     use comm::{DuplexStream, rendezvous};
-    use std::rt::test::run_in_newsched_task;
+    use std::rt::test::run_in_uv_task;
     use std::task::spawn_unlinked;
 
 
@@ -165,7 +165,7 @@ pub fn basic_rendezvous_test() {
     #[test]
     fn recv_a_lot() {
         // Rendezvous streams should be able to handle any number of messages being sent
-        do run_in_newsched_task {
+        do run_in_uv_task {
             let (port, chan) = rendezvous();
             do spawn {
                 do 1000000.times { chan.send(()) }
index 6c927794dc8117f7e568747fdfbd8d19fe159602..b5523c2ca5d16a8af166b2f7673bea2b71be10c5 100644 (file)
@@ -230,12 +230,15 @@ fn gather_loans_in_expr(this: &mut GatherLoanCtxt,
 
         // make sure that the thing we are pointing out stays valid
         // for the lifetime `scope_r` of the resulting ptr:
-        let scope_r = ty_region(tcx, ex.span, ty::expr_ty(tcx, ex));
-        this.guarantee_valid(ex.id,
-                             ex.span,
-                             base_cmt,
-                             LoanMutability::from_ast_mutability(mutbl),
-                             scope_r);
+        let expr_ty = ty::expr_ty(tcx, ex);
+        if !ty::type_is_bot(expr_ty) {
+            let scope_r = ty_region(tcx, ex.span, expr_ty);
+            this.guarantee_valid(ex.id,
+                                 ex.span,
+                                 base_cmt,
+                                 LoanMutability::from_ast_mutability(mutbl),
+                                 scope_r);
+        }
         visit::walk_expr(this, ex, ());
       }
 
index 8b5167b7e8fa5257fbb636a36ce4836079c4cef3..4e3aa2a9893fb27e1929c5b93435dc696b13ecd3 100644 (file)
@@ -288,37 +288,6 @@ pub fn get_extern_const(externs: &mut ExternMap, llmod: ModuleRef,
         return c;
     }
 }
-pub fn umax(cx: @mut Block, a: ValueRef, b: ValueRef) -> ValueRef {
-    let _icx = push_ctxt("umax");
-    let cond = ICmp(cx, lib::llvm::IntULT, a, b);
-    return Select(cx, cond, b, a);
-}
-
-pub fn umin(cx: @mut Block, a: ValueRef, b: ValueRef) -> ValueRef {
-    let _icx = push_ctxt("umin");
-    let cond = ICmp(cx, lib::llvm::IntULT, a, b);
-    return Select(cx, cond, a, b);
-}
-
-// Given a pointer p, returns a pointer sz(p) (i.e., inc'd by sz bytes).
-// The type of the returned pointer is always i8*.  If you care about the
-// return type, use bump_ptr().
-pub fn ptr_offs(bcx: @mut Block, base: ValueRef, sz: ValueRef) -> ValueRef {
-    let _icx = push_ctxt("ptr_offs");
-    let raw = PointerCast(bcx, base, Type::i8p());
-    InBoundsGEP(bcx, raw, [sz])
-}
-
-// Increment a pointer by a given amount and then cast it to be a pointer
-// to a given type.
-pub fn bump_ptr(bcx: @mut Block, t: ty::t, base: ValueRef, sz: ValueRef) ->
-   ValueRef {
-    let _icx = push_ctxt("bump_ptr");
-    let ccx = bcx.ccx();
-    let bumped = ptr_offs(bcx, base, sz);
-    let typ = type_of(ccx, t).ptr_to();
-    PointerCast(bcx, bumped, typ)
-}
 
 // Returns a pointer to the body for the box. The box may be an opaque
 // box. The result will be casted to the type of body_t, if it is statically
@@ -434,10 +403,6 @@ pub fn malloc_general(bcx: @mut Block, t: ty::t, heap: heap) -> MallocResult {
     assert!(heap != heap_exchange);
     malloc_general_dyn(bcx, t, heap, llsize_of(bcx.ccx(), ty))
 }
-pub fn malloc_boxed(bcx: @mut Block, t: ty::t)
-    -> MallocResult {
-    malloc_general(bcx, t, heap_managed)
-}
 
 pub fn heap_for_unique(bcx: @mut Block, t: ty::t) -> heap {
     if ty::type_contents(bcx.tcx(), t).contains_managed() {
@@ -534,12 +499,6 @@ pub fn set_no_split_stack(f: ValueRef) {
     }
 }
 
-pub fn set_glue_inlining(f: ValueRef, t: ty::t) {
-    if ty::type_is_structural(t) {
-        set_optimize_for_size(f);
-    } else { set_always_inline(f); }
-}
-
 // Double-check that we never ask LLVM to declare the same symbol twice. It
 // silently mangles such symbols, breaking our linkage model.
 pub fn note_unique_llvm_symbol(ccx: &mut CrateContext, sym: @str) {
index aaa187931c967085075ff5bb3a50fb0a645faf03..2b0f13d2a7cd839c95849eb8d5ce7c299cf74499 100644 (file)
@@ -674,7 +674,6 @@ pub fn declare_generic_glue(ccx: &mut CrateContext, t: ty::t, llfnty: Type,
     debug!("{} is for type {}", fn_nm, ppaux::ty_to_str(ccx.tcx, t));
     note_unique_llvm_symbol(ccx, fn_nm);
     let llfn = decl_cdecl_fn(ccx.llmod, fn_nm, llfnty);
-    set_glue_inlining(llfn, t);
     return llfn;
 }
 
index 8803d39b0c67e3c5e41a468a908711bb2ca610e7..376d178f8f723475213c5ba4ed20d1270dd1afe1 100644 (file)
@@ -47,6 +47,13 @@ fn clone(&self) -> *T {
     }
 }
 
+impl<T> Clone for *mut T {
+    #[inline]
+    fn clone(&self) -> *mut T {
+        *self
+    }
+}
+
 /// Return the first offset `i` such that `f(buf[i]) == true`.
 #[inline]
 pub unsafe fn position<T>(buf: *T, f: &fn(&T) -> bool) -> uint {
diff --git a/src/libstd/rt/basic.rs b/src/libstd/rt/basic.rs
new file mode 100644 (file)
index 0000000..86d3f8a
--- /dev/null
@@ -0,0 +1,256 @@
+// 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.
+
+//! This is a basic event loop implementation not meant for any "real purposes"
+//! other than testing the scheduler and proving that it's possible to have a
+//! pluggable event loop.
+
+use prelude::*;
+
+use cast;
+use rt::rtio::{EventLoop, IoFactory, RemoteCallback, PausibleIdleCallback};
+use unstable::sync::Exclusive;
+use util;
+
+/// This is the only exported function from this module.
+pub fn event_loop() -> ~EventLoop {
+    ~BasicLoop::new() as ~EventLoop
+}
+
+struct BasicLoop {
+    work: ~[~fn()],               // pending work
+    idle: Option<*BasicPausible>, // only one is allowed
+    remotes: ~[(uint, ~fn())],
+    next_remote: uint,
+    messages: Exclusive<~[Message]>
+}
+
+enum Message { RunRemote(uint), RemoveRemote(uint) }
+
+struct Time {
+    sec: u64,
+    nsec: u64,
+}
+
+impl Ord for Time {
+    fn lt(&self, other: &Time) -> bool {
+        self.sec < other.sec || self.nsec < other.nsec
+    }
+}
+
+impl BasicLoop {
+    fn new() -> BasicLoop {
+        BasicLoop {
+            work: ~[],
+            idle: None,
+            next_remote: 0,
+            remotes: ~[],
+            messages: Exclusive::new(~[]),
+        }
+    }
+
+    /// Process everything in the work queue (continually)
+    fn work(&mut self) {
+        while self.work.len() > 0 {
+            for work in util::replace(&mut self.work, ~[]).move_iter() {
+                work();
+            }
+        }
+    }
+
+    fn remote_work(&mut self) {
+        let messages = unsafe {
+            do self.messages.with |messages| {
+                if messages.len() > 0 {
+                    Some(util::replace(messages, ~[]))
+                } else {
+                    None
+                }
+            }
+        };
+        let messages = match messages {
+            Some(m) => m, None => return
+        };
+        for message in messages.iter() {
+            self.message(*message);
+        }
+    }
+
+    fn message(&mut self, message: Message) {
+        match message {
+            RunRemote(i) => {
+                match self.remotes.iter().find(|& &(id, _)| id == i) {
+                    Some(&(_, ref f)) => (*f)(),
+                    None => unreachable!()
+                }
+            }
+            RemoveRemote(i) => {
+                match self.remotes.iter().position(|&(id, _)| id == i) {
+                    Some(i) => { self.remotes.remove(i); }
+                    None => unreachable!()
+                }
+            }
+        }
+    }
+
+    /// Run the idle callback if one is registered
+    fn idle(&mut self) {
+        unsafe {
+            match self.idle {
+                Some(idle) => {
+                    if (*idle).active {
+                        (*(*idle).work.get_ref())();
+                    }
+                }
+                None => {}
+            }
+        }
+    }
+
+    fn has_idle(&self) -> bool {
+        unsafe { self.idle.is_some() && (**self.idle.get_ref()).active }
+    }
+}
+
+impl EventLoop for BasicLoop {
+    fn run(&mut self) {
+        // Not exactly efficient, but it gets the job done.
+        while self.remotes.len() > 0 || self.work.len() > 0 || self.has_idle() {
+
+            self.work();
+            self.remote_work();
+
+            if self.has_idle() {
+                self.idle();
+                continue
+            }
+
+            unsafe {
+                // We block here if we have no messages to process and we may
+                // receive a message at a later date
+                do self.messages.hold_and_wait |messages| {
+                    self.remotes.len() > 0 &&
+                        messages.len() == 0 &&
+                        self.work.len() == 0
+                }
+            }
+        }
+    }
+
+    fn callback(&mut self, f: ~fn()) {
+        self.work.push(f);
+    }
+
+    // XXX: Seems like a really weird requirement to have an event loop provide.
+    fn pausible_idle_callback(&mut self) -> ~PausibleIdleCallback {
+        let callback = ~BasicPausible::new(self);
+        rtassert!(self.idle.is_none());
+        unsafe {
+            let cb_ptr: &*BasicPausible = cast::transmute(&callback);
+            self.idle = Some(*cb_ptr);
+        }
+        return callback as ~PausibleIdleCallback;
+    }
+
+    fn remote_callback(&mut self, f: ~fn()) -> ~RemoteCallback {
+        let id = self.next_remote;
+        self.next_remote += 1;
+        self.remotes.push((id, f));
+        ~BasicRemote::new(self.messages.clone(), id) as ~RemoteCallback
+    }
+
+    /// This has no bindings for local I/O
+    fn io<'a>(&'a mut self, _: &fn(&'a mut IoFactory)) {}
+}
+
+struct BasicRemote {
+    queue: Exclusive<~[Message]>,
+    id: uint,
+}
+
+impl BasicRemote {
+    fn new(queue: Exclusive<~[Message]>, id: uint) -> BasicRemote {
+        BasicRemote { queue: queue, id: id }
+    }
+}
+
+impl RemoteCallback for BasicRemote {
+    fn fire(&mut self) {
+        unsafe {
+            do self.queue.hold_and_signal |queue| {
+                queue.push(RunRemote(self.id));
+            }
+        }
+    }
+}
+
+impl Drop for BasicRemote {
+    fn drop(&mut self) {
+        unsafe {
+            do self.queue.hold_and_signal |queue| {
+                queue.push(RemoveRemote(self.id));
+            }
+        }
+    }
+}
+
+struct BasicPausible {
+    eloop: *mut BasicLoop,
+    work: Option<~fn()>,
+    active: bool,
+}
+
+impl BasicPausible {
+    fn new(eloop: &mut BasicLoop) -> BasicPausible {
+        BasicPausible {
+            active: false,
+            work: None,
+            eloop: eloop,
+        }
+    }
+}
+
+impl PausibleIdleCallback for BasicPausible {
+    fn start(&mut self, f: ~fn()) {
+        rtassert!(!self.active && self.work.is_none());
+        self.active = true;
+        self.work = Some(f);
+    }
+    fn pause(&mut self) {
+        self.active = false;
+    }
+    fn resume(&mut self) {
+        self.active = true;
+    }
+    fn close(&mut self) {
+        self.active = false;
+        self.work = None;
+    }
+}
+
+impl Drop for BasicPausible {
+    fn drop(&mut self) {
+        unsafe {
+            (*self.eloop).idle = None;
+        }
+    }
+}
+
+fn time() -> Time {
+    #[fixed_stack_segment]; #[inline(never)];
+    extern {
+        fn get_time(sec: &mut i64, nsec: &mut i32);
+    }
+    let mut sec = 0;
+    let mut nsec = 0;
+    unsafe { get_time(&mut sec, &mut nsec) }
+
+    Time { sec: sec as u64, nsec: nsec as u64 }
+}
index 9dcb35c806f7cb8512df97b2c467596e0666131d..47c8dbd35c61449c0074516ea17fa2b4ec06197a 100644 (file)
@@ -221,17 +221,48 @@ fn flush(&mut self) {
 }
 
 impl<W: Writer> Decorator<W> for BufferedWriter<W> {
-    fn inner(self) -> W {
-        self.inner
-    }
+    fn inner(self) -> W { self.inner }
+    fn inner_ref<'a>(&'a self) -> &'a W { &self.inner }
+    fn inner_mut_ref<'a>(&'a mut self) -> &'a mut W { &mut self.inner }
+}
 
-    fn inner_ref<'a>(&'a self) -> &'a W {
-        &self.inner
+/// Wraps a Writer and buffers output to it, flushing whenever a newline (0xa,
+/// '\n') is detected.
+///
+/// Note that this structure does NOT flush the output when dropped.
+pub struct LineBufferedWriter<W> {
+    priv inner: BufferedWriter<W>,
+}
+
+impl<W: Writer> LineBufferedWriter<W> {
+    /// Creates a new `LineBufferedWriter`
+    pub fn new(inner: W) -> LineBufferedWriter<W> {
+        // Lines typically aren't that long, don't use a giant buffer
+        LineBufferedWriter {
+            inner: BufferedWriter::with_capacity(1024, inner)
+        }
     }
+}
 
-    fn inner_mut_ref<'a>(&'a mut self) -> &'a mut W {
-        &mut self.inner
+impl<W: Writer> Writer for LineBufferedWriter<W> {
+    fn write(&mut self, buf: &[u8]) {
+        match buf.iter().position(|&b| b == '\n' as u8) {
+            Some(i) => {
+                self.inner.write(buf.slice_to(i + 1));
+                self.inner.flush();
+                self.inner.write(buf.slice_from(i + 1));
+            }
+            None => self.inner.write(buf),
+        }
     }
+
+    fn flush(&mut self) { self.inner.flush() }
+}
+
+impl<W: Writer> Decorator<W> for LineBufferedWriter<W> {
+    fn inner(self) -> W { self.inner.inner() }
+    fn inner_ref<'a>(&'a self) -> &'a W { self.inner.inner_ref() }
+    fn inner_mut_ref<'a>(&'a mut self) -> &'a mut W { self.inner.inner_mut_ref() }
 }
 
 struct InternalBufferedWriter<W>(BufferedWriter<W>);
@@ -413,4 +444,19 @@ fn test_read_until() {
         assert_eq!(reader.read_until(8), Some(~[0]));
         assert_eq!(reader.read_until(9), None);
     }
+
+    #[test]
+    fn test_line_buffer() {
+        let mut writer = LineBufferedWriter::new(MemWriter::new());
+        writer.write([0]);
+        assert_eq!(*writer.inner_ref().inner_ref(), ~[]);
+        writer.write([1]);
+        assert_eq!(*writer.inner_ref().inner_ref(), ~[]);
+        writer.flush();
+        assert_eq!(*writer.inner_ref().inner_ref(), ~[0, 1]);
+        writer.write([0, '\n' as u8, 1]);
+        assert_eq!(*writer.inner_ref().inner_ref(), ~[0, 1, 0, '\n' as u8]);
+        writer.flush();
+        assert_eq!(*writer.inner_ref().inner_ref(), ~[0, 1, 0, '\n' as u8, 1]);
+    }
 }
index 758c97791658f878094960a97298377f34bdd999..decf801d59294faf174f182b35913b05073a687d 100644 (file)
@@ -606,6 +606,13 @@ pub fn standard_error(kind: IoErrorKind) -> IoError {
                 detail: None
             }
         }
+        IoUnavailable => {
+            IoError {
+                kind: IoUnavailable,
+                desc: "I/O is unavailable",
+                detail: None
+            }
+        }
         _ => fail!()
     }
 }
index b922e6400cc50801b91fa097cdf0505b6dee3da2..3f34d32b350b4a2a3b9ce85cb82bf1a11dc2e2b0 100644 (file)
@@ -30,6 +30,7 @@
 use libc;
 use option::{Option, Some, None};
 use result::{Ok, Err};
+use rt::io::buffered::{LineBufferedWriter, BufferedWriter};
 use rt::rtio::{IoFactory, RtioTTY, RtioFileStream, with_local_io,
                CloseAsynchronously};
 use super::{Reader, Writer, io_error, IoError, OtherIoError};
@@ -111,37 +112,78 @@ pub fn stderr() -> StdWriter {
     do src(libc::STDERR_FILENO, false) |src| { StdWriter { inner: src } }
 }
 
+/// Executes a closure with the local task's handle on stdout. By default, this
+/// stream is a buffering stream, so the handled yielded to the given closure
+/// can be used to flush the stdout stream (if necessary). The buffering used is
+/// line-buffering when stdout is attached to a terminal, and a fixed sized
+/// buffer if it is not attached to a terminal.
+///
+/// Note that handles generated via the `stdout()` function all output to the
+/// same stream, and output among all task may be interleaved as a result of
+/// this. This is provided to have access to the default stream for `print` and
+/// `println` (and the related macros) for this task.
+///
+/// Also note that logging macros do not use this stream. Using the logging
+/// macros will emit output to stderr.
+pub fn with_task_stdout(f: &fn(&mut Writer)) {
+    use rt::local::Local;
+    use rt::task::Task;
+
+    unsafe {
+        // Logging may require scheduling operations, so we can't remove the
+        // task from TLS right now, hence the unsafe borrow. Sad.
+        let task: *mut Task = Local::unsafe_borrow();
+
+        match (*task).stdout_handle {
+            Some(ref mut handle) => f(*handle),
+            None => {
+                let handle = stdout();
+                let mut handle = if handle.isatty() {
+                    ~LineBufferedWriter::new(handle) as ~Writer
+                } else {
+                    // The default capacity is very large, 64k, but this is just
+                    // a stdout stream, and possibly per task, so let's not make
+                    // this too expensive.
+                    ~BufferedWriter::with_capacity(4096, handle) as ~Writer
+                };
+                f(handle);
+                (*task).stdout_handle = Some(handle);
+            }
+        }
+    }
+}
+
 /// Prints a string to the stdout of the current process. No newline is emitted
 /// after the string is printed.
 pub fn print(s: &str) {
-    // XXX: need to see if not caching stdin() is the cause of performance
-    //      issues, it should be possible to cache a stdout handle in each Task
-    //      and then re-use that across calls to print/println. Note that the
-    //      resolution of this comment will affect all of the prints below as
-    //      well.
-    stdout().write(s.as_bytes());
+    do with_task_stdout |io| {
+        io.write(s.as_bytes());
+    }
 }
 
 /// Prints a string as a line. to the stdout of the current process. A literal
 /// `\n` character is printed to the console after the string.
 pub fn println(s: &str) {
-    let mut out = stdout();
-    out.write(s.as_bytes());
-    out.write(['\n' as u8]);
+    do with_task_stdout |io| {
+        io.write(s.as_bytes());
+        io.write(['\n' as u8]);
+    }
 }
 
 /// Similar to `print`, but takes a `fmt::Arguments` structure to be compatible
 /// with the `format_args!` macro.
 pub fn print_args(fmt: &fmt::Arguments) {
-    let mut out = stdout();
-    fmt::write(&mut out as &mut Writer, fmt);
+    do with_task_stdout |io| {
+        fmt::write(io, fmt);
+    }
 }
 
 /// Similar to `println`, but takes a `fmt::Arguments` structure to be
 /// compatible with the `format_args!` macro.
 pub fn println_args(fmt: &fmt::Arguments) {
-    let mut out = stdout();
-    fmt::writeln(&mut out as &mut Writer, fmt);
+    do with_task_stdout |io| {
+        fmt::writeln(io, fmt);
+    }
 }
 
 /// Representation of a reader of a standard input stream
index 31650ede7000d240b428e8300e5179499fa89de6..cb66d6f6199ae90240cf4142b2d10b7a49d8e029 100644 (file)
@@ -13,6 +13,8 @@
 use libc::exit;
 use option::{Some, None, Option};
 use rt::io;
+use rt::io::stdio::StdWriter;
+use rt::io::buffered::LineBufferedWriter;
 use rt::crate_map::{ModEntry, CrateMap, iter_crate_map, get_crate_map};
 use str::StrSlice;
 use u32;
@@ -170,7 +172,7 @@ pub trait Logger {
 /// This logger emits output to the stderr of the process, and contains a lazily
 /// initialized event-loop driven handle to the stream.
 pub struct StdErrLogger {
-    priv handle: Option<io::stdio::StdWriter>,
+    priv handle: Option<LineBufferedWriter<StdWriter>>,
 }
 
 impl StdErrLogger {
@@ -181,7 +183,7 @@ impl Logger for StdErrLogger {
     fn log(&mut self, args: &fmt::Arguments) {
         // First time logging? Get a handle to the stderr of this process.
         if self.handle.is_none() {
-            self.handle = Some(io::stderr());
+            self.handle = Some(LineBufferedWriter::new(io::stderr()));
         }
         fmt::writeln(self.handle.get_mut_ref() as &mut io::Writer, args);
     }
index 66d7a6bf48823b3f80df0610f67a0a4290043558..5113c28aa085e3a4ca429f120a913f932813fe4b 100644 (file)
@@ -102,6 +102,9 @@ pub mod shouldnt_be_public {
 // Internal macros used by the runtime.
 mod macros;
 
+/// Basic implementation of an EventLoop, provides no I/O interfaces
+mod basic;
+
 /// The global (exchange) heap.
 pub mod global_heap;
 
index 66a0676a2f410655649da3278e6e49f6749e3b7e..29f728a5e0cb8051553cdebff094f79675c0ce37 100644 (file)
@@ -28,7 +28,6 @@ pub trait EventLoop {
     fn run(&mut self);
     fn callback(&mut self, ~fn());
     fn pausible_idle_callback(&mut self) -> ~PausibleIdleCallback;
-    fn callback_ms(&mut self, ms: u64, ~fn());
     fn remote_callback(&mut self, ~fn()) -> ~RemoteCallback;
 
     /// The asynchronous I/O services. Not all event loops may provide one
index 26f287d2f879231de9c027e5338b467cd424a5ab..9965380d9dc31ab6cf1a21a8d3a1081655962133 100644 (file)
@@ -62,8 +62,6 @@ pub struct Scheduler {
     /// no longer try to go to sleep, but exit instead.
     no_sleep: bool,
     stack_pool: StackPool,
-    /// The event loop used to drive the scheduler and perform I/O
-    event_loop: ~EventLoop,
     /// The scheduler runs on a special task. When it is not running
     /// it is stored here instead of the work queue.
     priv sched_task: Option<~Task>,
@@ -85,7 +83,17 @@ pub struct Scheduler {
     priv yield_check_count: uint,
     /// A flag to tell the scheduler loop it needs to do some stealing
     /// in order to introduce randomness as part of a yield
-    priv steal_for_yield: bool
+    priv steal_for_yield: bool,
+
+    // n.b. currently destructors of an object are run in top-to-bottom in order
+    //      of field declaration. Due to its nature, the pausible idle callback
+    //      must have some sort of handle to the event loop, so it needs to get
+    //      destroyed before the event loop itself. For this reason, we destroy
+    //      the event loop last to ensure that any unsafe references to it are
+    //      destroyed before it's actually destroyed.
+
+    /// The event loop used to drive the scheduler and perform I/O
+    event_loop: ~EventLoop,
 }
 
 /// An indication of how hard to work on a given operation, the difference
@@ -912,7 +920,7 @@ mod test {
     use cell::Cell;
     use rt::thread::Thread;
     use rt::task::{Task, Sched};
-    use rt::rtio::EventLoop;
+    use rt::basic;
     use rt::util;
     use option::{Some};
 
@@ -1012,7 +1020,6 @@ fn test_home_sched() {
     #[test]
     fn test_schedule_home_states() {
 
-        use rt::uv::uvio::UvEventLoop;
         use rt::sleeper_list::SleeperList;
         use rt::work_queue::WorkQueue;
         use rt::sched::Shutdown;
@@ -1028,7 +1035,7 @@ fn test_schedule_home_states() {
 
             // Our normal scheduler
             let mut normal_sched = ~Scheduler::new(
-                ~UvEventLoop::new() as ~EventLoop,
+                basic::event_loop(),
                 normal_queue,
                 queues.clone(),
                 sleepers.clone());
@@ -1039,7 +1046,7 @@ fn test_schedule_home_states() {
 
             // Our special scheduler
             let mut special_sched = ~Scheduler::new_special(
-                ~UvEventLoop::new() as ~EventLoop,
+                basic::event_loop(),
                 special_queue.clone(),
                 queues.clone(),
                 sleepers.clone(),
@@ -1144,22 +1151,15 @@ fn test_stress_schedule_task_states() {
 
     #[test]
     fn test_io_callback() {
+        use rt::io::timer;
+
         // This is a regression test that when there are no schedulable tasks
         // in the work queue, but we are performing I/O, that once we do put
         // something in the work queue again the scheduler picks it up and doesn't
         // exit before emptying the work queue
-        do run_in_newsched_task {
+        do run_in_uv_task {
             do spawntask {
-                let sched: ~Scheduler = Local::take();
-                do sched.deschedule_running_task_and_then |sched, task| {
-                    let task = Cell::new(task);
-                    do sched.event_loop.callback_ms(10) {
-                        rtdebug!("in callback");
-                        let mut sched: ~Scheduler = Local::take();
-                        sched.enqueue_blocked_task(task.take());
-                        Local::put(sched);
-                    }
-                }
+                timer::sleep(10);
             }
         }
     }
@@ -1199,7 +1199,6 @@ fn no_missed_messages() {
         use rt::work_queue::WorkQueue;
         use rt::sleeper_list::SleeperList;
         use rt::stack::StackPool;
-        use rt::uv::uvio::UvEventLoop;
         use rt::sched::{Shutdown, TaskFromFriend};
         use util;
 
@@ -1210,7 +1209,7 @@ fn no_missed_messages() {
                 let queues = ~[queue.clone()];
 
                 let mut sched = ~Scheduler::new(
-                    ~UvEventLoop::new() as ~EventLoop,
+                    basic::event_loop(),
                     queue,
                     queues.clone(),
                     sleepers.clone());
index 1ea68bb52d7e09de5d97f183ea58b7daacbca499..f82eb929a3959ab870b58c29b5229387fe870d7d 100644 (file)
@@ -23,6 +23,7 @@
 use rt::borrowck;
 use rt::borrowck::BorrowRecord;
 use rt::env;
+use rt::io::Writer;
 use rt::kill::Death;
 use rt::local::Local;
 use rt::logging::StdErrLogger;
@@ -56,7 +57,8 @@ pub struct Task {
     sched: Option<~Scheduler>,
     task_type: TaskType,
     // Dynamic borrowck debugging info
-    borrow_list: Option<~[BorrowRecord]>
+    borrow_list: Option<~[BorrowRecord]>,
+    stdout_handle: Option<~Writer>,
 }
 
 pub enum TaskType {
@@ -141,7 +143,8 @@ pub fn new_sched_task() -> Task {
             name: None,
             sched: None,
             task_type: SchedTask,
-            borrow_list: None
+            borrow_list: None,
+            stdout_handle: None,
         }
     }
 
@@ -175,7 +178,8 @@ pub fn new_root_homed(stack_pool: &mut StackPool,
             coroutine: Some(Coroutine::new(stack_pool, stack_size, start)),
             sched: None,
             task_type: GreenTask(Some(home)),
-            borrow_list: None
+            borrow_list: None,
+            stdout_handle: None,
         }
     }
 
@@ -198,7 +202,8 @@ pub fn new_child_homed(&mut self,
             coroutine: Some(Coroutine::new(stack_pool, stack_size, start)),
             sched: None,
             task_type: GreenTask(Some(home)),
-            borrow_list: None
+            borrow_list: None,
+            stdout_handle: None,
         }
     }
 
@@ -234,6 +239,7 @@ pub fn run(&mut self, f: &fn()) {
 
             // Run the task main function, then do some cleanup.
             do f.finally {
+
                 // First, destroy task-local storage. This may run user dtors.
                 //
                 // FIXME #8302: Dear diary. I'm so tired and confused.
@@ -257,6 +263,17 @@ pub fn run(&mut self, f: &fn()) {
 
                 // Destroy remaining boxes. Also may run user dtors.
                 unsafe { cleanup::annihilate(); }
+
+                // Finally flush and destroy any output handles which the task
+                // owns. There are no boxes here, and no user destructors should
+                // run after this any more.
+                match self.stdout_handle.take() {
+                    Some(handle) => {
+                        let mut handle = handle;
+                        handle.flush();
+                    }
+                    None => {}
+                }
             }
         }
 
@@ -331,7 +348,7 @@ pub fn on_appropriate_sched() -> bool {
 impl Drop for Task {
     fn drop(&mut self) {
         rtdebug!("called drop for a task: {}", borrow::to_uint(self));
-        rtassert!(self.destroyed)
+        rtassert!(self.destroyed);
     }
 }
 
@@ -637,7 +654,7 @@ fn unwind() {
 
     #[test]
     fn rng() {
-        do run_in_newsched_task() {
+        do run_in_uv_task() {
             use rand::{rng, Rng};
             let mut r = rng();
             let _ = r.next_u32();
@@ -646,7 +663,7 @@ fn rng() {
 
     #[test]
     fn logging() {
-        do run_in_newsched_task() {
+        do run_in_uv_task() {
             info!("here i am. logging in a newsched task");
         }
     }
index c238b1dfba16a1527a3fc20cc80e1ace4d0f1b95..e4bbfe0a5a3e99be4016b1caa7fcbb9426920a5b 100644 (file)
@@ -21,6 +21,7 @@
 use super::io::net::ip::{SocketAddr, Ipv4Addr, Ipv6Addr};
 use vec::{OwnedVector, MutableVector, ImmutableVector};
 use path::GenericPath;
+use rt::basic;
 use rt::sched::Scheduler;
 use rt::rtio::EventLoop;
 use unstable::{run_in_bare_thread};
@@ -48,6 +49,28 @@ pub fn new_test_uv_sched() -> Scheduler {
 
 }
 
+pub fn new_test_sched() -> Scheduler {
+
+    let queue = WorkQueue::new();
+    let queues = ~[queue.clone()];
+
+    let mut sched = Scheduler::new(basic::event_loop(),
+                                   queue,
+                                   queues,
+                                   SleeperList::new());
+
+    // Don't wait for the Shutdown message
+    sched.no_sleep = true;
+    return sched;
+}
+
+pub fn run_in_uv_task(f: ~fn()) {
+    let f = Cell::new(f);
+    do run_in_bare_thread {
+        run_in_uv_task_core(f.take());
+    }
+}
+
 pub fn run_in_newsched_task(f: ~fn()) {
     let f = Cell::new(f);
     do run_in_bare_thread {
@@ -55,7 +78,7 @@ pub fn run_in_newsched_task(f: ~fn()) {
     }
 }
 
-pub fn run_in_newsched_task_core(f: ~fn()) {
+pub fn run_in_uv_task_core(f: ~fn()) {
 
     use rt::sched::Shutdown;
 
@@ -72,6 +95,23 @@ pub fn run_in_newsched_task_core(f: ~fn()) {
     sched.bootstrap(task);
 }
 
+pub fn run_in_newsched_task_core(f: ~fn()) {
+
+    use rt::sched::Shutdown;
+
+    let mut sched = ~new_test_sched();
+    let exit_handle = Cell::new(sched.make_handle());
+
+    let on_exit: ~fn(bool) = |exit_status| {
+        exit_handle.take().send(Shutdown);
+        rtassert!(exit_status);
+    };
+    let mut task = ~Task::new_root(&mut sched.stack_pool, None, f);
+    task.death.on_exit = Some(on_exit);
+
+    sched.bootstrap(task);
+}
+
 #[cfg(target_os="macos")]
 #[allow(non_camel_case_types)]
 mod darwin_fd_limit {
@@ -310,7 +350,7 @@ pub fn spawntask_thread(f: ~fn()) -> Thread {
 /// Get a ~Task for testing purposes other than actually scheduling it.
 pub fn with_test_task(blk: ~fn(~Task) -> ~Task) {
     do run_in_bare_thread {
-        let mut sched = ~new_test_uv_sched();
+        let mut sched = ~new_test_sched();
         let task = blk(~Task::new_root(&mut sched.stack_pool, None, ||{}));
         cleanup_task(task);
     }
index 63abd6eb92d7c2efc86058c423bbdb22b2eb07fd..dc202ecc174aed1fa656fa6299d22186226638c2 100644 (file)
@@ -228,15 +228,6 @@ fn pausible_idle_callback(&mut self) -> ~PausibleIdleCallback {
         } as ~PausibleIdleCallback
     }
 
-    fn callback_ms(&mut self, ms: u64, f: ~fn()) {
-        let mut timer =  TimerWatcher::new(self.uvio.uv_loop());
-        do timer.start(ms, 0) |timer, status| {
-            assert!(status.is_none());
-            timer.close(||());
-            f();
-        }
-    }
-
     fn remote_callback(&mut self, f: ~fn()) -> ~RemoteCallback {
         ~UvRemoteCallback::new(self.uvio.uv_loop(), f) as ~RemoteCallback
     }
@@ -1797,7 +1788,7 @@ fn get_winsize(&mut self) -> Result<(int, int), IoError> {
     }
 
     fn isatty(&self) -> bool {
-        unsafe { uvll::guess_handle(self.fd) == uvll::UV_TTY }
+        unsafe { uvll::guess_handle(self.fd) == uvll::UV_TTY as c_int }
     }
 }
 
index 75e6a0c6ca552f919039677dfc68c6c2524c72af..18e95c8966e18eb2d7c4de039cf07d8db2f0a984 100644 (file)
@@ -977,7 +977,8 @@ pub unsafe fn tty_get_winsize(tty: *uv_tty_t, width: *c_int,
     #[fixed_stack_segment]; #[inline(never)];
     rust_uv_tty_get_winsize(tty, width, height)
 }
-pub unsafe fn guess_handle(fd: c_int) -> uv_handle_type {
+// FIXME(#9613) this should return uv_handle_type, not a c_int
+pub unsafe fn guess_handle(fd: c_int) -> c_int {
     #[fixed_stack_segment]; #[inline(never)];
     rust_uv_guess_handle(fd)
 }
@@ -1148,7 +1149,7 @@ fn rust_uv_tty_init(loop_ptr: *uv_loop_t, tty: *uv_tty_t, fd: c_int,
     fn rust_uv_tty_set_mode(tty: *uv_tty_t, mode: c_int) -> c_int;
     fn rust_uv_tty_get_winsize(tty: *uv_tty_t, width: *c_int,
                                height: *c_int) -> c_int;
-    fn rust_uv_guess_handle(fd: c_int) -> uv_handle_type;
+    fn rust_uv_guess_handle(fd: c_int) -> c_int;
 
     // XXX: see comments in addrinfo.rs
     // These should all really be constants...
index 62a09984794978671703af45600f3d73cec8b3de..75b09187f04ccfb47aa3806436bf2ea029a7430c 100644 (file)
@@ -183,7 +183,7 @@ fn select_helper(num_ports: uint, send_on_chans: &[uint]) {
 
     #[test]
     fn select_one() {
-        do run_in_newsched_task { select_helper(1, [0]) }
+        do run_in_uv_task { select_helper(1, [0]) }
     }
 
     #[test]
@@ -191,14 +191,14 @@ fn select_two() {
         // NB. I would like to have a test that tests the first one that is
         // ready is the one that's returned, but that can't be reliably tested
         // with the randomized behaviour of optimistic_check.
-        do run_in_newsched_task { select_helper(2, [1]) }
-        do run_in_newsched_task { select_helper(2, [0]) }
-        do run_in_newsched_task { select_helper(2, [1,0]) }
+        do run_in_uv_task { select_helper(2, [1]) }
+        do run_in_uv_task { select_helper(2, [0]) }
+        do run_in_uv_task { select_helper(2, [1,0]) }
     }
 
     #[test]
     fn select_a_lot() {
-        do run_in_newsched_task { select_helper(12, [7,8,9]) }
+        do run_in_uv_task { select_helper(12, [7,8,9]) }
     }
 
     #[test]
@@ -208,7 +208,7 @@ fn select_stream() {
 
         // Sends 10 buffered packets, and uses select to retrieve them all.
         // Puts the port in a different spot in the vector each time.
-        do run_in_newsched_task {
+        do run_in_uv_task {
             let (ports, _) = unzip(range(0u, 10).map(|_| stream::<int>()));
             let (port, chan) = stream();
             do 10.times { chan.send(31337); }
@@ -229,7 +229,7 @@ fn select_stream() {
 
     #[test]
     fn select_unkillable() {
-        do run_in_newsched_task {
+        do run_in_uv_task {
             do task::unkillable { select_helper(2, [1]) }
         }
     }
@@ -242,7 +242,7 @@ fn select_blocking() {
         select_blocking_helper(false);
 
         fn select_blocking_helper(killable: bool) {
-            do run_in_newsched_task {
+            do run_in_uv_task {
                 let (p1,_c) = oneshot();
                 let (p2,c2) = oneshot();
                 let mut ports = [p1,p2];
@@ -287,7 +287,7 @@ fn select_racing_senders() {
         fn select_racing_senders_helper(killable: bool, send_on_chans: ~[uint]) {
             use rt::test::spawntask_random;
 
-            do run_in_newsched_task {
+            do run_in_uv_task {
                 // A bit of stress, since ordinarily this is just smoke and mirrors.
                 do 4.times {
                     let send_on_chans = send_on_chans.clone();
@@ -318,7 +318,7 @@ fn select_racing_senders_helper(killable: bool, send_on_chans: ~[uint]) {
 
     #[test]
     fn select_killed() {
-        do run_in_newsched_task {
+        do run_in_uv_task {
             let (success_p, success_c) = oneshot::<bool>();
             let success_c = Cell::new(success_c);
             do task::try {
index 30c99c628853fed6114c2cd3d3dd37889069f784..b72d6773ec567774bed2c38f30d0d7374aee39cf 100644 (file)
@@ -645,7 +645,7 @@ fn test_kill_unkillable_task() {
     // CPU, *after* the spawner is already switched-back-to (and passes the
     // killed check at the start of its timeslice). As far as I know, it's not
     // possible to make this race deterministic, or even more likely to happen.
-    do run_in_newsched_task {
+    do run_in_uv_task {
         do task::try {
             do task::spawn {
                 fail!();
@@ -662,7 +662,7 @@ fn test_kill_rekillable_task() {
 
     // Tests that when a kill signal is received, 'rekillable' and
     // 'unkillable' unwind correctly in conjunction with each other.
-    do run_in_newsched_task {
+    do run_in_uv_task {
         do task::try {
             do task::unkillable {
                 do task::rekillable {
@@ -730,8 +730,8 @@ fn test_cant_dup_task_builder() {
 #[ignore(reason = "linked failure")]
 #[test]
 fn test_spawn_unlinked_unsup_no_fail_down() { // grandchild sends on a port
-    use rt::test::run_in_newsched_task;
-    do run_in_newsched_task {
+    use rt::test::run_in_uv_task;
+    do run_in_uv_task {
         let (po, ch) = stream();
         let ch = SharedChan::new(ch);
         do spawn_unlinked {
@@ -749,16 +749,16 @@ fn test_spawn_unlinked_unsup_no_fail_down() { // grandchild sends on a port
 #[ignore(reason = "linked failure")]
 #[test]
 fn test_spawn_unlinked_unsup_no_fail_up() { // child unlinked fails
-    use rt::test::run_in_newsched_task;
-    do run_in_newsched_task {
+    use rt::test::run_in_uv_task;
+    do run_in_uv_task {
         do spawn_unlinked { fail!(); }
     }
 }
 #[ignore(reason = "linked failure")]
 #[test]
 fn test_spawn_unlinked_sup_no_fail_up() { // child unlinked fails
-    use rt::test::run_in_newsched_task;
-    do run_in_newsched_task {
+    use rt::test::run_in_uv_task;
+    do run_in_uv_task {
         do spawn_supervised { fail!(); }
         // Give child a chance to fail-but-not-kill-us.
         do 16.times { task::deschedule(); }
@@ -767,8 +767,8 @@ fn test_spawn_unlinked_sup_no_fail_up() { // child unlinked fails
 #[ignore(reason = "linked failure")]
 #[test]
 fn test_spawn_unlinked_sup_fail_down() {
-    use rt::test::run_in_newsched_task;
-    do run_in_newsched_task {
+    use rt::test::run_in_uv_task;
+    do run_in_uv_task {
         let result: Result<(),()> = do try {
             do spawn_supervised { block_forever(); }
             fail!(); // Shouldn't leave a child hanging around.
@@ -780,8 +780,8 @@ fn test_spawn_unlinked_sup_fail_down() {
 #[ignore(reason = "linked failure")]
 #[test]
 fn test_spawn_linked_sup_fail_up() { // child fails; parent fails
-    use rt::test::run_in_newsched_task;
-    do run_in_newsched_task {
+    use rt::test::run_in_uv_task;
+    do run_in_uv_task {
         let result: Result<(),()> = do try {
             // Unidirectional "parenting" shouldn't override bidirectional linked.
             // We have to cheat with opts - the interface doesn't support them because
@@ -801,8 +801,8 @@ fn test_spawn_unlinked_sup_fail_down() {
 #[ignore(reason = "linked failure")]
 #[test]
 fn test_spawn_linked_sup_fail_down() { // parent fails; child fails
-    use rt::test::run_in_newsched_task;
-    do run_in_newsched_task {
+    use rt::test::run_in_uv_task;
+    do run_in_uv_task {
         let result: Result<(),()> = do try {
             // We have to cheat with opts - the interface doesn't support them because
             // they don't make sense (redundant with task().supervised()).
@@ -818,8 +818,8 @@ fn test_spawn_unlinked_sup_fail_down() {
 #[ignore(reason = "linked failure")]
 #[test]
 fn test_spawn_linked_unsup_fail_up() { // child fails; parent fails
-    use rt::test::run_in_newsched_task;
-    do run_in_newsched_task {
+    use rt::test::run_in_uv_task;
+    do run_in_uv_task {
         let result: Result<(),()> = do try {
             // Default options are to spawn linked & unsupervised.
             do spawn { fail!(); }
@@ -831,8 +831,8 @@ fn test_spawn_unlinked_sup_fail_down() {
 #[ignore(reason = "linked failure")]
 #[test]
 fn test_spawn_linked_unsup_fail_down() { // parent fails; child fails
-    use rt::test::run_in_newsched_task;
-    do run_in_newsched_task {
+    use rt::test::run_in_uv_task;
+    do run_in_uv_task {
         let result: Result<(),()> = do try {
             // Default options are to spawn linked & unsupervised.
             do spawn { block_forever(); }
@@ -844,8 +844,8 @@ fn test_spawn_unlinked_sup_fail_down() {
 #[ignore(reason = "linked failure")]
 #[test]
 fn test_spawn_linked_unsup_default_opts() { // parent fails; child fails
-    use rt::test::run_in_newsched_task;
-    do run_in_newsched_task {
+    use rt::test::run_in_uv_task;
+    do run_in_uv_task {
         let result: Result<(),()> = do try {
             // Make sure the above test is the same as this one.
             let mut builder = task();
@@ -863,8 +863,8 @@ fn test_spawn_unlinked_sup_fail_down() {
 #[ignore(reason = "linked failure")]
 #[test]
 fn test_spawn_failure_propagate_grandchild() {
-    use rt::test::run_in_newsched_task;
-    do run_in_newsched_task {
+    use rt::test::run_in_uv_task;
+    do run_in_uv_task {
         let result: Result<(),()> = do try {
             // Middle task exits; does grandparent's failure propagate across the gap?
             do spawn_supervised {
@@ -880,8 +880,8 @@ fn test_spawn_failure_propagate_grandchild() {
 #[ignore(reason = "linked failure")]
 #[test]
 fn test_spawn_failure_propagate_secondborn() {
-    use rt::test::run_in_newsched_task;
-    do run_in_newsched_task {
+    use rt::test::run_in_uv_task;
+    do run_in_uv_task {
         let result: Result<(),()> = do try {
             // First-born child exits; does parent's failure propagate to sibling?
             do spawn_supervised {
@@ -897,8 +897,8 @@ fn test_spawn_failure_propagate_secondborn() {
 #[ignore(reason = "linked failure")]
 #[test]
 fn test_spawn_failure_propagate_nephew_or_niece() {
-    use rt::test::run_in_newsched_task;
-    do run_in_newsched_task {
+    use rt::test::run_in_uv_task;
+    do run_in_uv_task {
         let result: Result<(),()> = do try {
             // Our sibling exits; does our failure propagate to sibling's child?
             do spawn { // linked
@@ -914,8 +914,8 @@ fn test_spawn_failure_propagate_nephew_or_niece() {
 #[ignore(reason = "linked failure")]
 #[test]
 fn test_spawn_linked_sup_propagate_sibling() {
-    use rt::test::run_in_newsched_task;
-    do run_in_newsched_task {
+    use rt::test::run_in_uv_task;
+    do run_in_uv_task {
         let result: Result<(),()> = do try {
             // Middle sibling exits - does eldest's failure propagate to youngest?
             do spawn { // linked
@@ -930,9 +930,9 @@ fn test_spawn_linked_sup_propagate_sibling() {
 
 #[test]
 fn test_unnamed_task() {
-    use rt::test::run_in_newsched_task;
+    use rt::test::run_in_uv_task;
 
-    do run_in_newsched_task {
+    do run_in_uv_task {
         do spawn {
             do with_task_name |name| {
                 assert!(name.is_none());
@@ -943,9 +943,9 @@ fn test_unnamed_task() {
 
 #[test]
 fn test_owned_named_task() {
-    use rt::test::run_in_newsched_task;
+    use rt::test::run_in_uv_task;
 
-    do run_in_newsched_task {
+    do run_in_uv_task {
         let mut t = task();
         t.name(~"ada lovelace");
         do t.spawn {
@@ -958,9 +958,9 @@ fn test_owned_named_task() {
 
 #[test]
 fn test_static_named_task() {
-    use rt::test::run_in_newsched_task;
+    use rt::test::run_in_uv_task;
 
-    do run_in_newsched_task {
+    do run_in_uv_task {
         let mut t = task();
         t.name("ada lovelace");
         do t.spawn {
@@ -973,9 +973,9 @@ fn test_static_named_task() {
 
 #[test]
 fn test_send_named_task() {
-    use rt::test::run_in_newsched_task;
+    use rt::test::run_in_uv_task;
 
-    do run_in_newsched_task {
+    do run_in_uv_task {
         let mut t = task();
         t.name("ada lovelace".into_send_str());
         do t.spawn {
@@ -1326,9 +1326,9 @@ fn child_no(x: uint) -> ~fn() {
 
 #[test]
 fn test_simple_newsched_spawn() {
-    use rt::test::run_in_newsched_task;
+    use rt::test::run_in_uv_task;
 
-    do run_in_newsched_task {
+    do run_in_uv_task {
         spawn(||())
     }
 }
@@ -1336,8 +1336,8 @@ fn test_simple_newsched_spawn() {
 #[ignore(reason = "linked failure")]
 #[test]
 fn test_spawn_watched() {
-    use rt::test::run_in_newsched_task;
-    do run_in_newsched_task {
+    use rt::test::run_in_uv_task;
+    do run_in_uv_task {
         let result = do try {
             let mut t = task();
             t.unlinked();
@@ -1359,8 +1359,8 @@ fn test_spawn_watched() {
 #[ignore(reason = "linked failure")]
 #[test]
 fn test_indestructible() {
-    use rt::test::run_in_newsched_task;
-    do run_in_newsched_task {
+    use rt::test::run_in_uv_task;
+    do run_in_uv_task {
         let result = do try {
             let mut t = task();
             t.watched();
index 9d15dd031e0dce2b51573cd5a0aabe78c3b9e5cf..2b036c318bac7464a0647af0dafef24ac13915fa 100644 (file)
@@ -334,6 +334,23 @@ pub unsafe fn lock<T>(&self, f: &fn() -> T) -> T {
             }
         }
     }
+
+    pub unsafe fn signal(&self) {
+        rust_signal_little_lock(self.l);
+    }
+
+    pub unsafe fn lock_and_wait(&self, f: &fn() -> bool) {
+        do atomically {
+            rust_lock_little_lock(self.l);
+            do (|| {
+                if f() {
+                    rust_wait_little_lock(self.l);
+                }
+            }).finally {
+                rust_unlock_little_lock(self.l);
+            }
+        }
+    }
 }
 
 struct ExData<T> {
@@ -402,6 +419,34 @@ pub unsafe fn with_imm<U>(&self, f: &fn(x: &T) -> U) -> U {
         }
     }
 
+    #[inline]
+    pub unsafe fn hold_and_signal(&self, f: &fn(x: &mut T)) {
+        let rec = self.x.get();
+        do (*rec).lock.lock {
+            if (*rec).failed {
+                fail!("Poisoned Exclusive::new - another task failed inside!");
+            }
+            (*rec).failed = true;
+            f(&mut (*rec).data);
+            (*rec).failed = false;
+            (*rec).lock.signal();
+        }
+    }
+
+    #[inline]
+    pub unsafe fn hold_and_wait(&self, f: &fn(x: &T) -> bool) {
+        let rec = self.x.get();
+        do (*rec).lock.lock_and_wait {
+            if (*rec).failed {
+                fail!("Poisoned Exclusive::new - another task failed inside!");
+            }
+            (*rec).failed = true;
+            let result = f(&(*rec).data);
+            (*rec).failed = false;
+            result
+        }
+    }
+
     pub fn unwrap(self) -> T {
         let Exclusive { x: x } = self;
         // Someday we might need to unkillably unwrap an Exclusive, but not today.
@@ -415,6 +460,8 @@ pub fn unwrap(self) -> T {
 externfn!(fn rust_destroy_little_lock(lock: rust_little_lock))
 externfn!(fn rust_lock_little_lock(lock: rust_little_lock))
 externfn!(fn rust_unlock_little_lock(lock: rust_little_lock))
+externfn!(fn rust_signal_little_lock(lock: rust_little_lock))
+externfn!(fn rust_wait_little_lock(lock: rust_little_lock))
 
 #[cfg(test)]
 mod tests {
index 77020537661600115a4e2565895f243257626c53..a8eec52943ee004d8918cf746cd2ebf6296365df 100644 (file)
@@ -377,6 +377,16 @@ rust_unlock_little_lock(lock_and_signal *lock) {
     lock->unlock();
 }
 
+extern "C" void
+rust_wait_little_lock(lock_and_signal *lock) {
+    lock->wait();
+}
+
+extern "C" void
+rust_signal_little_lock(lock_and_signal *lock) {
+    lock->signal();
+}
+
 typedef void(startfn)(void*, void*);
 
 class raw_thread: public rust_thread {
index 0cbbb58d02c66d5ba4e1fbd712c59c06d7d79553..c59dacab88990863a046da9caa94b0bdf1d8aef9 100644 (file)
@@ -659,7 +659,7 @@ rust_uv_tty_get_winsize(uv_tty_t *tty, int *width, int *height) {
     return uv_tty_get_winsize(tty, width, height);
 }
 
-extern "C" uv_handle_type
+extern "C" int
 rust_uv_guess_handle(int fd) {
     return uv_guess_handle(fd);
 }
index 269da8e7882ac9fd61647b595f7828a36b8562fb..06f4c0006f17a6828009d401eebfd7e5b7a96acf 100644 (file)
@@ -128,6 +128,8 @@ rust_create_little_lock
 rust_destroy_little_lock
 rust_lock_little_lock
 rust_unlock_little_lock
+rust_signal_little_lock
+rust_wait_little_lock
 tdefl_compress_mem_to_heap
 tinfl_decompress_mem_to_heap
 rust_uv_ip4_port
diff --git a/src/test/run-fail/addr-of-bot.rs b/src/test/run-fail/addr-of-bot.rs
new file mode 100644 (file)
index 0000000..45dbe11
--- /dev/null
@@ -0,0 +1,15 @@
+// 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.
+
+// error-pattern:explicit failure
+
+fn main() {
+    &fail!()
+}
diff --git a/src/test/run-pass/field-destruction-order.rs b/src/test/run-pass/field-destruction-order.rs
new file mode 100644 (file)
index 0000000..1d4c08f
--- /dev/null
@@ -0,0 +1,52 @@
+// 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.
+
+// In theory, it doesn't matter what order destructors are run in for rust
+// because we have explicit ownership of values meaning that there's no need to
+// run one before another. With unsafe code, however, there may be a safe
+// interface which relies on fields having their destructors run in a particular
+// order. At the time of this writing, std::rt::sched::Scheduler is an example
+// of a structure which contains unsafe handles to FFI-like types, and the
+// destruction order of the fields matters in the sense that some handles need
+// to get destroyed before others.
+//
+// In C++, destruction order happens bottom-to-top in order of field
+// declarations, but we currently run them top-to-bottom. I don't think the
+// order really matters that much as long as we define what it is.
+
+struct A;
+struct B;
+struct C {
+    a: A,
+    b: B,
+}
+
+static mut hit: bool = false;
+
+impl Drop for A {
+    fn drop(&mut self) {
+        unsafe {
+            assert!(!hit);
+            hit = true;
+        }
+    }
+}
+
+impl Drop for B {
+    fn drop(&mut self) {
+        unsafe {
+            assert!(hit);
+        }
+    }
+}
+
+pub fn main() {
+    let _c = C { a: A, b: B };
+}