From e47ee779cd412ad629ab0f3db1babcc502862924 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 19 Apr 2013 15:18:38 -0700 Subject: [PATCH 01/40] wip --- src/libcore/rt/io/{util.rs => extensions.rs} | 0 src/libcore/rt/io/file.rs | 6 +----- src/libcore/rt/io/mod.rs | 11 +++++------ src/libcore/rt/io/net/unix.rs | 2 +- src/libcore/rt/io/{misc.rs => support.rs} | 0 5 files changed, 7 insertions(+), 12 deletions(-) rename src/libcore/rt/io/{util.rs => extensions.rs} (100%) rename src/libcore/rt/io/{misc.rs => support.rs} (100%) diff --git a/src/libcore/rt/io/util.rs b/src/libcore/rt/io/extensions.rs similarity index 100% rename from src/libcore/rt/io/util.rs rename to src/libcore/rt/io/extensions.rs diff --git a/src/libcore/rt/io/file.rs b/src/libcore/rt/io/file.rs index e041183b584..4e3e01a5ece 100644 --- a/src/libcore/rt/io/file.rs +++ b/src/libcore/rt/io/file.rs @@ -9,14 +9,10 @@ // except according to those terms. use prelude::*; -use super::misc::PathLike; +use super::support::PathLike; use super::{Reader, Writer, Seek, Close}; use super::{IoError, SeekStyle}; -/// Open a file with the default FileMode and FileAccess -/// # XXX are there sane defaults here? -pub fn open_file(_path: &P) -> FileStream { fail!() } - /// # XXX /// * Ugh, this is ridiculous. What is the best way to represent these options? enum FileMode { diff --git a/src/libcore/rt/io/mod.rs b/src/libcore/rt/io/mod.rs index b035532144c..7ab27cdd7ec 100644 --- a/src/libcore/rt/io/mod.rs +++ b/src/libcore/rt/io/mod.rs @@ -104,7 +104,6 @@ pub use self::stdio::stderr; pub use self::stdio::print; pub use self::stdio::println; -pub use self::file::open_file; pub use self::file::FileStream; pub use self::net::Listener; pub use self::net::ip::IpAddr; @@ -113,9 +112,9 @@ pub use self::net::tcp::TcpStream; pub use self::net::udp::UdpStream; // Some extension traits that all Readers and Writers get. -pub use self::util::ReaderUtil; -pub use self::util::ReaderByteConversions; -pub use self::util::WriterByteConversions; +pub use self::extensions::ReaderUtil; +pub use self::extensions::ReaderByteConversions; +pub use self::extensions::WriterByteConversions; /// Synchronous, non-blocking file I/O. pub mod file; @@ -140,10 +139,10 @@ pub mod flate; pub mod comm_adapters; /// Extension traits -mod util; +mod extensions; /// Non-I/O things needed by the I/O module -mod misc; +mod support; /// Thread-blocking implementations pub mod native { diff --git a/src/libcore/rt/io/net/unix.rs b/src/libcore/rt/io/net/unix.rs index 35eabe21b2a..de886949e7b 100644 --- a/src/libcore/rt/io/net/unix.rs +++ b/src/libcore/rt/io/net/unix.rs @@ -11,7 +11,7 @@ use prelude::*; use super::*; use super::super::*; -use super::super::misc::PathLike; +use super::super::support::PathLike; pub struct UnixStream; diff --git a/src/libcore/rt/io/misc.rs b/src/libcore/rt/io/support.rs similarity index 100% rename from src/libcore/rt/io/misc.rs rename to src/libcore/rt/io/support.rs From 6a5c4f68c2f8c10bf439a39373f5c518ed2f58a1 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 19 Apr 2013 18:47:31 -0700 Subject: [PATCH 02/40] core::rt: Just some poking at the I/O docs --- src/libcore/rt/io/mod.rs | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/libcore/rt/io/mod.rs b/src/libcore/rt/io/mod.rs index 7ab27cdd7ec..c04389e0290 100644 --- a/src/libcore/rt/io/mod.rs +++ b/src/libcore/rt/io/mod.rs @@ -10,8 +10,12 @@ /*! Synchronous I/O -This module defines the Rust interface for synchronous I/O. -It supports file access, +This module defines the Rust interface for synchronous I/O. It is +build around Reader and Writer traits that define byte stream sources +and sinks. Implementations are provided for common I/O streams like +file, TCP, UDP, Unix domain sockets, multiple types of memory bufers. +Readers and Writers may be composed to add things like string parsing, +and compression. This will likely live in core::io, not core::rt::io. @@ -27,7 +31,7 @@ Some examples of obvious things you might want to do * Read a complete file to a string, (converting newlines?) - let contents = open("message.txt").read_to_str(); // read_to_str?? + let contents = FileStream::open("message.txt").read_to_str(); // read_to_str?? * Write a line to a file @@ -36,13 +40,26 @@ Some examples of obvious things you might want to do * Iterate over the lines of a file + do FileStream::open("message.txt").each_line |line| { + println(line) + } + * Pull the lines of a file into a vector of strings + let lines = FileStream::open("message.txt").line_iter().to_vec(); + +* Make an simple HTTP request + + let socket = TcpStream::open("localhost:8080"); + socket.write_line("GET / HTTP/1.0"); + socket.write_line(""); + let response = socket.read_to_end(); + * Connect based on URL? Requires thinking about where the URL type lives and how to make protocol handlers extensible, e.g. the "tcp" protocol yields a `TcpStream`. - connect("tcp://localhost:8080").write_line("HTTP 1.0 GET /"); + connect("tcp://localhost:8080"); # Terms From 1f97e6d47f90f1ddfef2d3a888099bd95b0bb1df Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 20 Apr 2013 00:24:44 -0700 Subject: [PATCH 03/40] rt: Add rust_dbg_next_port for generating test port numbers --- src/rt/rust_test_helpers.cpp | 11 +++++++++++ src/rt/rustrt.def.in | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/rt/rust_test_helpers.cpp b/src/rt/rust_test_helpers.cpp index 64966bd3454..7938d65acd2 100644 --- a/src/rt/rust_test_helpers.cpp +++ b/src/rt/rust_test_helpers.cpp @@ -165,3 +165,14 @@ extern "C" CDECL TwoDoubles rust_dbg_extern_identity_TwoDoubles(TwoDoubles u) { return u; } + +// Generates increasing port numbers for network testing +extern "C" CDECL uintptr_t +rust_dbg_next_port() { + static lock_and_signal dbg_port_lock; + static uintptr_t next_port = 9000; + scoped_lock with(dbg_port_lock); + uintptr_t this_port = next_port; + next_port += 1; + return this_port; +} diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index 5a556ed2107..f84a73a394b 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -221,4 +221,4 @@ rust_uv_free_ip4_addr rust_uv_free_ip6_addr rust_call_nullary_fn rust_initialize_global_state - +rust_dbg_next_port From 744ba627f34ebf76b5a784085645780066d8d92a Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 20 Apr 2013 00:33:49 -0700 Subject: [PATCH 04/40] core::rt: Add a test mod and put run_in_newsched_task there --- src/libcore/rt/mod.rs | 4 ++++ src/libcore/rt/test.rs | 29 +++++++++++++++++++++++++++++ src/libcore/task/mod.rs | 2 +- 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 src/libcore/rt/test.rs diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs index e93e0c6fc6c..70bc2c7e063 100644 --- a/src/libcore/rt/mod.rs +++ b/src/libcore/rt/mod.rs @@ -49,6 +49,10 @@ mod context; mod thread; pub mod env; +/// Tools for testing the runtime +#[cfg(test)] +pub mod test; + #[cfg(stage0)] pub fn start(main: *u8, _argc: int, _argv: *c_char, _crate_map: *u8) -> int { use self::sched::{Scheduler, Task}; diff --git a/src/libcore/rt/test.rs b/src/libcore/rt/test.rs new file mode 100644 index 00000000000..14d69c23732 --- /dev/null +++ b/src/libcore/rt/test.rs @@ -0,0 +1,29 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// For setting up tests of the new scheduler +pub fn run_in_newsched_task(f: ~fn()) { + use cell::Cell; + use unstable::run_in_bare_thread; + use super::sched::Task; + use super::uvio::UvEventLoop; + + let f = Cell(Cell(f)); + + do run_in_bare_thread { + let mut sched = ~UvEventLoop::new_scheduler(); + let f = f.take(); + let task = ~do Task::new(&mut sched.stack_pool) { + (f.take())(); + }; + sched.task_queue.push_back(task); + sched.run(); + } +} diff --git a/src/libcore/task/mod.rs b/src/libcore/task/mod.rs index a6c03638713..a243bfba85c 100644 --- a/src/libcore/task/mod.rs +++ b/src/libcore/task/mod.rs @@ -1229,7 +1229,7 @@ fn test_spawn_thread_on_demand() { #[test] fn test_simple_newsched_spawn() { - use rt::run_in_newsched_task; + use rt::test::run_in_newsched_task; do run_in_newsched_task { spawn(||()) From d24a3a4b016331438ec3c611e37c11cfe41fa4af Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 20 Apr 2013 01:16:06 -0700 Subject: [PATCH 05/40] core::rt: Use generated port numbers in tests --- src/libcore/rt/test.rs | 10 ++++++++ src/libcore/rt/uv/net.rs | 48 +++++------------------------------- src/libcore/rt/uvio.rs | 13 +++++----- src/rt/rust_test_helpers.cpp | 2 +- 4 files changed, 24 insertions(+), 49 deletions(-) diff --git a/src/libcore/rt/test.rs b/src/libcore/rt/test.rs index 14d69c23732..c2ad7d37d42 100644 --- a/src/libcore/rt/test.rs +++ b/src/libcore/rt/test.rs @@ -27,3 +27,13 @@ pub fn run_in_newsched_task(f: ~fn()) { sched.run(); } } + +/// Get a port number, starting at 9600, for use in tests +pub fn next_test_port() -> u16 { + unsafe { + return rust_dbg_next_port() as u16; + } + extern { + fn rust_dbg_next_port() -> ::libc::uintptr_t; + } +} diff --git a/src/libcore/rt/uv/net.rs b/src/libcore/rt/uv/net.rs index 0dc1a4d86cb..54823d4dbcf 100644 --- a/src/libcore/rt/uv/net.rs +++ b/src/libcore/rt/uv/net.rs @@ -19,12 +19,10 @@ use super::{Loop, Watcher, Request, UvError, Buf, Callback, NativeHandle, NullCa vec_to_uv_buf, vec_from_uv_buf}; use super::super::io::net::ip::{IpAddr, Ipv4, Ipv6}; -#[cfg(test)] -use unstable::run_in_bare_thread; -#[cfg(test)] -use super::super::thread::Thread; -#[cfg(test)] -use cell::Cell; +#[cfg(test)] use cell::Cell; +#[cfg(test)] use unstable::run_in_bare_thread; +#[cfg(test)] use super::super::thread::Thread; +#[cfg(test)] use super::super::test::next_test_port; fn ip4_as_uv_ip4(addr: IpAddr, f: &fn(*sockaddr_in)) { match addr { @@ -361,7 +359,7 @@ fn connect_close() { let mut loop_ = Loop::new(); let mut tcp_watcher = { TcpWatcher::new(&mut loop_) }; // Connect to a port where nobody is listening - let addr = Ipv4(127, 0, 0, 1, 2923); + let addr = Ipv4(127, 0, 0, 1, next_test_port()); do tcp_watcher.connect(addr) |stream_watcher, status| { rtdebug!("tcp_watcher.connect!"); assert!(status.is_some()); @@ -373,47 +371,13 @@ fn connect_close() { } } -#[test] -#[ignore(reason = "need a server to connect to")] -fn connect_read() { - do run_in_bare_thread() { - let mut loop_ = Loop::new(); - let mut tcp_watcher = { TcpWatcher::new(&mut loop_) }; - let addr = Ipv4(127, 0, 0, 1, 2924); - do tcp_watcher.connect(addr) |stream_watcher, status| { - let mut stream_watcher = stream_watcher; - rtdebug!("tcp_watcher.connect!"); - assert!(status.is_none()); - let alloc: AllocCallback = |size| { - vec_to_uv_buf(vec::from_elem(size, 0)) - }; - do stream_watcher.read_start(alloc) - |stream_watcher, nread, buf, status| { - - let buf = vec_from_uv_buf(buf); - rtdebug!("read cb!"); - if status.is_none() { - let bytes = buf.unwrap(); - rtdebug!("%s", bytes.slice(0, nread as uint).to_str()); - } else { - rtdebug!("status after read: %s", status.get().to_str()); - rtdebug!("closing"); - stream_watcher.close(||()); - } - } - } - loop_.run(); - loop_.close(); - } -} - #[test] fn listen() { do run_in_bare_thread() { static MAX: int = 10; let mut loop_ = Loop::new(); let mut server_tcp_watcher = { TcpWatcher::new(&mut loop_) }; - let addr = Ipv4(127, 0, 0, 1, 2925); + let addr = Ipv4(127, 0, 0, 1, next_test_port()); server_tcp_watcher.bind(addr); let loop_ = loop_; rtdebug!("listening"); diff --git a/src/libcore/rt/uvio.rs b/src/libcore/rt/uvio.rs index ff539739835..a9aa0333b16 100644 --- a/src/libcore/rt/uvio.rs +++ b/src/libcore/rt/uvio.rs @@ -19,9 +19,10 @@ use cell::{Cell, empty_cell}; use cast::transmute; use super::sched::{Scheduler, local_sched}; -#[cfg(test)] use super::sched::Task; -#[cfg(test)] use unstable::run_in_bare_thread; #[cfg(test)] use uint; +#[cfg(test)] use unstable::run_in_bare_thread; +#[cfg(test)] use super::sched::Task; +#[cfg(test)] use super::test::next_test_port; pub struct UvEventLoop { uvio: UvIoFactory @@ -340,7 +341,7 @@ fn test_simple_io_no_connect() { let mut sched = ~UvEventLoop::new_scheduler(); let task = ~do Task::new(&mut sched.stack_pool) { let io = unsafe { local_sched::unsafe_borrow_io() }; - let addr = Ipv4(127, 0, 0, 1, 2926); + let addr = Ipv4(127, 0, 0, 1, next_test_port()); let maybe_chan = io.connect(addr); assert!(maybe_chan.is_none()); }; @@ -354,7 +355,7 @@ fn test_simple_io_no_connect() { fn test_simple_tcp_server_and_client() { do run_in_bare_thread { let mut sched = ~UvEventLoop::new_scheduler(); - let addr = Ipv4(127, 0, 0, 1, 2929); + let addr = Ipv4(127, 0, 0, 1, next_test_port()); let client_task = ~do Task::new(&mut sched.stack_pool) { unsafe { @@ -393,7 +394,7 @@ fn test_simple_tcp_server_and_client() { fn test_read_and_block() { do run_in_bare_thread { let mut sched = ~UvEventLoop::new_scheduler(); - let addr = Ipv4(127, 0, 0, 1, 2930); + let addr = Ipv4(127, 0, 0, 1, next_test_port()); let client_task = ~do Task::new(&mut sched.stack_pool) { let io = unsafe { local_sched::unsafe_borrow_io() }; @@ -454,7 +455,7 @@ fn test_read_and_block() { fn test_read_read_read() { do run_in_bare_thread { let mut sched = ~UvEventLoop::new_scheduler(); - let addr = Ipv4(127, 0, 0, 1, 2931); + let addr = Ipv4(127, 0, 0, 1, next_test_port()); let client_task = ~do Task::new(&mut sched.stack_pool) { let io = unsafe { local_sched::unsafe_borrow_io() }; diff --git a/src/rt/rust_test_helpers.cpp b/src/rt/rust_test_helpers.cpp index 7938d65acd2..d82c39d6838 100644 --- a/src/rt/rust_test_helpers.cpp +++ b/src/rt/rust_test_helpers.cpp @@ -170,7 +170,7 @@ rust_dbg_extern_identity_TwoDoubles(TwoDoubles u) { extern "C" CDECL uintptr_t rust_dbg_next_port() { static lock_and_signal dbg_port_lock; - static uintptr_t next_port = 9000; + static uintptr_t next_port = 9600; scoped_lock with(dbg_port_lock); uintptr_t this_port = next_port; next_port += 1; From eac629bf5c4b9efd0de3ff6a14d63584eb4a967d Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 20 Apr 2013 01:32:06 -0700 Subject: [PATCH 06/40] core::rt: Unignore some networking tests These should work now, I hope --- src/libcore/rt/uvio.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libcore/rt/uvio.rs b/src/libcore/rt/uvio.rs index a9aa0333b16..c615f068a69 100644 --- a/src/libcore/rt/uvio.rs +++ b/src/libcore/rt/uvio.rs @@ -335,7 +335,6 @@ impl Stream for UvStream { } #[test] -#[ignore(reason = "ffi struct issues")] fn test_simple_io_no_connect() { do run_in_bare_thread { let mut sched = ~UvEventLoop::new_scheduler(); @@ -351,7 +350,6 @@ fn test_simple_io_no_connect() { } #[test] -#[ignore(reason = "ffi struct issues")] fn test_simple_tcp_server_and_client() { do run_in_bare_thread { let mut sched = ~UvEventLoop::new_scheduler(); From 1a7561be4d2046a6a6b78ae5dab2e6fd3ba8db82 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 20 Apr 2013 01:37:59 -0700 Subject: [PATCH 07/40] core::rt: Remove redundant copy of run_in_newsched_task --- src/libcore/rt/mod.rs | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs index 70bc2c7e063..cf896dd18f3 100644 --- a/src/libcore/rt/mod.rs +++ b/src/libcore/rt/mod.rs @@ -164,24 +164,3 @@ fn test_context() { sched.run(); } } - -// For setting up tests of the new scheduler -#[cfg(test)] -pub fn run_in_newsched_task(f: ~fn()) { - use cell::Cell; - use unstable::run_in_bare_thread; - use self::sched::Task; - use self::uvio::UvEventLoop; - - let f = Cell(Cell(f)); - - do run_in_bare_thread { - let mut sched = ~UvEventLoop::new_scheduler(); - let f = f.take(); - let task = ~do Task::new(&mut sched.stack_pool) { - (f.take())(); - }; - sched.task_queue.push_back(task); - sched.run(); - } -} From e564fc7f6b02e917407b3aa6235fc0727afdaf2c Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 20 Apr 2013 01:55:10 -0700 Subject: [PATCH 08/40] core::rt: Don't directly create scheduler types in I/O tests There are some better abstractions for this now --- src/libcore/rt/test.rs | 16 ++++++++ src/libcore/rt/uvio.rs | 87 +++++++++++++++++------------------------- 2 files changed, 50 insertions(+), 53 deletions(-) diff --git a/src/libcore/rt/test.rs b/src/libcore/rt/test.rs index c2ad7d37d42..dfc058d6eba 100644 --- a/src/libcore/rt/test.rs +++ b/src/libcore/rt/test.rs @@ -28,6 +28,22 @@ pub fn run_in_newsched_task(f: ~fn()) { } } +/// Create a new task and run it right now +pub fn spawn_immediately(f: ~fn()) { + use cell::Cell; + use super::*; + use super::sched::*; + + let mut sched = local_sched::take(); + let task = ~Task::new(&mut sched.stack_pool, f); + do sched.switch_running_tasks_and_then(task) |task| { + let task = Cell(task); + do local_sched::borrow |sched| { + sched.task_queue.push_front(task.take()); + } + } +} + /// Get a port number, starting at 9600, for use in tests pub fn next_test_port() -> u16 { unsafe { diff --git a/src/libcore/rt/uvio.rs b/src/libcore/rt/uvio.rs index c615f068a69..d3953b7a797 100644 --- a/src/libcore/rt/uvio.rs +++ b/src/libcore/rt/uvio.rs @@ -22,7 +22,7 @@ use super::sched::{Scheduler, local_sched}; #[cfg(test)] use uint; #[cfg(test)] use unstable::run_in_bare_thread; #[cfg(test)] use super::sched::Task; -#[cfg(test)] use super::test::next_test_port; +#[cfg(test)] use super::test::*; pub struct UvEventLoop { uvio: UvIoFactory @@ -336,35 +336,21 @@ impl Stream for UvStream { #[test] fn test_simple_io_no_connect() { - do run_in_bare_thread { - let mut sched = ~UvEventLoop::new_scheduler(); - let task = ~do Task::new(&mut sched.stack_pool) { - let io = unsafe { local_sched::unsafe_borrow_io() }; - let addr = Ipv4(127, 0, 0, 1, next_test_port()); - let maybe_chan = io.connect(addr); - assert!(maybe_chan.is_none()); - }; - sched.task_queue.push_back(task); - sched.run(); + do run_in_newsched_task { + let io = unsafe { local_sched::unsafe_borrow_io() }; + let addr = Ipv4(127, 0, 0, 1, next_test_port()); + let maybe_chan = io.connect(addr); + assert!(maybe_chan.is_none()); } } #[test] fn test_simple_tcp_server_and_client() { - do run_in_bare_thread { - let mut sched = ~UvEventLoop::new_scheduler(); + do run_in_newsched_task { let addr = Ipv4(127, 0, 0, 1, next_test_port()); - let client_task = ~do Task::new(&mut sched.stack_pool) { - unsafe { - let io = local_sched::unsafe_borrow_io(); - let mut stream = io.connect(addr).unwrap(); - stream.write([0, 1, 2, 3, 4, 5, 6, 7]); - stream.close(); - } - }; - - let server_task = ~do Task::new(&mut sched.stack_pool) { + // Start the server first so it's listening when we connect + do spawn_immediately { unsafe { let io = local_sched::unsafe_borrow_io(); let mut listener = io.bind(addr).unwrap(); @@ -379,32 +365,25 @@ fn test_simple_tcp_server_and_client() { stream.close(); listener.close(); } - }; + } - // Start the server first so it listens before the client connects - sched.task_queue.push_back(server_task); - sched.task_queue.push_back(client_task); - sched.run(); + do spawn_immediately { + unsafe { + let io = local_sched::unsafe_borrow_io(); + let mut stream = io.connect(addr).unwrap(); + stream.write([0, 1, 2, 3, 4, 5, 6, 7]); + stream.close(); + } + } } } #[test] #[ignore(reason = "busted")] fn test_read_and_block() { - do run_in_bare_thread { - let mut sched = ~UvEventLoop::new_scheduler(); + do run_in_newsched_task { let addr = Ipv4(127, 0, 0, 1, next_test_port()); - let client_task = ~do Task::new(&mut sched.stack_pool) { - let io = unsafe { local_sched::unsafe_borrow_io() }; - let mut stream = io.connect(addr).unwrap(); - stream.write([0, 1, 2, 3, 4, 5, 6, 7]); - stream.write([0, 1, 2, 3, 4, 5, 6, 7]); - stream.write([0, 1, 2, 3, 4, 5, 6, 7]); - stream.write([0, 1, 2, 3, 4, 5, 6, 7]); - stream.close(); - }; - - let server_task = ~do Task::new(&mut sched.stack_pool) { + do spawn_immediately { let io = unsafe { local_sched::unsafe_borrow_io() }; let mut listener = io.bind(addr).unwrap(); let mut stream = listener.listen().unwrap(); @@ -440,22 +419,27 @@ fn test_read_and_block() { stream.close(); listener.close(); - }; + } + + do spawn_immediately { + let io = unsafe { local_sched::unsafe_borrow_io() }; + let mut stream = io.connect(addr).unwrap(); + stream.write([0, 1, 2, 3, 4, 5, 6, 7]); + stream.write([0, 1, 2, 3, 4, 5, 6, 7]); + stream.write([0, 1, 2, 3, 4, 5, 6, 7]); + stream.write([0, 1, 2, 3, 4, 5, 6, 7]); + stream.close(); + } - // Start the server first so it listens before the client connects - sched.task_queue.push_back(server_task); - sched.task_queue.push_back(client_task); - sched.run(); } } #[test] #[ignore(reason = "needs server")] fn test_read_read_read() { - do run_in_bare_thread { - let mut sched = ~UvEventLoop::new_scheduler(); + do run_in_newsched_task { let addr = Ipv4(127, 0, 0, 1, next_test_port()); - let client_task = ~do Task::new(&mut sched.stack_pool) { + do spawn_immediately { let io = unsafe { local_sched::unsafe_borrow_io() }; let mut stream = io.connect(addr).unwrap(); let mut buf = [0, .. 2048]; @@ -467,9 +451,6 @@ fn test_read_read_read() { } rtdebug_!("read %u bytes total", total_bytes_read as uint); stream.close(); - }; - - sched.task_queue.push_back(client_task); - sched.run(); + } } } From a11c032f36658667bb08382cc409455b0a1d0a61 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 20 Apr 2013 02:16:21 -0700 Subject: [PATCH 09/40] core::rt: Fix a broken uvio test --- src/libcore/rt/uvio.rs | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/libcore/rt/uvio.rs b/src/libcore/rt/uvio.rs index d3953b7a797..2582ddf3105 100644 --- a/src/libcore/rt/uvio.rs +++ b/src/libcore/rt/uvio.rs @@ -434,22 +434,39 @@ fn test_read_and_block() { } } -#[test] #[ignore(reason = "needs server")] +#[test] fn test_read_read_read() { do run_in_newsched_task { let addr = Ipv4(127, 0, 0, 1, next_test_port()); + static MAX: uint = 5000000; + + do spawn_immediately { + unsafe { + let io = local_sched::unsafe_borrow_io(); + let mut listener = io.bind(addr).unwrap(); + let mut stream = listener.listen().unwrap(); + let mut buf = [0, .. 2048]; + let mut total_bytes_written = 0; + while total_bytes_written < MAX { + stream.write(buf); + total_bytes_written += buf.len(); + } + stream.close(); + listener.close(); + } + } do spawn_immediately { let io = unsafe { local_sched::unsafe_borrow_io() }; let mut stream = io.connect(addr).unwrap(); let mut buf = [0, .. 2048]; let mut total_bytes_read = 0; - while total_bytes_read < 500000000 { + while total_bytes_read < MAX { let nread = stream.read(buf).unwrap(); rtdebug!("read %u bytes", nread as uint); total_bytes_read += nread; } - rtdebug_!("read %u bytes total", total_bytes_read as uint); + rtdebug!("read %u bytes total", total_bytes_read as uint); stream.close(); } } From 6e17202ff4a694a2cf96fd7d2ccb1d62265ce7c2 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 20 Apr 2013 02:41:30 -0700 Subject: [PATCH 10/40] core::rt: Add `next_test_ip4` for generating test addresses --- src/libcore/rt/test.rs | 7 +++++++ src/libcore/rt/uv/net.rs | 6 +++--- src/libcore/rt/uvio.rs | 8 ++++---- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/libcore/rt/test.rs b/src/libcore/rt/test.rs index dfc058d6eba..585d43a3ade 100644 --- a/src/libcore/rt/test.rs +++ b/src/libcore/rt/test.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use super::io::net::ip::{IpAddr, Ipv4}; + // For setting up tests of the new scheduler pub fn run_in_newsched_task(f: ~fn()) { use cell::Cell; @@ -53,3 +55,8 @@ pub fn next_test_port() -> u16 { fn rust_dbg_next_port() -> ::libc::uintptr_t; } } + +/// Get a unique localhost:port pair starting at 9600 +pub fn next_test_ip4() -> IpAddr { + Ipv4(127, 0, 0, 1, next_test_port()) +} diff --git a/src/libcore/rt/uv/net.rs b/src/libcore/rt/uv/net.rs index 54823d4dbcf..860c988b9c9 100644 --- a/src/libcore/rt/uv/net.rs +++ b/src/libcore/rt/uv/net.rs @@ -22,7 +22,7 @@ use super::super::io::net::ip::{IpAddr, Ipv4, Ipv6}; #[cfg(test)] use cell::Cell; #[cfg(test)] use unstable::run_in_bare_thread; #[cfg(test)] use super::super::thread::Thread; -#[cfg(test)] use super::super::test::next_test_port; +#[cfg(test)] use super::super::test::*; fn ip4_as_uv_ip4(addr: IpAddr, f: &fn(*sockaddr_in)) { match addr { @@ -359,7 +359,7 @@ fn connect_close() { let mut loop_ = Loop::new(); let mut tcp_watcher = { TcpWatcher::new(&mut loop_) }; // Connect to a port where nobody is listening - let addr = Ipv4(127, 0, 0, 1, next_test_port()); + let addr = next_test_ip4(); do tcp_watcher.connect(addr) |stream_watcher, status| { rtdebug!("tcp_watcher.connect!"); assert!(status.is_some()); @@ -377,7 +377,7 @@ fn listen() { static MAX: int = 10; let mut loop_ = Loop::new(); let mut server_tcp_watcher = { TcpWatcher::new(&mut loop_) }; - let addr = Ipv4(127, 0, 0, 1, next_test_port()); + let addr = next_test_ip4(); server_tcp_watcher.bind(addr); let loop_ = loop_; rtdebug!("listening"); diff --git a/src/libcore/rt/uvio.rs b/src/libcore/rt/uvio.rs index 2582ddf3105..92439a41b34 100644 --- a/src/libcore/rt/uvio.rs +++ b/src/libcore/rt/uvio.rs @@ -338,7 +338,7 @@ impl Stream for UvStream { fn test_simple_io_no_connect() { do run_in_newsched_task { let io = unsafe { local_sched::unsafe_borrow_io() }; - let addr = Ipv4(127, 0, 0, 1, next_test_port()); + let addr = next_test_ip4(); let maybe_chan = io.connect(addr); assert!(maybe_chan.is_none()); } @@ -347,7 +347,7 @@ fn test_simple_io_no_connect() { #[test] fn test_simple_tcp_server_and_client() { do run_in_newsched_task { - let addr = Ipv4(127, 0, 0, 1, next_test_port()); + let addr = next_test_ip4(); // Start the server first so it's listening when we connect do spawn_immediately { @@ -381,7 +381,7 @@ fn test_simple_tcp_server_and_client() { #[test] #[ignore(reason = "busted")] fn test_read_and_block() { do run_in_newsched_task { - let addr = Ipv4(127, 0, 0, 1, next_test_port()); + let addr = next_test_ip4(); do spawn_immediately { let io = unsafe { local_sched::unsafe_borrow_io() }; @@ -437,7 +437,7 @@ fn test_read_and_block() { #[test] fn test_read_read_read() { do run_in_newsched_task { - let addr = Ipv4(127, 0, 0, 1, next_test_port()); + let addr = next_test_ip4(); static MAX: uint = 5000000; do spawn_immediately { From bcb3cfb8ce8353ef91b8b7acc0d6339c809d9a29 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 20 Apr 2013 15:55:07 -0700 Subject: [PATCH 11/40] core::rt: Improve docs for run_in_newsched_task testing function --- src/libcore/rt/test.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libcore/rt/test.rs b/src/libcore/rt/test.rs index 585d43a3ade..e394a873fea 100644 --- a/src/libcore/rt/test.rs +++ b/src/libcore/rt/test.rs @@ -10,7 +10,8 @@ use super::io::net::ip::{IpAddr, Ipv4}; -// For setting up tests of the new scheduler +/// Creates a new scheduler in a new thread and runs a task in it, +/// then waits for the scheduler to exit. pub fn run_in_newsched_task(f: ~fn()) { use cell::Cell; use unstable::run_in_bare_thread; From d818ea81549a2ce12ba7b722997567f45417f099 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 20 Apr 2013 15:55:37 -0700 Subject: [PATCH 12/40] core::rt: Listener constructors are called and return a --- src/libcore/rt/io/net/tcp.rs | 29 ++++++++++++++++++++++++++++- src/libcore/rt/io/net/udp.rs | 2 +- src/libcore/rt/io/net/unix.rs | 2 +- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/libcore/rt/io/net/tcp.rs b/src/libcore/rt/io/net/tcp.rs index e3f71dca8c8..d78241b8e44 100644 --- a/src/libcore/rt/io/net/tcp.rs +++ b/src/libcore/rt/io/net/tcp.rs @@ -40,7 +40,7 @@ impl Close for TcpStream { pub struct TcpListener; impl TcpListener { - pub fn new(_addr: IpAddr) -> TcpListener { + pub fn bind(_addr: IpAddr) -> Result { fail!() } } @@ -48,3 +48,30 @@ impl TcpListener { impl Listener for TcpListener { fn accept(&mut self) -> Option { fail!() } } + +#[cfg(test)] +mod test { + use super::*; + use rt::test::*; + + #[test] #[ignore] + fn smoke_test() { + /*do run_in_newsched_task { + let addr = next_test_ip4(); + + do spawn_immediately { + let listener = TcpListener::bind(addr); + do listener.accept() { + let mut buf = [0]; + listener.read(buf); + assert!(buf[0] == 99); + } + } + + do spawn_immediately { + let stream = TcpStream::connect(addr); + stream.write([99]); + } + }*/ + } +} diff --git a/src/libcore/rt/io/net/udp.rs b/src/libcore/rt/io/net/udp.rs index f76bb58a45e..81a6677c14a 100644 --- a/src/libcore/rt/io/net/udp.rs +++ b/src/libcore/rt/io/net/udp.rs @@ -40,7 +40,7 @@ impl Close for UdpStream { pub struct UdpListener; impl UdpListener { - pub fn new(_addr: IpAddr) -> UdpListener { + pub fn bind(_addr: IpAddr) -> Result { fail!() } } diff --git a/src/libcore/rt/io/net/unix.rs b/src/libcore/rt/io/net/unix.rs index de886949e7b..a5f4f8e3ba8 100644 --- a/src/libcore/rt/io/net/unix.rs +++ b/src/libcore/rt/io/net/unix.rs @@ -40,7 +40,7 @@ impl Close for UnixStream { pub struct UnixListener; impl UnixListener { - pub fn new(_path: &P) -> UnixListener { + pub fn bind(_path: &P) -> Result { fail!() } } From 00474c13f32db8903e0c9ea4ab6186007a79e77c Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 20 Apr 2013 16:15:06 -0700 Subject: [PATCH 13/40] core: Ignore two long-running tests --- src/libcore/run.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libcore/run.rs b/src/libcore/run.rs index 8b18cc3c696..96f88e63680 100644 --- a/src/libcore/run.rs +++ b/src/libcore/run.rs @@ -598,12 +598,14 @@ mod tests { #[test] #[cfg(unix)] + #[ignore(reason = "long run time")] fn test_unforced_destroy_actually_kills() { test_destroy_actually_kills(false); } #[test] #[cfg(unix)] + #[ignore(reason = "long run time")] fn test_forced_destroy_actually_kills() { test_destroy_actually_kills(true); } From fa478f577565174327888ea768dfa3a1018416f2 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 20 Apr 2013 16:15:32 -0700 Subject: [PATCH 14/40] core: Speed up a test case --- src/libcore/rt/uvio.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/rt/uvio.rs b/src/libcore/rt/uvio.rs index 92439a41b34..2e9d0afa52f 100644 --- a/src/libcore/rt/uvio.rs +++ b/src/libcore/rt/uvio.rs @@ -438,7 +438,7 @@ fn test_read_and_block() { fn test_read_read_read() { do run_in_newsched_task { let addr = next_test_ip4(); - static MAX: uint = 5000000; + static MAX: uint = 500000; do spawn_immediately { unsafe { From 4eff3130c589b5fb256c537e90272646fd8406ab Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 20 Apr 2013 17:25:00 -0700 Subject: [PATCH 15/40] core::rt:: Implement Reader/Writer for MemReader/MemWriter --- src/libcore/rt/io/mem.rs | 69 ++++++++++++++++++++++++++++++++++++---- src/libcore/rt/io/mod.rs | 8 +++++ 2 files changed, 70 insertions(+), 7 deletions(-) diff --git a/src/libcore/rt/io/mem.rs b/src/libcore/rt/io/mem.rs index 600968a3c71..06e1466831d 100644 --- a/src/libcore/rt/io/mem.rs +++ b/src/libcore/rt/io/mem.rs @@ -17,7 +17,7 @@ use prelude::*; use super::*; - +use cmp::min; /// Writes to an owned, growable byte vector pub struct MemWriter { @@ -29,13 +29,15 @@ impl MemWriter { } impl Writer for MemWriter { - fn write(&mut self, _buf: &[u8]) { fail!() } + fn write(&mut self, buf: &[u8]) { + self.buf.push_all(buf) + } fn flush(&mut self) { /* no-op */ } } impl Seek for MemWriter { - fn tell(&self) -> u64 { fail!() } + fn tell(&self) -> u64 { self.buf.len() as u64 } fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() } } @@ -77,13 +79,27 @@ impl MemReader { } impl Reader for MemReader { - fn read(&mut self, _buf: &mut [u8]) -> Option { fail!() } + fn read(&mut self, buf: &mut [u8]) -> Option { + { if self.eof() { return None; } } - fn eof(&mut self) -> bool { fail!() } + let write_len = min(buf.len(), self.buf.len() - self.pos); + { + let input = self.buf.slice(self.pos, self.pos + write_len); + let output = vec::mut_slice(buf, 0, write_len); + assert!(input.len() == output.len()); + vec::bytes::copy_memory(output, input, write_len); + } + self.pos += write_len; + assert!(self.pos <= self.buf.len()); + + return Some(write_len); + } + + fn eof(&mut self) -> bool { self.pos == self.buf.len() } } impl Seek for MemReader { - fn tell(&self) -> u64 { fail!() } + fn tell(&self) -> u64 { self.pos as u64 } fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() } } @@ -163,4 +179,43 @@ impl<'self> Seek for BufReader<'self> { fn tell(&self) -> u64 { fail!() } fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() } -} \ No newline at end of file +} + +#[cfg(test)] +mod test { + use prelude::*; + use super::*; + + #[test] + fn test_mem_writer() { + let mut writer = MemWriter::new(); + assert!(writer.tell() == 0); + writer.write([0]); + assert!(writer.tell() == 1); + writer.write([1, 2, 3]); + writer.write([4, 5, 6, 7]); + assert!(writer.tell() == 8); + assert!(writer.inner() == ~[0, 1, 2, 3, 4, 5 , 6, 7]); + } + + #[test] + fn test_mem_reader() { + let mut reader = MemReader::new(~[0, 1, 2, 3, 4, 5, 6, 7]); + let mut buf = []; + assert!(reader.read(buf) == Some(0)); + assert!(reader.tell() == 0); + let mut buf = [0]; + assert!(reader.read(buf) == Some(1)); + assert!(reader.tell() == 1); + assert!(buf == [0]); + let mut buf = [0, ..4]; + assert!(reader.read(buf) == Some(4)); + assert!(reader.tell() == 5); + assert!(buf == [1, 2, 3, 4]); + assert!(reader.read(buf) == Some(3)); + assert!(buf.slice(0, 3) == [5, 6, 7]); + assert!(reader.eof()); + assert!(reader.read(buf) == None); + assert!(reader.eof()); + } +} diff --git a/src/libcore/rt/io/mod.rs b/src/libcore/rt/io/mod.rs index c04389e0290..238bd97a62d 100644 --- a/src/libcore/rt/io/mod.rs +++ b/src/libcore/rt/io/mod.rs @@ -269,6 +269,14 @@ pub enum SeekStyle { /// * Are `u64` and `i64` the right choices? pub trait Seek { fn tell(&self) -> u64; + + /// Seek to an offset in a stream + /// + /// A successful seek clears the EOF indicator. + /// + /// # XXX + /// + /// * What is the behavior when seeking past the end of a stream? fn seek(&mut self, pos: i64, style: SeekStyle); } From f9069baa70ea78117f2087fe6e359fb2ea0ae16a Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sun, 21 Apr 2013 16:28:17 -0700 Subject: [PATCH 16/40] core::rt: Add LocalServices for thread-local language services Things like the GC heap and unwinding are desirable everywhere the language might be used, not just in tasks. All Rust code should have access to LocalServices. --- src/libcore/rt/local_services.rs | 63 ++++++++++++++++++++++++++++++++ src/libcore/rt/mod.rs | 3 +- src/libcore/rt/sched/mod.rs | 6 ++- 3 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 src/libcore/rt/local_services.rs diff --git a/src/libcore/rt/local_services.rs b/src/libcore/rt/local_services.rs new file mode 100644 index 00000000000..8e386f80b77 --- /dev/null +++ b/src/libcore/rt/local_services.rs @@ -0,0 +1,63 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Language-level runtime services that should reasonably expected +//! to be available 'everywhere'. Local heaps, GC, unwinding, +//! local storage, and logging. Even a 'freestanding' Rust would likely want +//! to implement this. + +//! Local services may exist in at least three different contexts: +//! when running as a task, when running in the scheduler's context, +//! or when running outside of a scheduler but with local services +//! (freestanding rust with local services?). + +use prelude::*; +use super::sched::{Task, local_sched}; + +pub struct LocalServices { + heap: LocalHeap, + gc: GarbageCollector, + storage: LocalStorage, + logger: Logger, + unwinder: Unwinder +} + +pub struct LocalHeap; +pub struct GarbageCollector; +pub struct LocalStorage; +pub struct Logger; +pub struct Unwinder; + +impl LocalServices { + pub fn new() -> LocalServices { + LocalServices { + heap: LocalHeap, + gc: GarbageCollector, + storage: LocalStorage, + logger: Logger, + unwinder: Unwinder + } + } +} + +/// Borrow a pointer to the installed local services. +/// Fails (likely aborting the process) if local services are not available. +pub fn borrow_local_services(f: &fn(&mut LocalServices)) { + do local_sched::borrow |sched| { + match sched.current_task { + Some(~ref mut task) => { + f(&mut task.local_services) + } + None => { + fail!(~"no local services for schedulers yet") + } + } + } +} diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs index cf896dd18f3..e0190418b13 100644 --- a/src/libcore/rt/mod.rs +++ b/src/libcore/rt/mod.rs @@ -48,6 +48,7 @@ mod stack; mod context; mod thread; pub mod env; +pub mod local_services; /// Tools for testing the runtime #[cfg(test)] @@ -97,7 +98,7 @@ pub fn start(main: *u8, _argc: int, _argv: **c_char, _crate_map: *u8) -> int { /// Different runtime services are available depending on context. #[deriving(Eq)] pub enum RuntimeContext { - // Only default services, e.g. exchange heap + // Only the exchange heap is available GlobalContext, // The scheduler may be accessed SchedulerContext, diff --git a/src/libcore/rt/sched/mod.rs b/src/libcore/rt/sched/mod.rs index 28946281628..30136e443ee 100644 --- a/src/libcore/rt/sched/mod.rs +++ b/src/libcore/rt/sched/mod.rs @@ -16,6 +16,7 @@ use super::work_queue::WorkQueue; use super::stack::{StackPool, StackSegment}; use super::rtio::{EventLoop, EventLoopObject}; use super::context::Context; +use super::local_services::LocalServices; use cell::Cell; #[cfg(test)] use super::uvio::UvEventLoop; @@ -38,7 +39,7 @@ pub struct Scheduler { /// Always valid when a task is executing, otherwise not priv saved_context: Context, /// The currently executing task - priv current_task: Option<~Task>, + current_task: Option<~Task>, /// An action performed after a context switch on behalf of the /// code running before the context switch priv cleanup_job: Option @@ -326,6 +327,8 @@ pub struct Task { /// These are always valid when the task is not running, unless /// the task is dead priv saved_context: Context, + /// The heap, GC, unwinding, local storage, logging + local_services: LocalServices } pub impl Task { @@ -337,6 +340,7 @@ pub impl Task { return Task { current_stack_segment: stack, saved_context: initial_context, + local_services: LocalServices::new() }; } From 2fe118b26fdb2897eb000ae47f912cc6b2534324 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sun, 21 Apr 2013 17:42:45 -0700 Subject: [PATCH 17/40] rt: Don't make memory_region depend on rust_env I am going to use memory_region and boxed_region as the local heap in the new scheduler, for now at least, and I don't have a rust_env available. --- src/rt/boxed_region.cpp | 2 +- src/rt/boxed_region.h | 6 +++--- src/rt/memory_region.cpp | 15 +++++++++------ src/rt/memory_region.h | 5 +++-- src/rt/rust_sched_loop.cpp | 2 +- src/rt/rust_task.cpp | 2 +- 6 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/rt/boxed_region.cpp b/src/rt/boxed_region.cpp index d159df03dc3..e8ddb53148c 100644 --- a/src/rt/boxed_region.cpp +++ b/src/rt/boxed_region.cpp @@ -82,7 +82,7 @@ void boxed_region::free(rust_opaque_box *box) { if (box->next) box->next->prev = box->prev; if (live_allocs == box) live_allocs = box->next; - if (env->poison_on_free) { + if (poison_on_free) { memset(box_body(box), 0xab, box->td->size); } diff --git a/src/rt/boxed_region.h b/src/rt/boxed_region.h index 4097b6d41b7..178772007e5 100644 --- a/src/rt/boxed_region.h +++ b/src/rt/boxed_region.h @@ -24,7 +24,7 @@ struct rust_env; * a type descr which describes the payload (what follows the header). */ class boxed_region { private: - rust_env *env; + bool poison_on_free; memory_region *backing_region; rust_opaque_box *live_allocs; @@ -41,8 +41,8 @@ private: boxed_region& operator=(const boxed_region& rhs); public: - boxed_region(rust_env *e, memory_region *br) - : env(e) + boxed_region(memory_region *br, bool poison_on_free) + : poison_on_free(poison_on_free) , backing_region(br) , live_allocs(NULL) {} diff --git a/src/rt/memory_region.cpp b/src/rt/memory_region.cpp index 6de9d5a1df4..f3406712cb0 100644 --- a/src/rt/memory_region.cpp +++ b/src/rt/memory_region.cpp @@ -11,7 +11,6 @@ #include "sync/sync.h" #include "memory_region.h" -#include "rust_env.h" #if RUSTRT_TRACK_ALLOCATIONS >= 3 #include @@ -35,15 +34,19 @@ void *memory_region::get_data(alloc_header *ptr) { return (void*)((char *)ptr + HEADER_SIZE); } -memory_region::memory_region(rust_env *env, bool synchronized) : - _env(env), _parent(NULL), _live_allocations(0), - _detailed_leaks(env->detailed_leaks), +memory_region::memory_region(bool synchronized, + bool detailed_leaks, + bool poison_on_free) : + _parent(NULL), _live_allocations(0), + _detailed_leaks(detailed_leaks), + _poison_on_free(poison_on_free), _synchronized(synchronized) { } memory_region::memory_region(memory_region *parent) : - _env(parent->_env), _parent(parent), _live_allocations(0), + _parent(parent), _live_allocations(0), _detailed_leaks(parent->_detailed_leaks), + _poison_on_free(parent->_poison_on_free), _synchronized(parent->_synchronized) { } @@ -241,7 +244,7 @@ memory_region::claim_alloc(void *mem) { void memory_region::maybe_poison(void *mem) { - if (!_env->poison_on_free) + if (!_poison_on_free) return; # if RUSTRT_TRACK_ALLOCATIONS >= 1 diff --git a/src/rt/memory_region.h b/src/rt/memory_region.h index 999a992eefa..4ad57c11809 100644 --- a/src/rt/memory_region.h +++ b/src/rt/memory_region.h @@ -54,11 +54,11 @@ private: inline alloc_header *get_header(void *mem); inline void *get_data(alloc_header *); - rust_env *_env; memory_region *_parent; int _live_allocations; array_list _allocation_list; const bool _detailed_leaks; + const bool _poison_on_free; const bool _synchronized; lock_and_signal _lock; @@ -75,7 +75,8 @@ private: memory_region& operator=(const memory_region& rhs); public: - memory_region(rust_env *env, bool synchronized); + memory_region(bool synchronized, + bool detailed_leaks, bool poison_on_free); memory_region(memory_region *parent); void *malloc(size_t size, const char *tag); void *realloc(void *mem, size_t size); diff --git a/src/rt/rust_sched_loop.cpp b/src/rt/rust_sched_loop.cpp index 90393acdd59..e7ce4b08ee4 100644 --- a/src/rt/rust_sched_loop.cpp +++ b/src/rt/rust_sched_loop.cpp @@ -36,7 +36,7 @@ rust_sched_loop::rust_sched_loop(rust_scheduler *sched, int id, bool killed) : sched(sched), log_lvl(log_debug), min_stack_size(kernel->env->min_stack_size), - local_region(kernel->env, false), + local_region(false, kernel->env->detailed_leaks, kernel->env->poison_on_free), // FIXME #2891: calculate a per-scheduler name. name("main") { diff --git a/src/rt/rust_task.cpp b/src/rt/rust_task.cpp index 63dc1c9833e..4680f32c19a 100644 --- a/src/rt/rust_task.cpp +++ b/src/rt/rust_task.cpp @@ -36,7 +36,7 @@ rust_task::rust_task(rust_sched_loop *sched_loop, rust_task_state state, kernel(sched_loop->kernel), name(name), list_index(-1), - boxed(sched_loop->kernel->env, &local_region), + boxed(&local_region, sched_loop->kernel->env->poison_on_free), local_region(&sched_loop->local_region), unwinding(false), total_stack_sz(0), From d7f5e437a28dd85b8a7523af9212a9a1100ea725 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sun, 21 Apr 2013 19:03:52 -0700 Subject: [PATCH 18/40] core::rt: Add the local heap to newsched tasks Reusing the existing boxed_region implementation from the runtime --- src/libcore/rt/local_heap.rs | 81 ++++++++++++++++++++++++++++++++ src/libcore/rt/local_services.rs | 19 +++++++- src/libcore/rt/mod.rs | 1 + src/libcore/unstable/lang.rs | 26 +++++++++- src/rt/boxed_region.cpp | 12 ++--- src/rt/rust_builtin.cpp | 35 ++++++++++++++ src/rt/rustrt.def.in | 6 +++ 7 files changed, 170 insertions(+), 10 deletions(-) create mode 100644 src/libcore/rt/local_heap.rs diff --git a/src/libcore/rt/local_heap.rs b/src/libcore/rt/local_heap.rs new file mode 100644 index 00000000000..fbd4a77d79b --- /dev/null +++ b/src/libcore/rt/local_heap.rs @@ -0,0 +1,81 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The local, garbage collected heap + +use libc::{c_void, uintptr_t, size_t}; +use ops::Drop; + +type MemoryRegion = c_void; +type BoxedRegion = c_void; + +pub type OpaqueBox = c_void; +pub type TypeDesc = c_void; + +pub struct LocalHeap { + memory_region: *MemoryRegion, + boxed_region: *BoxedRegion +} + +impl LocalHeap { + pub fn new() -> LocalHeap { + unsafe { + // Don't need synchronization for the single-threaded local heap + let synchronized = false as uintptr_t; + // XXX: These usually come from the environment + let detailed_leaks = false as uintptr_t; + let poison_on_free = false as uintptr_t; + let region = rust_new_memory_region(synchronized, detailed_leaks, poison_on_free); + assert!(region.is_not_null()); + let boxed = rust_new_boxed_region(region, poison_on_free); + assert!(boxed.is_not_null()); + LocalHeap { + memory_region: region, + boxed_region: boxed + } + } + } + + pub fn alloc(&mut self, td: *TypeDesc, size: uint) -> *OpaqueBox { + unsafe { + return rust_boxed_region_malloc(self.boxed_region, td, size as size_t); + } + } + + pub fn free(&mut self, box: *OpaqueBox) { + unsafe { + return rust_boxed_region_free(self.boxed_region, box); + } + } +} + +impl Drop for LocalHeap { + fn finalize(&self) { + unsafe { + rust_delete_boxed_region(self.boxed_region); + rust_delete_memory_region(self.memory_region); + } + } +} + +extern { + fn rust_new_memory_region(synchronized: uintptr_t, + detailed_leaks: uintptr_t, + poison_on_free: uintptr_t) -> *MemoryRegion; + fn rust_delete_memory_region(region: *MemoryRegion); + fn rust_new_boxed_region(region: *MemoryRegion, + poison_on_free: uintptr_t) -> *BoxedRegion; + fn rust_delete_boxed_region(region: *BoxedRegion); + fn rust_boxed_region_malloc(region: *BoxedRegion, + td: *TypeDesc, + size: size_t) -> *OpaqueBox; + fn rust_boxed_region_free(region: *BoxedRegion, box: *OpaqueBox); +} + diff --git a/src/libcore/rt/local_services.rs b/src/libcore/rt/local_services.rs index 8e386f80b77..b9d99283e14 100644 --- a/src/libcore/rt/local_services.rs +++ b/src/libcore/rt/local_services.rs @@ -20,6 +20,7 @@ use prelude::*; use super::sched::{Task, local_sched}; +use super::local_heap::LocalHeap; pub struct LocalServices { heap: LocalHeap, @@ -29,7 +30,6 @@ pub struct LocalServices { unwinder: Unwinder } -pub struct LocalHeap; pub struct GarbageCollector; pub struct LocalStorage; pub struct Logger; @@ -38,7 +38,7 @@ pub struct Unwinder; impl LocalServices { pub fn new() -> LocalServices { LocalServices { - heap: LocalHeap, + heap: LocalHeap::new(), gc: GarbageCollector, storage: LocalStorage, logger: Logger, @@ -61,3 +61,18 @@ pub fn borrow_local_services(f: &fn(&mut LocalServices)) { } } } + +#[cfg(test)] +mod test { + use rt::test::*; + + #[test] + fn local_heap() { + do run_in_newsched_task() { + let a = @5; + let b = a; + assert!(*a == 5); + assert!(*b == 5); + } + } +} \ No newline at end of file diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs index e0190418b13..4a767d61f74 100644 --- a/src/libcore/rt/mod.rs +++ b/src/libcore/rt/mod.rs @@ -49,6 +49,7 @@ mod context; mod thread; pub mod env; pub mod local_services; +mod local_heap; /// Tools for testing the runtime #[cfg(test)] diff --git a/src/libcore/unstable/lang.rs b/src/libcore/unstable/lang.rs index be776a39742..0bf1ad36a1f 100644 --- a/src/libcore/unstable/lang.rs +++ b/src/libcore/unstable/lang.rs @@ -17,6 +17,8 @@ use str; use sys; use unstable::exchange_alloc; use cast::transmute; +use rt::{context, OldTaskContext}; +use rt::local_services::borrow_local_services; #[allow(non_camel_case_types)] pub type rust_task = c_void; @@ -81,7 +83,18 @@ pub unsafe fn exchange_free(ptr: *c_char) { #[lang="malloc"] #[inline(always)] pub unsafe fn local_malloc(td: *c_char, size: uintptr_t) -> *c_char { - return rustrt::rust_upcall_malloc(td, size); + match context() { + OldTaskContext => { + return rustrt::rust_upcall_malloc(td, size); + } + _ => { + let mut alloc = ::ptr::null(); + do borrow_local_services |srv| { + alloc = srv.heap.alloc(td as *c_void, size as uint) as *c_char; + } + return alloc; + } + } } // NB: Calls to free CANNOT be allowed to fail, as throwing an exception from @@ -90,7 +103,16 @@ pub unsafe fn local_malloc(td: *c_char, size: uintptr_t) -> *c_char { #[lang="free"] #[inline(always)] pub unsafe fn local_free(ptr: *c_char) { - rustrt::rust_upcall_free(ptr); + match context() { + OldTaskContext => { + rustrt::rust_upcall_free(ptr); + } + _ => { + do borrow_local_services |srv| { + srv.heap.free(ptr as *c_void); + } + } + } } #[lang="borrow_as_imm"] diff --git a/src/rt/boxed_region.cpp b/src/rt/boxed_region.cpp index e8ddb53148c..a49b52bffe1 100644 --- a/src/rt/boxed_region.cpp +++ b/src/rt/boxed_region.cpp @@ -27,11 +27,11 @@ rust_opaque_box *boxed_region::malloc(type_desc *td, size_t body_size) { if (live_allocs) live_allocs->prev = box; live_allocs = box; - LOG(rust_get_current_task(), box, + /*LOG(rust_get_current_task(), box, "@malloc()=%p with td %p, size %lu==%lu+%lu, " "align %lu, prev %p, next %p\n", box, td, total_size, sizeof(rust_opaque_box), body_size, - td->align, box->prev, box->next); + td->align, box->prev, box->next);*/ return box; } @@ -50,9 +50,9 @@ rust_opaque_box *boxed_region::realloc(rust_opaque_box *box, if (new_box->next) new_box->next->prev = new_box; if (live_allocs == box) live_allocs = new_box; - LOG(rust_get_current_task(), box, + /*LOG(rust_get_current_task(), box, "@realloc()=%p with orig=%p, size %lu==%lu+%lu", - new_box, box, total_size, sizeof(rust_opaque_box), new_size); + new_box, box, total_size, sizeof(rust_opaque_box), new_size);*/ return new_box; } @@ -74,9 +74,9 @@ void boxed_region::free(rust_opaque_box *box) { // double frees (kind of). assert(box->td != NULL); - LOG(rust_get_current_task(), box, + /*LOG(rust_get_current_task(), box, "@free(%p) with td %p, prev %p, next %p\n", - box, box->td, box->prev, box->next); + box, box->td, box->prev, box->next);*/ if (box->prev) box->prev->next = box->next; if (box->next) box->next->prev = box->prev; diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index a0db6f64f69..b8749b8f73d 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -851,6 +851,41 @@ rust_initialize_global_state() { } } +extern "C" CDECL memory_region* +rust_new_memory_region(uintptr_t synchronized, + uintptr_t detailed_leaks, + uintptr_t poison_on_free) { + return new memory_region((bool)synchronized, + (bool)detailed_leaks, + (bool)poison_on_free); +} + +extern "C" CDECL void +rust_delete_memory_region(memory_region *region) { + delete region; +} + +extern "C" CDECL boxed_region* +rust_new_boxed_region(memory_region *region, + uintptr_t poison_on_free) { + return new boxed_region(region, poison_on_free); +} + +extern "C" CDECL void +rust_delete_boxed_region(boxed_region *region) { + delete region; +} + +extern "C" CDECL rust_opaque_box* +rust_boxed_region_malloc(boxed_region *region, type_desc *td, size_t size) { + return region->malloc(td, size); +} + +extern "C" CDECL void +rust_boxed_region_free(boxed_region *region, rust_opaque_box *box) { + region->free(box); +} + // // Local Variables: // mode: C++ diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index f84a73a394b..9aa80156783 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -222,3 +222,9 @@ rust_uv_free_ip6_addr rust_call_nullary_fn rust_initialize_global_state rust_dbg_next_port +rust_new_memory_region +rust_delete_memory_region +rust_new_boxed_region +rust_delete_boxed_region +rust_boxed_region_malloc +rust_boxed_region_free From d0786fdffcbae5c89762455fd3b3ffb5b9a3b6a1 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 22 Apr 2013 12:54:03 -0700 Subject: [PATCH 19/40] core::rt Wire up task-local storage to the new scheduler --- src/libcore/rt/local_services.rs | 58 +++++++++++++++++-- src/libcore/rt/sched/mod.rs | 7 +++ src/libcore/task/local_data.rs | 10 ++-- src/libcore/task/local_data_priv.rs | 87 ++++++++++++++++++++++++----- src/libcore/task/spawn.rs | 8 +-- 5 files changed, 144 insertions(+), 26 deletions(-) diff --git a/src/libcore/rt/local_services.rs b/src/libcore/rt/local_services.rs index b9d99283e14..d29e57a17af 100644 --- a/src/libcore/rt/local_services.rs +++ b/src/libcore/rt/local_services.rs @@ -19,6 +19,7 @@ //! (freestanding rust with local services?). use prelude::*; +use libc::c_void; use super::sched::{Task, local_sched}; use super::local_heap::LocalHeap; @@ -27,11 +28,12 @@ pub struct LocalServices { gc: GarbageCollector, storage: LocalStorage, logger: Logger, - unwinder: Unwinder + unwinder: Unwinder, + destroyed: bool } pub struct GarbageCollector; -pub struct LocalStorage; +pub struct LocalStorage(*c_void, Option<~fn(*c_void)>); pub struct Logger; pub struct Unwinder; @@ -40,11 +42,34 @@ impl LocalServices { LocalServices { heap: LocalHeap::new(), gc: GarbageCollector, - storage: LocalStorage, + storage: LocalStorage(ptr::null(), None), logger: Logger, - unwinder: Unwinder + unwinder: Unwinder, + destroyed: false } } + + /// Must be called manually before finalization to clean up + /// thread-local resources. Some of the routines here expect + /// LocalServices to be available recursively so this must be + /// called unsafely, without removing LocalServices from + /// thread-local-storage. + pub fn destroy(&mut self) { + // This is just an assertion that `destroy` was called unsafely + // and this instance of LocalServices is still accessible. + do borrow_local_services |sched| { + assert!(ptr::ref_eq(sched, self)); + } + match self.storage { + LocalStorage(ptr, Some(ref dtor)) => (*dtor)(ptr), + _ => () + } + self.destroyed = true; + } +} + +impl Drop for LocalServices { + fn finalize(&self) { assert!(self.destroyed) } } /// Borrow a pointer to the installed local services. @@ -62,6 +87,19 @@ pub fn borrow_local_services(f: &fn(&mut LocalServices)) { } } +pub unsafe fn unsafe_borrow_local_services() -> &mut LocalServices { + use cast::transmute_mut_region; + + match local_sched::unsafe_borrow().current_task { + Some(~ref mut task) => { + transmute_mut_region(&mut task.local_services) + } + None => { + fail!(~"no local services for schedulers yet") + } + } +} + #[cfg(test)] mod test { use rt::test::*; @@ -75,4 +113,16 @@ mod test { assert!(*b == 5); } } + + #[test] + fn tls() { + use task::local_data::*; + do run_in_newsched_task() { + unsafe { + fn key(_x: @~str) { } + local_data_set(key, @~"data"); + assert!(*local_data_get(key).get() == ~"data"); + } + } + } } \ No newline at end of file diff --git a/src/libcore/rt/sched/mod.rs b/src/libcore/rt/sched/mod.rs index 30136e443ee..b7d861b8946 100644 --- a/src/libcore/rt/sched/mod.rs +++ b/src/libcore/rt/sched/mod.rs @@ -357,6 +357,13 @@ pub impl Task { start(); + unsafe { + // Destroy the local heap, TLS, etc. + let sched = local_sched::unsafe_borrow(); + let task = sched.current_task.get_mut_ref(); + task.local_services.destroy(); + } + let sched = local_sched::take(); sched.terminate_current_task(); }; diff --git a/src/libcore/task/local_data.rs b/src/libcore/task/local_data.rs index 261671f6de9..6e919a74ed4 100644 --- a/src/libcore/task/local_data.rs +++ b/src/libcore/task/local_data.rs @@ -27,7 +27,7 @@ magic. */ use prelude::*; -use task::local_data_priv::{local_get, local_pop, local_modify, local_set}; +use task::local_data_priv::{local_get, local_pop, local_modify, local_set, Handle}; use task::rt; /** @@ -53,7 +53,7 @@ pub type LocalDataKey<'self,T> = &'self fn(v: @T); pub unsafe fn local_data_pop( key: LocalDataKey) -> Option<@T> { - local_pop(rt::rust_get_task(), key) + local_pop(Handle::new(), key) } /** * Retrieve a task-local data value. It will also be kept alive in the @@ -62,7 +62,7 @@ pub unsafe fn local_data_pop( pub unsafe fn local_data_get( key: LocalDataKey) -> Option<@T> { - local_get(rt::rust_get_task(), key) + local_get(Handle::new(), key) } /** * Store a value in task-local data. If this key already has a value, @@ -71,7 +71,7 @@ pub unsafe fn local_data_get( pub unsafe fn local_data_set( key: LocalDataKey, data: @T) { - local_set(rt::rust_get_task(), key, data) + local_set(Handle::new(), key, data) } /** * Modify a task-local data value. If the function returns 'None', the @@ -81,7 +81,7 @@ pub unsafe fn local_data_modify( key: LocalDataKey, modify_fn: &fn(Option<@T>) -> Option<@T>) { - local_modify(rt::rust_get_task(), key, modify_fn) + local_modify(Handle::new(), key, modify_fn) } #[test] diff --git a/src/libcore/task/local_data_priv.rs b/src/libcore/task/local_data_priv.rs index 43f5fa4654b..50e8286e738 100644 --- a/src/libcore/task/local_data_priv.rs +++ b/src/libcore/task/local_data_priv.rs @@ -18,6 +18,30 @@ use task::rt; use task::local_data::LocalDataKey; use super::rt::rust_task; +use rt::local_services::LocalStorage; + +pub enum Handle { + OldHandle(*rust_task), + NewHandle(*mut LocalStorage) +} + +impl Handle { + pub fn new() -> Handle { + use rt::{context, OldTaskContext}; + use rt::local_services::unsafe_borrow_local_services; + unsafe { + match context() { + OldTaskContext => { + OldHandle(rt::rust_get_task()) + } + _ => { + let local_services = unsafe_borrow_local_services(); + NewHandle(&mut local_services.storage) + } + } + } + } +} pub trait LocalData { } impl LocalData for @T { } @@ -39,7 +63,7 @@ type TaskLocalElement = (*libc::c_void, *libc::c_void, @LocalData); // Has to be a pointer at outermost layer; the foreign call returns void *. type TaskLocalMap = @mut ~[Option]; -extern fn cleanup_task_local_map(map_ptr: *libc::c_void) { +fn cleanup_task_local_map(map_ptr: *libc::c_void) { unsafe { assert!(!map_ptr.is_null()); // Get and keep the single reference that was created at the @@ -50,8 +74,19 @@ extern fn cleanup_task_local_map(map_ptr: *libc::c_void) { } // Gets the map from the runtime. Lazily initialises if not done so already. +unsafe fn get_local_map(handle: Handle) -> TaskLocalMap { + match handle { + OldHandle(task) => get_task_local_map(task), + NewHandle(local_storage) => get_newsched_local_map(local_storage) + } +} + unsafe fn get_task_local_map(task: *rust_task) -> TaskLocalMap { + extern fn cleanup_task_local_map_(map_ptr: *libc::c_void) { + cleanup_task_local_map(map_ptr); + } + // Relies on the runtime initialising the pointer to null. // Note: The map's box lives in TLS invisibly referenced once. Each time // we retrieve it for get/set, we make another reference, which get/set @@ -62,7 +97,7 @@ unsafe fn get_task_local_map(task: *rust_task) -> TaskLocalMap { // Use reinterpret_cast -- transmute would take map away from us also. rt::rust_set_task_local_data( task, cast::reinterpret_cast(&map)); - rt::rust_task_local_data_atexit(task, cleanup_task_local_map); + rt::rust_task_local_data_atexit(task, cleanup_task_local_map_); // Also need to reference it an extra time to keep it for now. let nonmut = cast::transmute::]>(map); @@ -77,6 +112,32 @@ unsafe fn get_task_local_map(task: *rust_task) -> TaskLocalMap { } } +unsafe fn get_newsched_local_map(local: *mut LocalStorage) -> TaskLocalMap { + match &mut *local { + &LocalStorage(map_ptr, Some(_)) => { + assert!(map_ptr.is_not_null()); + let map = cast::transmute(map_ptr); + let nonmut = cast::transmute::]>(map); + cast::bump_box_refcount(nonmut); + return map; + } + &LocalStorage(ref mut map_ptr, ref mut at_exit) => { + assert!((*map_ptr).is_null()); + let map: TaskLocalMap = @mut ~[]; + // Use reinterpret_cast -- transmute would take map away from us also. + *map_ptr = cast::reinterpret_cast(&map); + let at_exit_fn: ~fn(*libc::c_void) = |p|cleanup_task_local_map(p); + *at_exit = Some(at_exit_fn); + // Also need to reference it an extra time to keep it for now. + let nonmut = cast::transmute::]>(map); + cast::bump_box_refcount(nonmut); + return map; + } + } +} + unsafe fn key_to_key_value( key: LocalDataKey) -> *libc::c_void { @@ -106,10 +167,10 @@ unsafe fn local_data_lookup( } unsafe fn local_get_helper( - task: *rust_task, key: LocalDataKey, + handle: Handle, key: LocalDataKey, do_pop: bool) -> Option<@T> { - let map = get_task_local_map(task); + let map = get_local_map(handle); // Interpreturn our findings from the map do local_data_lookup(map, key).map |result| { // A reference count magically appears on 'data' out of thin air. It @@ -128,23 +189,23 @@ unsafe fn local_get_helper( pub unsafe fn local_pop( - task: *rust_task, + handle: Handle, key: LocalDataKey) -> Option<@T> { - local_get_helper(task, key, true) + local_get_helper(handle, key, true) } pub unsafe fn local_get( - task: *rust_task, + handle: Handle, key: LocalDataKey) -> Option<@T> { - local_get_helper(task, key, false) + local_get_helper(handle, key, false) } pub unsafe fn local_set( - task: *rust_task, key: LocalDataKey, data: @T) { + handle: Handle, key: LocalDataKey, data: @T) { - let map = get_task_local_map(task); + let map = get_local_map(handle); // Store key+data as *voids. Data is invisibly referenced once; key isn't. let keyval = key_to_key_value(key); // We keep the data in two forms: one as an unsafe pointer, so we can get @@ -174,12 +235,12 @@ pub unsafe fn local_set( } pub unsafe fn local_modify( - task: *rust_task, key: LocalDataKey, + handle: Handle, key: LocalDataKey, modify_fn: &fn(Option<@T>) -> Option<@T>) { // Could be more efficient by doing the lookup work, but this is easy. - let newdata = modify_fn(local_pop(task, key)); + let newdata = modify_fn(local_pop(handle, key)); if newdata.is_some() { - local_set(task, key, newdata.unwrap()); + local_set(handle, key, newdata.unwrap()); } } diff --git a/src/libcore/task/spawn.rs b/src/libcore/task/spawn.rs index 118c4cc2312..1e3857dff9a 100644 --- a/src/libcore/task/spawn.rs +++ b/src/libcore/task/spawn.rs @@ -80,7 +80,7 @@ use prelude::*; use unstable; use ptr; use hashmap::HashSet; -use task::local_data_priv::{local_get, local_set}; +use task::local_data_priv::{local_get, local_set, OldHandle}; use task::rt::rust_task; use task::rt; use task::{Failure, ManualThreads, PlatformThread, SchedOpts, SingleThreaded}; @@ -451,7 +451,7 @@ fn gen_child_taskgroup(linked: bool, supervised: bool) /*##################################################################* * Step 1. Get spawner's taskgroup info. *##################################################################*/ - let spawner_group = match local_get(spawner, taskgroup_key!()) { + let spawner_group = match local_get(OldHandle(spawner), taskgroup_key!()) { None => { // Main task, doing first spawn ever. Lazily initialise here. let mut members = new_taskset(); @@ -463,7 +463,7 @@ fn gen_child_taskgroup(linked: bool, supervised: bool) // Main task/group has no ancestors, no notifier, etc. let group = @TCB(spawner, tasks, AncestorList(None), true, None); - local_set(spawner, taskgroup_key!(), group); + local_set(OldHandle(spawner), taskgroup_key!(), group); group } Some(group) => group @@ -627,7 +627,7 @@ fn spawn_raw_oldsched(opts: TaskOpts, f: ~fn()) { let group = @TCB(child, child_arc, ancestors, is_main, notifier); unsafe { - local_set(child, taskgroup_key!(), group); + local_set(OldHandle(child), taskgroup_key!(), group); } // Run the child's body. From e5d21b9ff1ea4160b728b62aeca110c0a563d9ee Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 22 Apr 2013 13:10:59 -0700 Subject: [PATCH 20/40] core::rt: Make I/O constructors return Option instead of Result For consistency, for all I/O calls, inspecting the error can be done with the io_error condition. --- src/libcore/rt/io/file.rs | 2 +- src/libcore/rt/io/net/tcp.rs | 4 ++-- src/libcore/rt/io/net/udp.rs | 4 ++-- src/libcore/rt/io/net/unix.rs | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libcore/rt/io/file.rs b/src/libcore/rt/io/file.rs index 4e3e01a5ece..e4fe066a173 100644 --- a/src/libcore/rt/io/file.rs +++ b/src/libcore/rt/io/file.rs @@ -42,7 +42,7 @@ impl FileStream { pub fn open(_path: &P, _mode: FileMode, _access: FileAccess - ) -> Result { + ) -> Option { fail!() } } diff --git a/src/libcore/rt/io/net/tcp.rs b/src/libcore/rt/io/net/tcp.rs index d78241b8e44..d726bae821c 100644 --- a/src/libcore/rt/io/net/tcp.rs +++ b/src/libcore/rt/io/net/tcp.rs @@ -16,7 +16,7 @@ use super::ip::IpAddr; pub struct TcpStream; impl TcpStream { - pub fn connect(_addr: IpAddr) -> Result { + pub fn connect(_addr: IpAddr) -> Option { fail!() } } @@ -40,7 +40,7 @@ impl Close for TcpStream { pub struct TcpListener; impl TcpListener { - pub fn bind(_addr: IpAddr) -> Result { + pub fn bind(_addr: IpAddr) -> Option { fail!() } } diff --git a/src/libcore/rt/io/net/udp.rs b/src/libcore/rt/io/net/udp.rs index 81a6677c14a..8691a697e88 100644 --- a/src/libcore/rt/io/net/udp.rs +++ b/src/libcore/rt/io/net/udp.rs @@ -16,7 +16,7 @@ use super::ip::IpAddr; pub struct UdpStream; impl UdpStream { - pub fn connect(_addr: IpAddr) -> Result { + pub fn connect(_addr: IpAddr) -> Option { fail!() } } @@ -40,7 +40,7 @@ impl Close for UdpStream { pub struct UdpListener; impl UdpListener { - pub fn bind(_addr: IpAddr) -> Result { + pub fn bind(_addr: IpAddr) -> Option { fail!() } } diff --git a/src/libcore/rt/io/net/unix.rs b/src/libcore/rt/io/net/unix.rs index a5f4f8e3ba8..bb3db6ec0d5 100644 --- a/src/libcore/rt/io/net/unix.rs +++ b/src/libcore/rt/io/net/unix.rs @@ -16,7 +16,7 @@ use super::super::support::PathLike; pub struct UnixStream; impl UnixStream { - pub fn connect(_path: &P) -> Result { + pub fn connect(_path: &P) -> Option { fail!() } } @@ -40,7 +40,7 @@ impl Close for UnixStream { pub struct UnixListener; impl UnixListener { - pub fn bind(_path: &P) -> Result { + pub fn bind(_path: &P) -> Option { fail!() } } From 6644a034f0650c638eea8809a5f035ffaca0de88 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 22 Apr 2013 13:26:37 -0700 Subject: [PATCH 21/40] core::rt: Move the definition of Listener to rt::io --- src/libcore/rt/io/mod.rs | 24 +++++++++++++++++++++--- src/libcore/rt/io/net/mod.rs | 31 ------------------------------- 2 files changed, 21 insertions(+), 34 deletions(-) delete mode 100644 src/libcore/rt/io/net/mod.rs diff --git a/src/libcore/rt/io/mod.rs b/src/libcore/rt/io/mod.rs index 238bd97a62d..f1b248c6e1d 100644 --- a/src/libcore/rt/io/mod.rs +++ b/src/libcore/rt/io/mod.rs @@ -122,7 +122,6 @@ pub use self::stdio::print; pub use self::stdio::println; pub use self::file::FileStream; -pub use self::net::Listener; pub use self::net::ip::IpAddr; pub use self::net::tcp::TcpListener; pub use self::net::tcp::TcpStream; @@ -137,8 +136,14 @@ pub use self::extensions::WriterByteConversions; pub mod file; /// Synchronous, non-blocking network I/O. -#[path = "net/mod.rs"] -pub mod net; +pub mod net { + pub mod tcp; + pub mod udp; + pub mod ip; + #[cfg(unix)] + pub mod unix; + pub mod http; +} /// Readers and Writers for memory buffers and strings. #[cfg(not(stage0))] // XXX Using unsnapshotted features @@ -280,6 +285,19 @@ pub trait Seek { fn seek(&mut self, pos: i64, style: SeekStyle); } +/// A listener is a value that listens for connections +pub trait Listener { + /// Wait for and accept an incoming connection + /// + /// Returns `None` on timeout. + /// + /// # Failure + /// + /// Raises `io_error` condition. If the condition is handled, + /// then `accept` returns `None`. + fn accept(&mut self) -> Option; +} + /// Common trait for decorator types. /// /// Provides accessors to get the inner, 'decorated' values. The I/O library diff --git a/src/libcore/rt/io/net/mod.rs b/src/libcore/rt/io/net/mod.rs deleted file mode 100644 index 130ff6b38fa..00000000000 --- a/src/libcore/rt/io/net/mod.rs +++ /dev/null @@ -1,31 +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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use prelude::*; - -pub mod tcp; -pub mod udp; -pub mod ip; -#[cfg(unix)] -pub mod unix; -pub mod http; - -/// A listener is a value that listens for connections -pub trait Listener { - /// Wait for and accept an incoming connection - /// - /// Returns `None` on timeout. - /// - /// # Failure - /// - /// Raises `io_error` condition. If the condition is handled, - /// then `accept` returns `None`. - fn accept(&mut self) -> Option; -} From fe13b865192028645b50c17d2cb1a6d44481f338 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 22 Apr 2013 14:43:02 -0700 Subject: [PATCH 22/40] Make conditions public. #6009 --- src/libcore/condition.rs | 21 +++++++++++++++++++++ src/libsyntax/ext/expand.rs | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/libcore/condition.rs b/src/libcore/condition.rs index dc6c80228dd..c639239981c 100644 --- a/src/libcore/condition.rs +++ b/src/libcore/condition.rs @@ -192,4 +192,25 @@ mod test { assert!(trapped); } + + // Issue #6009 + mod m { + condition! { + sadness: int -> int; + } + + mod n { + use super::sadness; + + #[test] + fn test_conditions_are_public() { + let mut trapped = false; + do sadness::cond.trap(|_| { + 0 + }).in { + sadness::cond.raise(0); + } + } + } + } } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 430402a8982..5c5817d480d 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -475,7 +475,7 @@ pub fn core_macros() -> ~str { { $c:ident: $in:ty -> $out:ty; } => { - mod $c { + pub mod $c { fn key(_x: @::core::condition::Handler<$in,$out>) { } pub static cond : From 5fbb0949a53a6ac51c6d9b187ef4c464e52ae536 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 22 Apr 2013 14:52:40 -0700 Subject: [PATCH 23/40] core::rt: Add implementations of Reader, Writer, and Listener for Option These will make it easier to write I/O code without worrying about errors --- src/libcore/rt/io/mod.rs | 24 +++++- src/libcore/rt/io/option.rs | 153 ++++++++++++++++++++++++++++++++++++ 2 files changed, 174 insertions(+), 3 deletions(-) create mode 100644 src/libcore/rt/io/option.rs diff --git a/src/libcore/rt/io/mod.rs b/src/libcore/rt/io/mod.rs index f1b248c6e1d..d9d6622277f 100644 --- a/src/libcore/rt/io/mod.rs +++ b/src/libcore/rt/io/mod.rs @@ -152,6 +152,9 @@ pub mod mem; /// Non-blocking access to stdin, stdout, stderr pub mod stdio; +/// Implementations for Option +mod option; + /// Basic stream compression. XXX: Belongs with other flate code #[cfg(not(stage0))] // XXX Using unsnapshotted features pub mod flate; @@ -194,12 +197,14 @@ pub struct IoError { detail: Option<~str> } +#[deriving(Eq)] pub enum IoErrorKind { FileNotFound, FilePermission, ConnectionFailed, Closed, - OtherIoError + OtherIoError, + PreviousIoError } // XXX: Can't put doc comments on macros @@ -232,9 +237,9 @@ pub trait Reader { /// println(reader.read_line()); /// } /// - /// # XXX + /// # Failue /// - /// What does this return if the Reader is in an error state? + /// Returns `true` on failure. fn eof(&mut self) -> bool; } @@ -323,3 +328,16 @@ pub trait Decorator { /// Take a mutable reference to the decorated value fn inner_mut_ref<'a>(&'a mut self) -> &'a mut T; } + +pub fn standard_error(kind: IoErrorKind) -> IoError { + match kind { + PreviousIoError => { + IoError { + kind: PreviousIoError, + desc: "Failing due to a previous I/O error", + detail: None + } + } + _ => fail!() + } +} diff --git a/src/libcore/rt/io/option.rs b/src/libcore/rt/io/option.rs new file mode 100644 index 00000000000..95f8711cb5b --- /dev/null +++ b/src/libcore/rt/io/option.rs @@ -0,0 +1,153 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementations of I/O traits for the Option type +//! +//! I/O constructors return option types to allow errors to be handled. +//! These implementations allow e.g. `Option` to be used +//! as a `Reader` without unwrapping the option first. +//! +//! # XXX Seek and Close + +use option::*; +use super::{Reader, Writer, Listener}; +use super::{standard_error, PreviousIoError, io_error, IoError}; + +fn prev_io_error() -> IoError { + standard_error(PreviousIoError) +} + +impl Writer for Option { + fn write(&mut self, buf: &[u8]) { + match *self { + Some(ref mut writer) => writer.write(buf), + None => io_error::cond.raise(prev_io_error()) + } + } + + fn flush(&mut self) { + match *self { + Some(ref mut writer) => writer.flush(), + None => io_error::cond.raise(prev_io_error()) + } + } +} + +impl Reader for Option { + fn read(&mut self, buf: &mut [u8]) -> Option { + match *self { + Some(ref mut reader) => reader.read(buf), + None => { + io_error::cond.raise(prev_io_error()); + None + } + } + } + + fn eof(&mut self) -> bool { + match *self { + Some(ref mut reader) => reader.eof(), + None => { + io_error::cond.raise(prev_io_error()); + true + } + } + } +} + +impl, S> Listener for Option { + fn accept(&mut self) -> Option { + match *self { + Some(ref mut listener) => listener.accept(), + None => { + io_error::cond.raise(prev_io_error()); + None + } + } + } +} + +#[cfg(test)] +mod test { + use option::*; + use super::super::mem::*; + use rt::test::*; + use super::super::{PreviousIoError, io_error}; + + #[test] + fn test_option_writer() { + do run_in_newsched_task { + let mut writer: Option = Some(MemWriter::new()); + writer.write([0, 1, 2]); + writer.flush(); + assert!(writer.unwrap().inner() == ~[0, 1, 2]); + } + } + + #[test] + fn test_option_writer_error() { + do run_in_newsched_task { + let mut writer: Option = None; + + let mut called = false; + do io_error::cond.trap(|err| { + assert!(err.kind == PreviousIoError); + called = true; + }).in { + writer.write([0, 0, 0]); + } + assert!(called); + + let mut called = false; + do io_error::cond.trap(|err| { + assert!(err.kind == PreviousIoError); + called = true; + }).in { + writer.flush(); + } + assert!(called); + } + } + + #[test] + fn test_option_reader() { + do run_in_newsched_task { + let mut reader: Option = Some(MemReader::new(~[0, 1, 2, 3])); + let mut buf = [0, 0]; + reader.read(buf); + assert!(buf == [0, 1]); + assert!(!reader.eof()); + } + } + + #[test] + fn test_option_reader_error() { + let mut reader: Option = None; + let mut buf = []; + + let mut called = false; + do io_error::cond.trap(|err| { + assert!(err.kind == PreviousIoError); + called = true; + }).in { + reader.read(buf); + } + assert!(called); + + let mut called = false; + do io_error::cond.trap(|err| { + assert!(err.kind == PreviousIoError); + called = true; + }).in { + assert!(reader.eof()); + } + assert!(called); + } +} From 42c0f88232847e97e6cf3578ef197d1942bba44d Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 22 Apr 2013 17:15:31 -0700 Subject: [PATCH 24/40] core::rt: Add unwinding to newsched tasks --- src/libcore/rt/io/mod.rs | 1 + src/libcore/rt/local_services.rs | 78 ++++++++++++++++++++++++++++++-- src/libcore/rt/sched/mod.rs | 7 +-- src/libcore/rt/test.rs | 41 +++++++++++++++++ src/libcore/sys.rs | 25 ++++++++-- src/libcore/task/mod.rs | 18 +++++++- src/rt/rust_builtin.cpp | 22 +++++++++ src/rt/rust_upcall.cpp | 16 +++++-- src/rt/rustrt.def.in | 2 + 9 files changed, 190 insertions(+), 20 deletions(-) diff --git a/src/libcore/rt/io/mod.rs b/src/libcore/rt/io/mod.rs index d9d6622277f..131743305bc 100644 --- a/src/libcore/rt/io/mod.rs +++ b/src/libcore/rt/io/mod.rs @@ -153,6 +153,7 @@ pub mod mem; pub mod stdio; /// Implementations for Option +#[cfg(not(stage0))] // Requires condition! fixes mod option; /// Basic stream compression. XXX: Belongs with other flate code diff --git a/src/libcore/rt/local_services.rs b/src/libcore/rt/local_services.rs index d29e57a17af..fc75a256428 100644 --- a/src/libcore/rt/local_services.rs +++ b/src/libcore/rt/local_services.rs @@ -19,7 +19,8 @@ //! (freestanding rust with local services?). use prelude::*; -use libc::c_void; +use libc::{c_void, uintptr_t}; +use cast::transmute; use super::sched::{Task, local_sched}; use super::local_heap::LocalHeap; @@ -35,7 +36,10 @@ pub struct LocalServices { pub struct GarbageCollector; pub struct LocalStorage(*c_void, Option<~fn(*c_void)>); pub struct Logger; -pub struct Unwinder; + +pub struct Unwinder { + unwinding: bool, +} impl LocalServices { pub fn new() -> LocalServices { @@ -44,17 +48,28 @@ impl LocalServices { gc: GarbageCollector, storage: LocalStorage(ptr::null(), None), logger: Logger, - unwinder: Unwinder, + unwinder: Unwinder { unwinding: false }, destroyed: false } } + pub fn run(&mut self, f: &fn()) { + // This is just an assertion that `run` was called unsafely + // and this instance of LocalServices is still accessible. + do borrow_local_services |sched| { + assert!(ptr::ref_eq(sched, self)); + } + + self.unwinder.try(f); + self.destroy(); + } + /// Must be called manually before finalization to clean up /// thread-local resources. Some of the routines here expect /// LocalServices to be available recursively so this must be /// called unsafely, without removing LocalServices from /// thread-local-storage. - pub fn destroy(&mut self) { + fn destroy(&mut self) { // This is just an assertion that `destroy` was called unsafely // and this instance of LocalServices is still accessible. do borrow_local_services |sched| { @@ -72,6 +87,51 @@ impl Drop for LocalServices { fn finalize(&self) { assert!(self.destroyed) } } +// Just a sanity check to make sure we are catching a Rust-thrown exception +static UNWIND_TOKEN: uintptr_t = 839147; + +impl Unwinder { + pub fn try(&mut self, f: &fn()) { + use sys::Closure; + + unsafe { + let closure: Closure = transmute(f); + let code = transmute(closure.code); + let env = transmute(closure.env); + + let token = rust_try(try_fn, code, env); + assert!(token == 0 || token == UNWIND_TOKEN); + } + + extern fn try_fn(code: *c_void, env: *c_void) { + unsafe { + let closure: Closure = Closure { + code: transmute(code), + env: transmute(env), + }; + let closure: &fn() = transmute(closure); + closure(); + } + } + + extern { + #[rust_stack] + fn rust_try(f: *u8, code: *c_void, data: *c_void) -> uintptr_t; + } + } + + pub fn begin_unwind(&mut self) -> ! { + self.unwinding = true; + unsafe { + rust_begin_unwind(UNWIND_TOKEN); + return transmute(()); + } + extern { + fn rust_begin_unwind(token: uintptr_t); + } + } +} + /// Borrow a pointer to the installed local services. /// Fails (likely aborting the process) if local services are not available. pub fn borrow_local_services(f: &fn(&mut LocalServices)) { @@ -125,4 +185,14 @@ mod test { } } } + + #[test] + fn unwind() { + do run_in_newsched_task() { + let result = spawn_try(||()); + assert!(result.is_ok()); + let result = spawn_try(|| fail!()); + assert!(result.is_err()); + } + } } \ No newline at end of file diff --git a/src/libcore/rt/sched/mod.rs b/src/libcore/rt/sched/mod.rs index b7d861b8946..65456c30fee 100644 --- a/src/libcore/rt/sched/mod.rs +++ b/src/libcore/rt/sched/mod.rs @@ -353,15 +353,10 @@ pub impl Task { unsafe { let sched = local_sched::unsafe_borrow(); sched.run_cleanup_job(); - } - start(); - - unsafe { - // Destroy the local heap, TLS, etc. let sched = local_sched::unsafe_borrow(); let task = sched.current_task.get_mut_ref(); - task.local_services.destroy(); + task.local_services.run(start); } let sched = local_sched::take(); diff --git a/src/libcore/rt/test.rs b/src/libcore/rt/test.rs index e394a873fea..f3d73c91bd6 100644 --- a/src/libcore/rt/test.rs +++ b/src/libcore/rt/test.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use result::{Result, Ok, Err}; use super::io::net::ip::{IpAddr, Ipv4}; /// Creates a new scheduler in a new thread and runs a task in it, @@ -47,6 +48,46 @@ pub fn spawn_immediately(f: ~fn()) { } } +/// Spawn a task and wait for it to finish, returning whether it completed successfully or failed +pub fn spawn_try(f: ~fn()) -> Result<(), ()> { + use cell::Cell; + use super::sched::*; + use task; + use unstable::finally::Finally; + + // Our status variables will be filled in from the scheduler context + let mut failed = false; + let failed_ptr: *mut bool = &mut failed; + + // Switch to the scheduler + let f = Cell(Cell(f)); + let mut sched = local_sched::take(); + do sched.deschedule_running_task_and_then() |old_task| { + let old_task = Cell(old_task); + let f = f.take(); + let mut sched = local_sched::take(); + let new_task = ~do Task::new(&mut sched.stack_pool) { + do (|| { + (f.take())() + }).finally { + // Check for failure then resume the parent task + unsafe { *failed_ptr = task::failing(); } + let sched = local_sched::take(); + do sched.switch_running_tasks_and_then(old_task.take()) |new_task| { + let new_task = Cell(new_task); + do local_sched::borrow |sched| { + sched.task_queue.push_front(new_task.take()); + } + } + } + }; + + sched.resume_task_immediately(new_task); + } + + if !failed { Ok(()) } else { Err(()) } +} + /// Get a port number, starting at 9600, for use in tests pub fn next_test_port() -> u16 { unsafe { diff --git a/src/libcore/sys.rs b/src/libcore/sys.rs index 04f96f5eb22..c50bc03517f 100644 --- a/src/libcore/sys.rs +++ b/src/libcore/sys.rs @@ -134,12 +134,27 @@ pub fn log_str(t: &T) -> ~str { /** Initiate task failure */ pub fn begin_unwind(msg: ~str, file: ~str, line: uint) -> ! { - do str::as_buf(msg) |msg_buf, _msg_len| { - do str::as_buf(file) |file_buf, _file_len| { + + use rt::{context, OldTaskContext}; + use rt::local_services::unsafe_borrow_local_services; + + match context() { + OldTaskContext => { + do str::as_buf(msg) |msg_buf, _msg_len| { + do str::as_buf(file) |file_buf, _file_len| { + unsafe { + let msg_buf = cast::transmute(msg_buf); + let file_buf = cast::transmute(file_buf); + begin_unwind_(msg_buf, file_buf, line as libc::size_t) + } + } + } + } + _ => { + gc::cleanup_stack_for_failure(); unsafe { - let msg_buf = cast::transmute(msg_buf); - let file_buf = cast::transmute(file_buf); - begin_unwind_(msg_buf, file_buf, line as libc::size_t) + let local_services = unsafe_borrow_local_services(); + local_services.unwinder.begin_unwind(); } } } diff --git a/src/libcore/task/mod.rs b/src/libcore/task/mod.rs index a243bfba85c..e1f4805a692 100644 --- a/src/libcore/task/mod.rs +++ b/src/libcore/task/mod.rs @@ -558,8 +558,22 @@ pub fn yield() { pub fn failing() -> bool { //! True if the running task has failed - unsafe { - rt::rust_task_is_unwinding(rt::rust_get_task()) + use rt::{context, OldTaskContext}; + use rt::local_services::borrow_local_services; + + match context() { + OldTaskContext => { + unsafe { + rt::rust_task_is_unwinding(rt::rust_get_task()) + } + } + _ => { + let mut unwinding = false; + do borrow_local_services |local| { + unwinding = local.unwinder.unwinding; + } + return unwinding; + } } } diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index b8749b8f73d..b37644460aa 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -886,6 +886,28 @@ rust_boxed_region_free(boxed_region *region, rust_opaque_box *box) { region->free(box); } +typedef void *(rust_try_fn)(void*, void*); + +extern "C" CDECL uintptr_t +rust_try(rust_try_fn f, void *fptr, void *env) { + try { + f(fptr, env); + } catch (uintptr_t token) { + assert(token != 0); + return token; + } + return 0; +} + +extern "C" CDECL void +rust_begin_unwind(uintptr_t token) { +#ifndef __WIN32__ + throw token; +#else + abort("failing on win32"); +#endif +} + // // Local Variables: // mode: C++ diff --git a/src/rt/rust_upcall.cpp b/src/rt/rust_upcall.cpp index 9f39e1433fc..34236c36c14 100644 --- a/src/rt/rust_upcall.cpp +++ b/src/rt/rust_upcall.cpp @@ -272,7 +272,13 @@ upcall_rust_personality(int version, s_rust_personality_args args = {(_Unwind_Reason_Code)0, version, actions, exception_class, ue_header, context}; - rust_task *task = rust_get_current_task(); + rust_task *task = rust_try_get_current_task(); + + if (task == NULL) { + // Assuming we're running with the new scheduler + upcall_s_rust_personality(&args); + return args.retval; + } // The personality function is run on the stack of the // last function that threw or landed, which is going @@ -309,8 +315,12 @@ upcall_del_stack() { // needs to acquire the value of the stack pointer extern "C" CDECL void upcall_reset_stack_limit() { - rust_task *task = rust_get_current_task(); - task->reset_stack_limit(); + rust_task *task = rust_try_get_current_task(); + if (task != NULL) { + task->reset_stack_limit(); + } else { + // We must be in a newsched task + } } // diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index 9aa80156783..5e9a4b343ee 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -228,3 +228,5 @@ rust_new_boxed_region rust_delete_boxed_region rust_boxed_region_malloc rust_boxed_region_free +rust_try +rust_begin_unwind \ No newline at end of file From a292d5175091f61e92d0f393275e32ad2b05d584 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 22 Apr 2013 19:20:12 -0700 Subject: [PATCH 25/40] mk: Pass CFLAGS to the uv build --- mk/rt.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mk/rt.mk b/mk/rt.mk index 015992abf78..538d1e2534f 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -169,7 +169,7 @@ $$(LIBUV_LIB_$(1)): $$(LIBUV_DEPS) else ifeq ($(OSTYPE_$(1)), linux-androideabi) $$(LIBUV_LIB_$(1)): $$(LIBUV_DEPS) $$(Q)$$(MAKE) -C $$(S)src/libuv/ \ - CFLAGS="$$(LIBUV_FLAGS_$$(HOST_$(1))) $$(SNAP_DEFINES)" \ + CFLAGS="$$(LIBUV_FLAGS_$$(HOST_$(1))) $$(SNAP_DEFINES) $$(CFLAGS)" \ LDFLAGS="$$(LIBUV_FLAGS_$$(HOST_$(1)))" \ CC="$$(CC_$(1))" \ CXX="$$(CXX_$(1))" \ @@ -181,7 +181,7 @@ $$(LIBUV_LIB_$(1)): $$(LIBUV_DEPS) else $$(LIBUV_LIB_$(1)): $$(LIBUV_DEPS) $$(Q)$$(MAKE) -C $$(S)src/libuv/ \ - CFLAGS="$$(LIBUV_FLAGS_$$(HOST_$(1))) $$(SNAP_DEFINES)" \ + CFLAGS="$$(LIBUV_FLAGS_$$(HOST_$(1))) $$(SNAP_DEFINES) $$(CFLAGS)" \ LDFLAGS="$$(LIBUV_FLAGS_$$(HOST_$(1)))" \ CC="$$(CC_$(1))" \ CXX="$$(CXX_$(1))" \ From 8a2f9cae213d38b31e9372b9d8a7fee1263f1363 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 22 Apr 2013 19:20:31 -0700 Subject: [PATCH 26/40] core::rt: Fix a use after free in uv 'write' --- src/libcore/rt/uv/mod.rs | 6 ++++-- src/libcore/rt/uv/net.rs | 16 ++++++++++------ src/libcore/rt/uvio.rs | 5 ++++- src/libcore/rt/uvll.rs | 6 +++--- 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/libcore/rt/uv/mod.rs b/src/libcore/rt/uv/mod.rs index 32757d6376e..4c4b2e03d3d 100644 --- a/src/libcore/rt/uv/mod.rs +++ b/src/libcore/rt/uv/mod.rs @@ -301,7 +301,8 @@ struct WatcherData { write_cb: Option, connect_cb: Option, close_cb: Option, - alloc_cb: Option + alloc_cb: Option, + buf: Option } pub fn install_watcher_data>(watcher: &mut W) { @@ -311,7 +312,8 @@ pub fn install_watcher_data>(watcher: &mut W) { write_cb: None, connect_cb: None, close_cb: None, - alloc_cb: None + alloc_cb: None, + buf: None }; let data = transmute::<~WatcherData, *c_void>(data); uvll::set_data_for_uv_handle(watcher.native_handle(), data); diff --git a/src/libcore/rt/uv/net.rs b/src/libcore/rt/uv/net.rs index 860c988b9c9..04b9008b067 100644 --- a/src/libcore/rt/uv/net.rs +++ b/src/libcore/rt/uv/net.rs @@ -107,21 +107,25 @@ pub impl StreamWatcher { let req = WriteRequest::new(); let buf = vec_to_uv_buf(msg); - // XXX: Allocation - let bufs = ~[buf]; + assert!(data.buf.is_none()); + data.buf = Some(buf); + let bufs = [buf]; unsafe { assert!(0 == uvll::write(req.native_handle(), self.native_handle(), - &bufs, write_cb)); + bufs, write_cb)); } - // XXX: Freeing immediately after write. Is this ok? - let _v = vec_from_uv_buf(buf); extern fn write_cb(req: *uvll::uv_write_t, status: c_int) { let write_request: WriteRequest = NativeHandle::from_native_handle(req); let mut stream_watcher = write_request.stream(); write_request.delete(); - let cb = get_watcher_data(&mut stream_watcher).write_cb.swap_unwrap(); + let cb = { + let data = get_watcher_data(&mut stream_watcher); + let _vec = vec_from_uv_buf(data.buf.swap_unwrap()); + let cb = data.write_cb.swap_unwrap(); + cb + }; let status = status_to_maybe_uv_error(stream_watcher.native_handle(), status); cb(stream_watcher, status); } diff --git a/src/libcore/rt/uvio.rs b/src/libcore/rt/uvio.rs index 2e9d0afa52f..e7b2880b74b 100644 --- a/src/libcore/rt/uvio.rs +++ b/src/libcore/rt/uvio.rs @@ -445,7 +445,7 @@ fn test_read_read_read() { let io = local_sched::unsafe_borrow_io(); let mut listener = io.bind(addr).unwrap(); let mut stream = listener.listen().unwrap(); - let mut buf = [0, .. 2048]; + let mut buf = [1, .. 2048]; let mut total_bytes_written = 0; while total_bytes_written < MAX { stream.write(buf); @@ -465,6 +465,9 @@ fn test_read_read_read() { let nread = stream.read(buf).unwrap(); rtdebug!("read %u bytes", nread as uint); total_bytes_read += nread; + for uint::range(0, nread) |i| { + assert!(buf[i] == 1); + } } rtdebug!("read %u bytes total", total_bytes_read as uint); stream.close(); diff --git a/src/libcore/rt/uvll.rs b/src/libcore/rt/uvll.rs index 640a69743ba..0f75ebb6090 100644 --- a/src/libcore/rt/uvll.rs +++ b/src/libcore/rt/uvll.rs @@ -219,9 +219,9 @@ pub unsafe fn accept(server: *c_void, client: *c_void) -> c_int { return rust_uv_accept(server as *c_void, client as *c_void); } -pub unsafe fn write(req: *uv_write_t, stream: *T, buf_in: *~[uv_buf_t], cb: *u8) -> c_int { - let buf_ptr = vec::raw::to_ptr(*buf_in); - let buf_cnt = vec::len(*buf_in) as i32; +pub unsafe fn write(req: *uv_write_t, stream: *T, buf_in: &[uv_buf_t], cb: *u8) -> c_int { + let buf_ptr = vec::raw::to_ptr(buf_in); + let buf_cnt = vec::len(buf_in) as i32; return rust_uv_write(req as *c_void, stream as *c_void, buf_ptr, buf_cnt, cb); } pub unsafe fn read_start(stream: *uv_stream_t, on_alloc: *u8, on_read: *u8) -> c_int { From f4af40a1db4862cfe1f17311e3e39cfff3324d82 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 22 Apr 2013 21:27:24 -0700 Subject: [PATCH 27/40] mk: core has another level of directories with rt/io/net --- Makefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index dd2e6a95861..33077872c16 100644 --- a/Makefile.in +++ b/Makefile.in @@ -238,7 +238,7 @@ $(foreach target,$(CFG_TARGET_TRIPLES),\ CORELIB_CRATE := $(S)src/libcore/core.rc CORELIB_INPUTS := $(wildcard $(addprefix $(S)src/libcore/, \ - core.rc *.rs */*.rs */*/*rs)) + core.rc *.rs */*.rs */*/*rs */*/*/*rs)) ###################################################################### # Standard library variables From 2a819ae465c5f375df00ead0b3f4c9009da23f25 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Tue, 23 Apr 2013 15:11:28 -0700 Subject: [PATCH 28/40] core::rt: Tasks to not require an unwinder A task without an unwinder will abort the process on failure. I'm using this in the runtime tests to guarantee that a call to `assert!` actually triggers some kind of failure (an abort) instead of silently doing nothing. This is essentially in lieu of a working linked failure implementation. --- src/libcore/core.rc | 3 +++ src/libcore/macros.rs | 39 +++++++++++++++++++++++++++++ src/libcore/rt/local_services.rs | 30 +++++++++++++++++++---- src/libcore/rt/mod.rs | 20 --------------- src/libcore/rt/sched/mod.rs | 10 ++++++-- src/libcore/rt/test.rs | 42 +++++++++++++++++++++++--------- src/libcore/rt/uvio.rs | 12 ++++----- src/libcore/sys.rs | 6 ++++- src/libcore/task/mod.rs | 11 ++++++++- 9 files changed, 126 insertions(+), 47 deletions(-) create mode 100644 src/libcore/macros.rs diff --git a/src/libcore/core.rc b/src/libcore/core.rc index e7a5cfbaf4b..a3b2cb4aaf9 100644 --- a/src/libcore/core.rc +++ b/src/libcore/core.rc @@ -114,6 +114,9 @@ pub mod linkhack { } } +// Internal macros +mod macros; + /* The Prelude. */ pub mod prelude; diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs new file mode 100644 index 00000000000..e1276a75e05 --- /dev/null +++ b/src/libcore/macros.rs @@ -0,0 +1,39 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_escape]; + +// Some basic logging +macro_rules! rtdebug ( + ($( $arg:expr),+) => ( { + dumb_println(fmt!( $($arg),+ )); + + fn dumb_println(s: &str) { + use io::WriterUtil; + let dbg = ::libc::STDERR_FILENO as ::io::fd_t; + dbg.write_str(s); + dbg.write_str("\n"); + } + + } ) +) + +// An alternate version with no output, for turning off logging +macro_rules! rtdebug_ ( + ($( $arg:expr),+) => ( $(let _ = $arg)*; ) +) + +macro_rules! abort( + ($( $msg:expr),+) => ( { + rtdebug!($($msg),+); + + unsafe { ::libc::abort(); } + } ) +) diff --git a/src/libcore/rt/local_services.rs b/src/libcore/rt/local_services.rs index fc75a256428..d09d082c858 100644 --- a/src/libcore/rt/local_services.rs +++ b/src/libcore/rt/local_services.rs @@ -29,7 +29,7 @@ pub struct LocalServices { gc: GarbageCollector, storage: LocalStorage, logger: Logger, - unwinder: Unwinder, + unwinder: Option, destroyed: bool } @@ -48,7 +48,18 @@ impl LocalServices { gc: GarbageCollector, storage: LocalStorage(ptr::null(), None), logger: Logger, - unwinder: Unwinder { unwinding: false }, + unwinder: Some(Unwinder { unwinding: false }), + destroyed: false + } + } + + pub fn without_unwinding() -> LocalServices { + LocalServices { + heap: LocalHeap::new(), + gc: GarbageCollector, + storage: LocalStorage(ptr::null(), None), + logger: Logger, + unwinder: None, destroyed: false } } @@ -60,7 +71,16 @@ impl LocalServices { assert!(ptr::ref_eq(sched, self)); } - self.unwinder.try(f); + match self.unwinder { + Some(ref mut unwinder) => { + // If there's an unwinder then set up the catch block + unwinder.try(f); + } + None => { + // Otherwise, just run the body + f() + } + } self.destroy(); } @@ -189,9 +209,9 @@ mod test { #[test] fn unwind() { do run_in_newsched_task() { - let result = spawn_try(||()); + let result = spawntask_try(||()); assert!(result.is_ok()); - let result = spawn_try(|| fail!()); + let result = spawntask_try(|| fail!()); assert!(result.is_err()); } } diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs index 4a767d61f74..ab89a4c26a5 100644 --- a/src/libcore/rt/mod.rs +++ b/src/libcore/rt/mod.rs @@ -12,26 +12,6 @@ use libc::c_char; -// Some basic logging -macro_rules! rtdebug_ ( - ($( $arg:expr),+) => ( { - dumb_println(fmt!( $($arg),+ )); - - fn dumb_println(s: &str) { - use io::WriterUtil; - let dbg = ::libc::STDERR_FILENO as ::io::fd_t; - dbg.write_str(s); - dbg.write_str("\n"); - } - - } ) -) - -// An alternate version with no output, for turning off logging -macro_rules! rtdebug ( - ($( $arg:expr),+) => ( $(let _ = $arg)*; ) -) - #[path = "sched/mod.rs"] mod sched; mod rtio; diff --git a/src/libcore/rt/sched/mod.rs b/src/libcore/rt/sched/mod.rs index 65456c30fee..f7b9bd82668 100644 --- a/src/libcore/rt/sched/mod.rs +++ b/src/libcore/rt/sched/mod.rs @@ -149,7 +149,7 @@ pub impl Scheduler { } } - // Control never reaches here + abort!("control reached end of task"); } fn schedule_new_task(~self, task: ~Task) { @@ -333,6 +333,12 @@ pub struct Task { pub impl Task { fn new(stack_pool: &mut StackPool, start: ~fn()) -> Task { + Task::with_local(stack_pool, LocalServices::new(), start) + } + + fn with_local(stack_pool: &mut StackPool, + local_services: LocalServices, + start: ~fn()) -> Task { let start = Task::build_start_wrapper(start); let mut stack = stack_pool.take_segment(TASK_MIN_STACK_SIZE); // NB: Context holds a pointer to that ~fn @@ -340,7 +346,7 @@ pub impl Task { return Task { current_stack_segment: stack, saved_context: initial_context, - local_services: LocalServices::new() + local_services: local_services }; } diff --git a/src/libcore/rt/test.rs b/src/libcore/rt/test.rs index f3d73c91bd6..f7ba881f84e 100644 --- a/src/libcore/rt/test.rs +++ b/src/libcore/rt/test.rs @@ -8,38 +8,56 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use cell::Cell; use result::{Result, Ok, Err}; use super::io::net::ip::{IpAddr, Ipv4}; +use rt::local_services::LocalServices; /// Creates a new scheduler in a new thread and runs a task in it, -/// then waits for the scheduler to exit. +/// then waits for the scheduler to exit. Failure of the task +/// will abort the process. pub fn run_in_newsched_task(f: ~fn()) { - use cell::Cell; use unstable::run_in_bare_thread; use super::sched::Task; use super::uvio::UvEventLoop; - let f = Cell(Cell(f)); + let f = Cell(f); do run_in_bare_thread { let mut sched = ~UvEventLoop::new_scheduler(); - let f = f.take(); - let task = ~do Task::new(&mut sched.stack_pool) { - (f.take())(); - }; + let task = ~Task::with_local(&mut sched.stack_pool, + LocalServices::without_unwinding(), + f.take()); sched.task_queue.push_back(task); sched.run(); } } -/// Create a new task and run it right now -pub fn spawn_immediately(f: ~fn()) { - use cell::Cell; +/// Test tasks will abort on failure instead of unwinding +pub fn spawntask(f: ~fn()) { use super::*; use super::sched::*; let mut sched = local_sched::take(); - let task = ~Task::new(&mut sched.stack_pool, f); + let task = ~Task::with_local(&mut sched.stack_pool, + LocalServices::without_unwinding(), + f); + do sched.switch_running_tasks_and_then(task) |task| { + let task = Cell(task); + let sched = local_sched::take(); + sched.schedule_new_task(task.take()); + } +} + +/// Create a new task and run it right now. Aborts on failure +pub fn spawntask_immediately(f: ~fn()) { + use super::*; + use super::sched::*; + + let mut sched = local_sched::take(); + let task = ~Task::with_local(&mut sched.stack_pool, + LocalServices::without_unwinding(), + f); do sched.switch_running_tasks_and_then(task) |task| { let task = Cell(task); do local_sched::borrow |sched| { @@ -49,7 +67,7 @@ pub fn spawn_immediately(f: ~fn()) { } /// Spawn a task and wait for it to finish, returning whether it completed successfully or failed -pub fn spawn_try(f: ~fn()) -> Result<(), ()> { +pub fn spawntask_try(f: ~fn()) -> Result<(), ()> { use cell::Cell; use super::sched::*; use task; diff --git a/src/libcore/rt/uvio.rs b/src/libcore/rt/uvio.rs index e7b2880b74b..4cceb048cbc 100644 --- a/src/libcore/rt/uvio.rs +++ b/src/libcore/rt/uvio.rs @@ -350,7 +350,7 @@ fn test_simple_tcp_server_and_client() { let addr = next_test_ip4(); // Start the server first so it's listening when we connect - do spawn_immediately { + do spawntask_immediately { unsafe { let io = local_sched::unsafe_borrow_io(); let mut listener = io.bind(addr).unwrap(); @@ -367,7 +367,7 @@ fn test_simple_tcp_server_and_client() { } } - do spawn_immediately { + do spawntask_immediately { unsafe { let io = local_sched::unsafe_borrow_io(); let mut stream = io.connect(addr).unwrap(); @@ -383,7 +383,7 @@ fn test_read_and_block() { do run_in_newsched_task { let addr = next_test_ip4(); - do spawn_immediately { + do spawntask_immediately { let io = unsafe { local_sched::unsafe_borrow_io() }; let mut listener = io.bind(addr).unwrap(); let mut stream = listener.listen().unwrap(); @@ -421,7 +421,7 @@ fn test_read_and_block() { listener.close(); } - do spawn_immediately { + do spawntask_immediately { let io = unsafe { local_sched::unsafe_borrow_io() }; let mut stream = io.connect(addr).unwrap(); stream.write([0, 1, 2, 3, 4, 5, 6, 7]); @@ -440,7 +440,7 @@ fn test_read_read_read() { let addr = next_test_ip4(); static MAX: uint = 500000; - do spawn_immediately { + do spawntask_immediately { unsafe { let io = local_sched::unsafe_borrow_io(); let mut listener = io.bind(addr).unwrap(); @@ -456,7 +456,7 @@ fn test_read_read_read() { } } - do spawn_immediately { + do spawntask_immediately { let io = unsafe { local_sched::unsafe_borrow_io() }; let mut stream = io.connect(addr).unwrap(); let mut buf = [0, .. 2048]; diff --git a/src/libcore/sys.rs b/src/libcore/sys.rs index c50bc03517f..2c3670ad498 100644 --- a/src/libcore/sys.rs +++ b/src/libcore/sys.rs @@ -10,6 +10,7 @@ //! Misc low level stuff +use option::{Some, None}; use cast; use cmp::{Eq, Ord}; use gc; @@ -154,7 +155,10 @@ pub fn begin_unwind(msg: ~str, file: ~str, line: uint) -> ! { gc::cleanup_stack_for_failure(); unsafe { let local_services = unsafe_borrow_local_services(); - local_services.unwinder.begin_unwind(); + match local_services.unwinder { + Some(ref mut unwinder) => unwinder.begin_unwind(), + None => abort!("failure without unwinder. aborting process") + } } } } diff --git a/src/libcore/task/mod.rs b/src/libcore/task/mod.rs index e1f4805a692..d31a511eca8 100644 --- a/src/libcore/task/mod.rs +++ b/src/libcore/task/mod.rs @@ -570,7 +570,16 @@ pub fn failing() -> bool { _ => { let mut unwinding = false; do borrow_local_services |local| { - unwinding = local.unwinder.unwinding; + unwinding = match local.unwinder { + Some(unwinder) => { + unwinder.unwinding + } + None => { + // Because there is no unwinder we can't be unwinding. + // (The process will abort on failure) + false + } + } } return unwinding; } From 6373861510795bcaa6e98e97942c32eb26263bd8 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Tue, 23 Apr 2013 19:52:16 -0700 Subject: [PATCH 29/40] core: Convert reinterpret_cast to transmute in TLS. #6039 --- src/libcore/task/local_data_priv.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/libcore/task/local_data_priv.rs b/src/libcore/task/local_data_priv.rs index 66808bf21aa..e32d18719bd 100644 --- a/src/libcore/task/local_data_priv.rs +++ b/src/libcore/task/local_data_priv.rs @@ -49,8 +49,8 @@ impl LocalData for @T { } impl Eq for @LocalData { fn eq(&self, other: &@LocalData) -> bool { unsafe { - let ptr_a: (uint, uint) = cast::reinterpret_cast(&(*self)); - let ptr_b: (uint, uint) = cast::reinterpret_cast(other); + let ptr_a: &(uint, uint) = cast::transmute(self); + let ptr_b: &(uint, uint) = cast::transmute(other); return ptr_a == ptr_b; } } @@ -68,7 +68,7 @@ fn cleanup_task_local_map(map_ptr: *libc::c_void) { assert!(!map_ptr.is_null()); // Get and keep the single reference that was created at the // beginning. - let _map: TaskLocalMap = cast::reinterpret_cast(&map_ptr); + let _map: TaskLocalMap = cast::transmute(map_ptr); // All local_data will be destroyed along with the map. } } @@ -125,14 +125,9 @@ unsafe fn get_newsched_local_map(local: *mut LocalStorage) -> TaskLocalMap { &LocalStorage(ref mut map_ptr, ref mut at_exit) => { assert!((*map_ptr).is_null()); let map: TaskLocalMap = @mut ~[]; - // Use reinterpret_cast -- transmute would take map away from us also. - *map_ptr = cast::reinterpret_cast(&map); + *map_ptr = cast::transmute(map); let at_exit_fn: ~fn(*libc::c_void) = |p|cleanup_task_local_map(p); *at_exit = Some(at_exit_fn); - // Also need to reference it an extra time to keep it for now. - let nonmut = cast::transmute::]>(map); - cast::bump_box_refcount(nonmut); return map; } } @@ -143,7 +138,7 @@ unsafe fn key_to_key_value( // Keys are closures, which are (fnptr,envptr) pairs. Use fnptr. // Use reintepret_cast -- transmute would leak (forget) the closure. - let pair: (*libc::c_void, *libc::c_void) = cast::reinterpret_cast(&key); + let pair: (*libc::c_void, *libc::c_void) = cast::transmute(key); pair.first() } @@ -213,7 +208,7 @@ pub unsafe fn local_set( // own on it can be dropped when the box is destroyed. The unsafe pointer // does not have a reference associated with it, so it may become invalid // when the box is destroyed. - let data_ptr = cast::reinterpret_cast(&data); + let data_ptr = *cast::transmute::<&@T, &*libc::c_void>(&data); let data_box = @data as @LocalData; // Construct new entry to store in the map. let new_entry = Some((keyval, data_ptr, data_box)); From c0e734d203ac5c51ba2cac7b4e5ef099d83350da Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Tue, 23 Apr 2013 19:21:37 -0700 Subject: [PATCH 30/40] core::rt: Add more I/O docs --- src/libcore/rt/io/mod.rs | 171 ++++++++++++++++++++++++++++++++++----- src/libcore/rt/mod.rs | 2 + 2 files changed, 152 insertions(+), 21 deletions(-) diff --git a/src/libcore/rt/io/mod.rs b/src/libcore/rt/io/mod.rs index 131743305bc..ced4ba0ee23 100644 --- a/src/libcore/rt/io/mod.rs +++ b/src/libcore/rt/io/mod.rs @@ -10,12 +10,14 @@ /*! Synchronous I/O -This module defines the Rust interface for synchronous I/O. It is -build around Reader and Writer traits that define byte stream sources -and sinks. Implementations are provided for common I/O streams like -file, TCP, UDP, Unix domain sockets, multiple types of memory bufers. -Readers and Writers may be composed to add things like string parsing, -and compression. +This module defines the Rust interface for synchronous I/O. +It models byte-oriented input and output with the Reader and Writer traits. +Types that implement both `Reader` and `Writer` and called 'streams', +and automatically implement trait `Stream`. +Implementations are provided for common I/O streams like +file, TCP, UDP, Unix domain sockets. +Readers and Writers may be composed to add capabilities like string +parsing, encoding, and compression. This will likely live in core::io, not core::rt::io. @@ -31,22 +33,22 @@ Some examples of obvious things you might want to do * Read a complete file to a string, (converting newlines?) - let contents = FileStream::open("message.txt").read_to_str(); // read_to_str?? + let contents = File::open("message.txt").read_to_str(); // read_to_str?? * Write a line to a file - let file = FileStream::open("message.txt", Create, Write); + let file = File::open("message.txt", Create, Write); file.write_line("hello, file!"); * Iterate over the lines of a file - do FileStream::open("message.txt").each_line |line| { + do File::open("message.txt").each_line |line| { println(line) } * Pull the lines of a file into a vector of strings - let lines = FileStream::open("message.txt").line_iter().to_vec(); + let lines = File::open("message.txt").line_iter().to_vec(); * Make an simple HTTP request @@ -63,25 +65,145 @@ Some examples of obvious things you might want to do # Terms -* reader -* writer -* stream -* Blocking vs. non-blocking -* synchrony and asynchrony +* Reader - An I/O source, reads bytes into a buffer +* Writer - An I/O sink, writes bytes from a buffer +* Stream - Typical I/O sources like files and sockets are both Readers and Writers, + and are collectively referred to a `streams`. +* Decorator - A Reader or Writer that composes with others to add additional capabilities + such as encoding or decoding -I tend to call this implementation non-blocking, because performing I/O -doesn't block the progress of other tasks. Is that how we want to present -it, 'synchronous but non-blocking'? +# Blocking and synchrony + +When discussing I/O you often hear the terms 'synchronous' and +'asynchronous', along with 'blocking' and 'non-blocking' compared and +contrasted. A synchronous I/O interface performs each I/O operation to +completion before proceeding to the next. Synchronous interfaces are +usually used in imperative style as a sequence of commands. An +asynchronous interface allows multiple I/O requests to be issued +simultaneously, without waiting for each to complete before proceeding +to the next. + +Asynchronous interfaces are used to achieve 'non-blocking' I/O. In +traditional single-threaded systems, performing a synchronous I/O +operation means that the program stops all activity (it 'blocks') +until the I/O is complete. Blocking is bad for performance when +there are other computations that could be done. + +Asynchronous interfaces are most often associated with the callback +(continuation-passing) style popularised by node.js. Such systems rely +on all computations being run inside an event loop which maintains a +list of all pending I/O events; when one completes the registered +callback is run and the code that made the I/O request continiues. +Such interfaces achieve non-blocking at the expense of being more +difficult to reason about. + +Rust's I/O interface is synchronous - easy to read - and non-blocking by default. + +Remember that Rust tasks are 'green threads', lightweight threads that +are multiplexed onto a single operating system thread. If that system +thread blocks then no other task may proceed. Rust tasks are +relatively cheap to create, so as long as other tasks are free to +execute then non-blocking code may be written by simply creating a new +task. + +When discussing blocking in regards to Rust's I/O model, we are +concerned with whether performing I/O blocks other Rust tasks from +proceeding. In other words, when a task calls `read`, it must then +wait (or 'sleep', or 'block') until the call to `read` is complete. +During this time, other tasks may or may not be executed, depending on +how `read` is implemented. + + +Rust's default I/O implementation is non-blocking; by cooperating +directly with the task scheduler it arranges to never block progress +of *other* tasks. Under the hood, Rust uses asynchronous I/O via a +per-scheduler (and hence per-thread) event loop. Synchronous I/O +requests are implemented by descheduling the running task and +performing an asynchronous request; the task is only resumed once the +asynchronous request completes. + +For blocking (but possibly more efficient) implementations, look +in the `io::native` module. # Error Handling +I/O is an area where nearly every operation can result in unexpected +errors. It should allow errors to be handled efficiently. +It needs to be convenient to use I/O when you don't care +about dealing with specific errors. + +Rust's I/O employs a combination of techniques to reduce boilerplate +while still providing feedback about errors. The basic strategy: + +* Errors are fatal by default, resulting in task failure +* Errors raise the `io_error` conditon which provides an opportunity to inspect + an IoError object containing details. +* Return values must have a sensible null or zero value which is returned + if a condition is handled successfully. This may be an `Option`, an empty + vector, or other designated error value. +* Common traits are implemented for `Option`, e.g. `impl Reader for Option`, + so that nullable values do not have to be 'unwrapped' before use. + +These features combine in the API to allow for expressions like +`File::new("diary.txt").write_line("met a girl")` without having to +worry about whether "diary.txt" exists or whether the write +succeeds. As written, if either `new` or `write_line` encounters +an error the task will fail. + +If you wanted to handle the error though you might write + + let mut error = None; + do io_error::cond(|e: IoError| { + error = Some(e); + }).in { + File::new("diary.txt").write_line("met a girl"); + } + + if error.is_some() { + println("failed to write my diary"); + } + +XXX: Need better condition handling syntax + +In this case the condition handler will have the opportunity to +inspect the IoError raised by either the call to `new` or the call to +`write_line`, but then execution will continue. + +So what actually happens if `new` encounters an error? To understand +that it's important to know that what `new` returns is not a `File` +but an `Option`. If the file does not open, and the condition +is handled, then `new` will simply return `None`. Because there is an +implementation of `Writer` (the trait required ultimately required for +types to implement `write_line`) there is no need to inspect or unwrap +the `Option` and we simply call `write_line` on it. If `new` +returned a `None` then the followup call to `write_line` will also +raise an error. + +## Concerns about this strategy + +This structure will encourage a programming style that is prone +to errors similar to null pointer dereferences. +In particular code written to ignore errors and expect conditions to be unhandled +will start passing around null or zero objects when wrapped in a condition handler. + +* XXX: How should we use condition handlers that return values? + + +# Issues withi/o scheduler affinity, work stealing, task pinning + # Resource management * `close` vs. RAII -# Paths and URLs +# Paths, URLs and overloaded constructors -# std + + +# Scope + +In scope for core + +* Url? Some I/O things don't belong in core @@ -90,7 +212,12 @@ Some I/O things don't belong in core - http - flate -# XXX +Out of scope + +* Async I/O. We'll probably want it eventually + + +# XXX Questions and issues * Should default constructors take `Path` or `&str`? `Path` makes simple cases verbose. Overloading would be nice. @@ -100,6 +227,7 @@ Some I/O things don't belong in core * fsync * relationship with filesystem querying, Directory, File types etc. * Rename Reader/Writer to ByteReader/Writer, make Reader/Writer generic? +* Can Port and Chan be implementations of a generic Reader/Writer? * Trait for things that are both readers and writers, Stream? * How to handle newline conversion * String conversion @@ -109,6 +237,7 @@ Some I/O things don't belong in core * Do we need `close` at all? dtors might be good enough * How does I/O relate to the Iterator trait? * std::base64 filters +* Using conditions is a big unknown since we don't have much experience with them */ diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs index ab89a4c26a5..56ed7dc95b6 100644 --- a/src/libcore/rt/mod.rs +++ b/src/libcore/rt/mod.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +/*! The Rust runtime, including the scheduler and I/O interface */ + #[doc(hidden)]; use libc::c_char; From 354460e53b8de468db77433e97e7cdf809c68ccd Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Tue, 23 Apr 2013 20:41:00 -0700 Subject: [PATCH 31/40] Tidy --- src/libcore/rt/uvio.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/rt/uvio.rs b/src/libcore/rt/uvio.rs index 54f3276c317..984ae55399c 100644 --- a/src/libcore/rt/uvio.rs +++ b/src/libcore/rt/uvio.rs @@ -439,7 +439,7 @@ fn test_read_read_read() { do run_in_newsched_task { let addr = next_test_ip4(); static MAX: uint = 500000; - + do spawntask_immediately { unsafe { let io = local_sched::unsafe_borrow_io(); From 34f7255afa4aa80ade1f98b99f0f54cb424b129e Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Tue, 23 Apr 2013 22:51:48 -0700 Subject: [PATCH 32/40] mk: reorganize doc build to fix dependencies. #6042 Most of our documentation requires both pandoc and node.js. This simplifies the logic around those checks and fixes an error when building without node.js but with pandoc. --- mk/docs.mk | 73 +++++++++++++++++++++++++----------------------------- 1 file changed, 34 insertions(+), 39 deletions(-) diff --git a/mk/docs.mk b/mk/docs.mk index 6873d433e95..252f62cf871 100644 --- a/mk/docs.mk +++ b/mk/docs.mk @@ -16,15 +16,8 @@ DOCS := ###################################################################### -# Pandoc (reference-manual related) +# Docs, from pandoc, rustdoc (which runs pandoc), and node ###################################################################### -ifeq ($(CFG_PANDOC),) - $(info cfg: no pandoc found, omitting doc/rust.pdf) -else - - ifeq ($(CFG_NODE),) - $(info cfg: no node found, omitting doc/tutorial.html) - else doc/rust.css: rust.css @$(call E, cp: $@) @@ -34,6 +27,18 @@ doc/manual.css: manual.css @$(call E, cp: $@) $(Q)cp -a $< $@ 2> /dev/null +ifeq ($(CFG_PANDOC),) + $(info cfg: no pandoc found, omitting docs) + NO_DOCS = 1 +endif + +ifeq ($(CFG_NODE),) + $(info cfg: no node found, omitting docs) + NO_DOCS = 1 +endif + +ifneq ($(NO_DOCS),1) + DOCS += doc/rust.html doc/rust.html: rust.md doc/version_info.html doc/rust.css doc/manual.css @$(call E, pandoc: $@) @@ -47,17 +52,6 @@ doc/rust.html: rust.md doc/version_info.html doc/rust.css doc/manual.css --css=manual.css \ --include-before-body=doc/version_info.html \ --output=$@ - endif - - ifeq ($(CFG_PDFLATEX),) - $(info cfg: no pdflatex found, omitting doc/rust.pdf) - else - ifeq ($(CFG_XETEX),) - $(info cfg: no xetex found, disabling doc/rust.pdf) - else - ifeq ($(CFG_LUATEX),) - $(info cfg: lacking luatex, disabling pdflatex) - else DOCS += doc/rust.pdf doc/rust.tex: rust.md doc/version.md @@ -70,17 +64,6 @@ doc/rust.tex: rust.md doc/version.md --from=markdown --to=latex \ --output=$@ -doc/rust.pdf: doc/rust.tex - @$(call E, pdflatex: $@) - $(Q)$(CFG_PDFLATEX) \ - -interaction=batchmode \ - -output-directory=doc \ - $< - - endif - endif - endif - DOCS += doc/rustpkg.html doc/rustpkg.html: rustpkg.md doc/version_info.html doc/rust.css doc/manual.css @$(call E, pandoc: $@) @@ -95,13 +78,6 @@ doc/rustpkg.html: rustpkg.md doc/version_info.html doc/rust.css doc/manual.css --include-before-body=doc/version_info.html \ --output=$@ -###################################################################### -# Node (tutorial related) -###################################################################### - ifeq ($(CFG_NODE),) - $(info cfg: no node found, omitting doc/tutorial.html) - else - DOCS += doc/tutorial.html doc/tutorial.html: tutorial.md doc/version_info.html doc/rust.css @$(call E, pandoc: $@) @@ -153,9 +129,28 @@ doc/tutorial-tasks.html: tutorial-tasks.md doc/version_info.html doc/rust.css --include-before-body=doc/version_info.html \ --output=$@ - endif -endif + ifeq ($(CFG_PDFLATEX),) + $(info cfg: no pdflatex found, omitting doc/rust.pdf) + else + ifeq ($(CFG_XETEX),) + $(info cfg: no xetex found, disabling doc/rust.pdf) + else + ifeq ($(CFG_LUATEX),) + $(info cfg: lacking luatex, disabling pdflatex) + else +doc/rust.pdf: doc/rust.tex + @$(call E, pdflatex: $@) + $(Q)$(CFG_PDFLATEX) \ + -interaction=batchmode \ + -output-directory=doc \ + $< + + endif + endif + endif + +endif # No pandoc / node ###################################################################### # LLnextgen (grammar analysis from refman) From 1962803854bcf7245013f8a1377c0ade845b5c54 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Wed, 24 Apr 2013 12:11:33 -0700 Subject: [PATCH 33/40] core: Warning police --- src/libcore/condition.rs | 2 ++ src/libcore/rt/io/file.rs | 2 +- src/libcore/rt/io/net/tcp.rs | 3 --- src/libcore/rt/io/net/udp.rs | 1 - src/libcore/rt/io/net/unix.rs | 1 - src/libcore/rt/local_services.rs | 2 +- src/libcore/rt/test.rs | 2 -- src/libcore/rt/uvio.rs | 3 +-- src/libcore/task/local_data.rs | 1 - 9 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/libcore/condition.rs b/src/libcore/condition.rs index c639239981c..1240fe03dd5 100644 --- a/src/libcore/condition.rs +++ b/src/libcore/condition.rs @@ -206,10 +206,12 @@ mod test { fn test_conditions_are_public() { let mut trapped = false; do sadness::cond.trap(|_| { + trapped = true; 0 }).in { sadness::cond.raise(0); } + assert!(trapped); } } } diff --git a/src/libcore/rt/io/file.rs b/src/libcore/rt/io/file.rs index e4fe066a173..85dc180452f 100644 --- a/src/libcore/rt/io/file.rs +++ b/src/libcore/rt/io/file.rs @@ -11,7 +11,7 @@ use prelude::*; use super::support::PathLike; use super::{Reader, Writer, Seek, Close}; -use super::{IoError, SeekStyle}; +use super::SeekStyle; /// # XXX /// * Ugh, this is ridiculous. What is the best way to represent these options? diff --git a/src/libcore/rt/io/net/tcp.rs b/src/libcore/rt/io/net/tcp.rs index d726bae821c..c95b4344fe7 100644 --- a/src/libcore/rt/io/net/tcp.rs +++ b/src/libcore/rt/io/net/tcp.rs @@ -9,7 +9,6 @@ // except according to those terms. use prelude::*; -use super::*; use super::super::*; use super::ip::IpAddr; @@ -51,8 +50,6 @@ impl Listener for TcpListener { #[cfg(test)] mod test { - use super::*; - use rt::test::*; #[test] #[ignore] fn smoke_test() { diff --git a/src/libcore/rt/io/net/udp.rs b/src/libcore/rt/io/net/udp.rs index 8691a697e88..0cb2978fb1a 100644 --- a/src/libcore/rt/io/net/udp.rs +++ b/src/libcore/rt/io/net/udp.rs @@ -9,7 +9,6 @@ // except according to those terms. use prelude::*; -use super::*; use super::super::*; use super::ip::IpAddr; diff --git a/src/libcore/rt/io/net/unix.rs b/src/libcore/rt/io/net/unix.rs index bb3db6ec0d5..262816beecc 100644 --- a/src/libcore/rt/io/net/unix.rs +++ b/src/libcore/rt/io/net/unix.rs @@ -9,7 +9,6 @@ // except according to those terms. use prelude::*; -use super::*; use super::super::*; use super::super::support::PathLike; diff --git a/src/libcore/rt/local_services.rs b/src/libcore/rt/local_services.rs index 8a6277c2094..a03bc6c409f 100644 --- a/src/libcore/rt/local_services.rs +++ b/src/libcore/rt/local_services.rs @@ -21,7 +21,7 @@ use prelude::*; use libc::{c_void, uintptr_t}; use cast::transmute; -use super::sched::{Task, local_sched}; +use super::sched::local_sched; use super::local_heap::LocalHeap; pub struct LocalServices { diff --git a/src/libcore/rt/test.rs b/src/libcore/rt/test.rs index f7ba881f84e..63db7054088 100644 --- a/src/libcore/rt/test.rs +++ b/src/libcore/rt/test.rs @@ -35,7 +35,6 @@ pub fn run_in_newsched_task(f: ~fn()) { /// Test tasks will abort on failure instead of unwinding pub fn spawntask(f: ~fn()) { - use super::*; use super::sched::*; let mut sched = local_sched::take(); @@ -51,7 +50,6 @@ pub fn spawntask(f: ~fn()) { /// Create a new task and run it right now. Aborts on failure pub fn spawntask_immediately(f: ~fn()) { - use super::*; use super::sched::*; let mut sched = local_sched::take(); diff --git a/src/libcore/rt/uvio.rs b/src/libcore/rt/uvio.rs index 984ae55399c..94f8c0bf707 100644 --- a/src/libcore/rt/uvio.rs +++ b/src/libcore/rt/uvio.rs @@ -11,7 +11,7 @@ use option::*; use result::*; -use super::io::net::ip::{IpAddr, Ipv4}; // n.b. Ipv4 is used only in tests +use super::io::net::ip::IpAddr; use super::uv::*; use super::rtio::*; use ops::Drop; @@ -21,7 +21,6 @@ use super::sched::{Scheduler, local_sched}; #[cfg(test)] use uint; #[cfg(test)] use unstable::run_in_bare_thread; -#[cfg(test)] use super::sched::Task; #[cfg(test)] use super::test::*; pub struct UvEventLoop { diff --git a/src/libcore/task/local_data.rs b/src/libcore/task/local_data.rs index 65e68931915..dff5908c047 100644 --- a/src/libcore/task/local_data.rs +++ b/src/libcore/task/local_data.rs @@ -28,7 +28,6 @@ magic. use prelude::*; use task::local_data_priv::{local_get, local_pop, local_modify, local_set, Handle}; -use task::rt; /** * Indexes a task-local data slot. The function's code pointer is used for From 08659f5acc42e9ed73c85359a2b1f02e5484dbb0 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Wed, 24 Apr 2013 12:11:55 -0700 Subject: [PATCH 34/40] core: Turn off rtdebug logging Accidentally left in on at some point --- src/libcore/macros.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs index e1276a75e05..b19a753b715 100644 --- a/src/libcore/macros.rs +++ b/src/libcore/macros.rs @@ -11,7 +11,7 @@ #[macro_escape]; // Some basic logging -macro_rules! rtdebug ( +macro_rules! rtdebug_ ( ($( $arg:expr),+) => ( { dumb_println(fmt!( $($arg),+ )); @@ -26,7 +26,7 @@ macro_rules! rtdebug ( ) // An alternate version with no output, for turning off logging -macro_rules! rtdebug_ ( +macro_rules! rtdebug ( ($( $arg:expr),+) => ( $(let _ = $arg)*; ) ) From 436657b5f04a6d7504cfc00224c26910569c67eb Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Wed, 24 Apr 2013 15:34:02 -0700 Subject: [PATCH 35/40] Rename cleanup_task_local_map_ to cleanup_task_local_map_extern_cb Per pcwalton's suggestion. --- src/libcore/task/local_data_priv.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcore/task/local_data_priv.rs b/src/libcore/task/local_data_priv.rs index e32d18719bd..fdf1fa2a532 100644 --- a/src/libcore/task/local_data_priv.rs +++ b/src/libcore/task/local_data_priv.rs @@ -83,7 +83,7 @@ unsafe fn get_local_map(handle: Handle) -> TaskLocalMap { unsafe fn get_task_local_map(task: *rust_task) -> TaskLocalMap { - extern fn cleanup_task_local_map_(map_ptr: *libc::c_void) { + extern fn cleanup_task_local_map_extern_cb(map_ptr: *libc::c_void) { cleanup_task_local_map(map_ptr); } @@ -97,7 +97,7 @@ unsafe fn get_task_local_map(task: *rust_task) -> TaskLocalMap { // Use reinterpret_cast -- transmute would take map away from us also. rt::rust_set_task_local_data( task, cast::transmute(map)); - rt::rust_task_local_data_atexit(task, cleanup_task_local_map_); + rt::rust_task_local_data_atexit(task, cleanup_task_local_map_extern_cb); // Also need to reference it an extra time to keep it for now. let nonmut = cast::transmute::]>(map); From abc49fdfae0b80acfa010fd6151ff8ffc229c03b Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Wed, 24 Apr 2013 21:33:03 -0700 Subject: [PATCH 36/40] rt: abort doesn't take an argument --- src/rt/rust_builtin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index 2097d8dd933..8b7b89680fc 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -909,7 +909,7 @@ rust_begin_unwind(uintptr_t token) { #ifndef __WIN32__ throw token; #else - abort("failing on win32"); + abort(); #endif } From 6818e241b49c03c0fe0994dbe8340e2d8f482f09 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 29 Apr 2013 16:44:21 -0700 Subject: [PATCH 37/40] core: Turn off the local heap in newsched in stage0 to work around windows bustage core won't compile in stage0 without. --- src/libcore/unstable/lang.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/libcore/unstable/lang.rs b/src/libcore/unstable/lang.rs index bf3bf5adc0a..cb3f399f591 100644 --- a/src/libcore/unstable/lang.rs +++ b/src/libcore/unstable/lang.rs @@ -90,6 +90,14 @@ pub unsafe fn exchange_free(ptr: *c_char) { #[lang="malloc"] #[inline(always)] +#[cfg(stage0)] // For some reason this isn't working on windows in stage0 +pub unsafe fn local_malloc(td: *c_char, size: uintptr_t) -> *c_char { + return rustrt::rust_upcall_malloc_noswitch(td, size); +} + +#[lang="malloc"] +#[inline(always)] +#[cfg(not(stage0))] pub unsafe fn local_malloc(td: *c_char, size: uintptr_t) -> *c_char { match context() { OldTaskContext => { @@ -110,6 +118,17 @@ pub unsafe fn local_malloc(td: *c_char, size: uintptr_t) -> *c_char { // problem occurs, call exit instead. #[lang="free"] #[inline(always)] +#[cfg(stage0)] +pub unsafe fn local_free(ptr: *c_char) { + rustrt::rust_upcall_free_noswitch(ptr); +} + +// NB: Calls to free CANNOT be allowed to fail, as throwing an exception from +// inside a landing pad may corrupt the state of the exception handler. If a +// problem occurs, call exit instead. +#[lang="free"] +#[inline(always)] +#[cfg(not(stage0))] pub unsafe fn local_free(ptr: *c_char) { match context() { OldTaskContext => { From 9847428acf91e313c9c742fc38c69546bcfc8b26 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Tue, 30 Apr 2013 17:45:08 -0700 Subject: [PATCH 38/40] mk: Fix pdf build --- mk/docs.mk | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mk/docs.mk b/mk/docs.mk index 252f62cf871..f49c75d6acb 100644 --- a/mk/docs.mk +++ b/mk/docs.mk @@ -53,7 +53,7 @@ doc/rust.html: rust.md doc/version_info.html doc/rust.css doc/manual.css --include-before-body=doc/version_info.html \ --output=$@ -DOCS += doc/rust.pdf +DOCS += doc/rust.tex doc/rust.tex: rust.md doc/version.md @$(call E, pandoc: $@) $(Q)$(CFG_NODE) $(S)doc/prep.js $< | \ @@ -139,6 +139,7 @@ doc/tutorial-tasks.html: tutorial-tasks.md doc/version_info.html doc/rust.css $(info cfg: lacking luatex, disabling pdflatex) else +DOCS += doc/rust.pdf doc/rust.pdf: doc/rust.tex @$(call E, pdflatex: $@) $(Q)$(CFG_PDFLATEX) \ From db6a62c537852a30f030f866598c358d01fb95cd Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 2 May 2013 18:42:07 -0700 Subject: [PATCH 39/40] rustc: Drop the visitor object from the visitor glue Recent demoding makes the visitor glue leak. It hasn't shown up in tests because the box annihilator deletes the leaked boxes. This affects the new scheduler though which does not yet have a box annihilator. I don't think there's any great way to test this besides setting up a task that doesn't run the box annihilator and I don't know that that's a capability we want tasks to have. --- src/librustc/middle/trans/glue.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/librustc/middle/trans/glue.rs b/src/librustc/middle/trans/glue.rs index 4c5a17056b2..4025835495b 100644 --- a/src/librustc/middle/trans/glue.rs +++ b/src/librustc/middle/trans/glue.rs @@ -394,10 +394,15 @@ pub fn call_tydesc_glue(cx: block, v: ValueRef, t: ty::t, field: uint) pub fn make_visit_glue(bcx: block, v: ValueRef, t: ty::t) { let _icx = bcx.insn_ctxt("make_visit_glue"); - let mut bcx = bcx; - let (visitor_trait, object_ty) = ty::visitor_object_ty(bcx.tcx()); - let v = PointerCast(bcx, v, T_ptr(type_of::type_of(bcx.ccx(), object_ty))); - bcx = reflect::emit_calls_to_trait_visit_ty(bcx, t, v, visitor_trait.def_id); + let bcx = do with_scope(bcx, None, ~"visitor cleanup") |bcx| { + let mut bcx = bcx; + let (visitor_trait, object_ty) = ty::visitor_object_ty(bcx.tcx()); + let v = PointerCast(bcx, v, T_ptr(type_of::type_of(bcx.ccx(), object_ty))); + bcx = reflect::emit_calls_to_trait_visit_ty(bcx, t, v, visitor_trait.def_id); + // The visitor is a boxed object and needs to be dropped + add_clean(bcx, v, object_ty); + bcx + }; build_return(bcx); } From f8dffc6789113a10c9dbf1d815c3569b19b53e96 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 2 May 2013 19:13:56 -0700 Subject: [PATCH 40/40] core: Wire up the unwinder to newsched again This was some merge fallout --- src/libcore/sys.rs | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/libcore/sys.rs b/src/libcore/sys.rs index 215fc95f8c7..4a8d9fe27d9 100644 --- a/src/libcore/sys.rs +++ b/src/libcore/sys.rs @@ -204,22 +204,32 @@ impl FailWithCause for &'static str { #[cfg(stage0)] pub fn begin_unwind(msg: ~str, file: ~str, line: uint) -> ! { + do str::as_buf(msg) |msg_buf, _msg_len| { + do str::as_buf(file) |file_buf, _file_len| { + unsafe { + let msg_buf = cast::transmute(msg_buf); + let file_buf = cast::transmute(file_buf); + begin_unwind_(msg_buf, file_buf, line as libc::size_t) + } + } + } +} + +// FIXME #4427: Temporary until rt::rt_fail_ goes away +pub fn begin_unwind_(msg: *c_char, file: *c_char, line: size_t) -> ! { use rt::{context, OldTaskContext}; use rt::local_services::unsafe_borrow_local_services; match context() { OldTaskContext => { - do str::as_buf(msg) |msg_buf, _msg_len| { - do str::as_buf(file) |file_buf, _file_len| { - unsafe { - let msg_buf = cast::transmute(msg_buf); - let file_buf = cast::transmute(file_buf); - begin_unwind_(msg_buf, file_buf, line as libc::size_t) - } - } + unsafe { + gc::cleanup_stack_for_failure(); + rustrt::rust_upcall_fail(msg, file, line); + cast::transmute(()) } } _ => { + // XXX: Need to print the failure message gc::cleanup_stack_for_failure(); unsafe { let local_services = unsafe_borrow_local_services(); @@ -232,15 +242,6 @@ pub fn begin_unwind(msg: ~str, file: ~str, line: uint) -> ! { } } -// FIXME #4427: Temporary until rt::rt_fail_ goes away -pub fn begin_unwind_(msg: *c_char, file: *c_char, line: size_t) -> ! { - unsafe { - gc::cleanup_stack_for_failure(); - rustrt::rust_upcall_fail(msg, file, line); - cast::transmute(()) - } -} - // NOTE: remove function after snapshot #[cfg(stage0)] pub fn fail_assert(msg: &str, file: &str, line: uint) -> ! {