From e47ee779cd412ad629ab0f3db1babcc502862924 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 19 Apr 2013 15:18:38 -0700 Subject: [PATCH 01/50] 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 e041183b58452..4e3e01a5eceb9 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 b035532144c44..7ab27cdd7ec55 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 35eabe21b2a6b..de886949e7b0d 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/50] 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 7ab27cdd7ec55..c04389e029060 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/50] 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 64966bd345489..7938d65acd21d 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 5a556ed2107df..f84a73a394b6c 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/50] 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 e93e0c6fc6cc9..70bc2c7e063fc 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 0000000000000..14d69c2373253 --- /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 a6c03638713ed..a243bfba85c6f 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/50] 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 14d69c2373253..c2ad7d37d42ad 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 0dc1a4d86cbc9..54823d4dbcf61 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 ff5397398354a..a9aa0333b1631 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 7938d65acd21d..d82c39d6838ec 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/50] 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 a9aa0333b1631..c615f068a6955 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/50] 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 70bc2c7e063fc..cf896dd18f34c 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/50] 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 c2ad7d37d42ad..dfc058d6eba5c 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 c615f068a6955..d3953b7a797ab 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/50] 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 d3953b7a797ab..2582ddf310510 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/50] 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 dfc058d6eba5c..585d43a3ade8b 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 54823d4dbcf61..860c988b9c990 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 2582ddf310510..92439a41b342b 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/50] 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 585d43a3ade8b..e394a873feac6 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/50] 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 e3f71dca8c827..d78241b8e446e 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 f76bb58a45eb9..81a6677c14afa 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 de886949e7b0d..a5f4f8e3ba8b4 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/50] 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 8b18cc3c6968c..96f88e6368080 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/50] 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 92439a41b342b..2e9d0afa52fe5 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/50] 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 600968a3c7105..06e1466831df0 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; } } + + 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()); - fn eof(&mut self) -> bool { fail!() } + 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 c04389e029060..238bd97a62d17 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/50] 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 0000000000000..8e386f80b77d7 --- /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 cf896dd18f34c..e0190418b132b 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 28946281628b1..30136e443ee39 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/50] 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 d159df03dc3c0..e8ddb53148c1a 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 4097b6d41b756..178772007e518 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 @@ class boxed_region { 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 6de9d5a1df4a2..f3406712cb012 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 999a992eefaea..4ad57c11809cc 100644 --- a/src/rt/memory_region.h +++ b/src/rt/memory_region.h @@ -54,11 +54,11 @@ class memory_region { 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 @@ class memory_region { 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 90393acdd59d6..e7ce4b08ee46a 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 63dc1c9833e21..4680f32c19a84 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/50] 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 0000000000000..fbd4a77d79b98 --- /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 8e386f80b77d7..b9d99283e14f2 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 e0190418b132b..4a767d61f7444 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 be776a39742f0..0bf1ad36a1faf 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 e8ddb53148c1a..a49b52bffe153 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 a0db6f64f69fc..b8749b8f73d81 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 f84a73a394b6c..9aa8015678360 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/50] 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 b9d99283e14f2..d29e57a17af15 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 30136e443ee39..b7d861b8946b8 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 261671f6de9ae..6e919a74ed4f2 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 43f5fa4654bc7..50e8286e738d2 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 118c4cc23125b..1e3857dff9a95 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/50] 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 4e3e01a5eceb9..e4fe066a173f4 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 d78241b8e446e..d726bae821c87 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 81a6677c14afa..8691a697e8888 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 a5f4f8e3ba8b4..bb3db6ec0d502 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/50] 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 238bd97a62d17..f1b248c6e1d71 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 130ff6b38fa82..0000000000000 --- 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/50] 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 dc6c80228dd74..c639239981cab 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 430402a8982fc..5c5817d480dc4 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/50] 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 f1b248c6e1d71..d9d6622277f77 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 0000000000000..95f8711cb5bd5 --- /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/50] 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 d9d6622277f77..131743305bcce 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 d29e57a17af15..fc75a25642861 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 b7d861b8946b8..65456c30fee79 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 e394a873feac6..f3d73c91bd604 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 04f96f5eb229e..c50bc03517fa4 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 a243bfba85c6f..e1f4805a69208 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 b8749b8f73d81..b37644460aa9e 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 9f39e1433fc63..34236c36c1466 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 9aa8015678360..5e9a4b343eeda 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/50] 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 015992abf7821..538d1e2534fae 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/50] 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 32757d6376e73..4c4b2e03d3daa 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 860c988b9c990..04b9008b06770 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 2e9d0afa52fe5..e7b2880b74b50 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 640a69743ba6a..0f75ebb6090a1 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/50] 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 dd2e6a95861bd..33077872c1696 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/50] 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 e7a5cfbaf4b25..a3b2cb4aaf959 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 0000000000000..e1276a75e0557 --- /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 fc75a25642861..d09d082c8581c 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 4a767d61f7444..ab89a4c26a54d 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 65456c30fee79..f7b9bd8266844 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 f3d73c91bd604..f7ba881f84edb 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::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::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); 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 e7b2880b74b50..4cceb048cbc75 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 c50bc03517fa4..2c3670ad498e2 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 e1f4805a69208..d31a511eca8c9 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/50] 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 66808bf21aa2b..e32d18719bd00 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/50] core::rt: Add more I/O docs --- src/libcore/rt/io/mod.rs | 173 ++++++++++++++++++++++++++++++++++----- src/libcore/rt/mod.rs | 2 + 2 files changed, 153 insertions(+), 22 deletions(-) diff --git a/src/libcore/rt/io/mod.rs b/src/libcore/rt/io/mod.rs index 131743305bcce..ced4ba0ee2309 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 - -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'? +* 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 + +# 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 ab89a4c26a54d..56ed7dc95b6df 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/50] 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 54f3276c31752..984ae55399ceb 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/50] 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 | 71 +++++++++++++++++++++++++----------------------------- 1 file changed, 33 insertions(+), 38 deletions(-) diff --git a/mk/docs.mk b/mk/docs.mk index 6873d433e951f..252f62cf87116 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=$@ + 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 +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/50] 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 c639239981cab..1240fe03dd54d 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 e4fe066a173f4..85dc180452ffc 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 d726bae821c87..c95b4344fe75d 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 8691a697e8888..0cb2978fb1a68 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 bb3db6ec0d502..262816beecc98 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 8a6277c209417..a03bc6c409f8b 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 f7ba881f84edb..63db705408800 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 984ae55399ceb..94f8c0bf707dd 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 65e6893191575..dff5908c04796 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/50] 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 e1276a75e0557..b19a753b71577 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/50] 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 e32d18719bd00..fdf1fa2a53273 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/50] 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 2097d8dd93335..8b7b89680fcca 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/50] 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 bf3bf5adc0ac5..cb3f399f5919a 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 c2b8f98917597dd719ad93b25dcf000aeaa6d5d0 Mon Sep 17 00:00:00 2001 From: Luqman Aden Date: Mon, 29 Apr 2013 19:48:28 -0700 Subject: [PATCH 38/50] librustc: Fix drop finalizer not running for newtype structs. --- src/librustc/middle/trans/base.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index efa10dfc2aa34..892a8da367c9f 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -2071,6 +2071,7 @@ pub fn trans_tuple_struct(ccx: @CrateContext, let bcx = copy_args_to_allocas(fcx, bcx, fn_args, raw_llargs, arg_tys); let repr = adt::represent_type(ccx, tup_ty); + adt::trans_start_init(bcx, repr, fcx.llretptr.get(), 0); for fields.eachi |i, field| { let lldestptr = adt::trans_field_ptr(bcx, From 9847428acf91e313c9c742fc38c69546bcfc8b26 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Tue, 30 Apr 2013 17:45:08 -0700 Subject: [PATCH 39/50] 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 252f62cf87116..f49c75d6acb01 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 c64471ab8624e256c745dfd5d5e89ea6aa9e8228 Mon Sep 17 00:00:00 2001 From: Daniel Ralston Date: Wed, 1 May 2013 01:59:36 -0700 Subject: [PATCH 40/50] Add trait object field types to back/abi.rs, and use them I've added trt_field_vtable, trt_field_box, and trt_field_tydesc, and inserted them in place of the "magic numbers" used to access trait object fields through GEPi(). --- src/librustc/back/abi.rs | 7 +++++++ src/librustc/middle/trans/glue.rs | 12 ++++++------ src/librustc/middle/trans/meth.rs | 26 +++++++++++++++----------- 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/librustc/back/abi.rs b/src/librustc/back/abi.rs index 70a029ede6f8d..6d963d7cfd2cb 100644 --- a/src/librustc/back/abi.rs +++ b/src/librustc/back/abi.rs @@ -57,6 +57,13 @@ pub static n_tydesc_fields: uint = 8u; pub static fn_field_code: uint = 0u; pub static fn_field_box: uint = 1u; +// The three fields of a trait object/trait instance: vtable, box, and type +// description. +pub static trt_field_vtable: uint = 0u; +pub static trt_field_box: uint = 1u; +// This field is only present in unique trait objects, so it comes last. +pub static trt_field_tydesc: uint = 2u; + pub static vec_elt_fill: uint = 0u; pub static vec_elt_alloc: uint = 1u; diff --git a/src/librustc/middle/trans/glue.rs b/src/librustc/middle/trans/glue.rs index 4c5a17056b2ea..f1888084c0b05 100644 --- a/src/librustc/middle/trans/glue.rs +++ b/src/librustc/middle/trans/glue.rs @@ -549,12 +549,12 @@ pub fn make_drop_glue(bcx: block, v0: ValueRef, t: ty::t) { closure::make_closure_glue(bcx, v0, t, drop_ty) } ty::ty_trait(_, _, ty::BoxTraitStore, _) => { - let llbox = Load(bcx, GEPi(bcx, v0, [0u, 1u])); + let llbox = Load(bcx, GEPi(bcx, v0, [0u, abi::trt_field_box])); decr_refcnt_maybe_free(bcx, llbox, ty::mk_opaque_box(ccx.tcx)) } ty::ty_trait(_, _, ty::UniqTraitStore, _) => { - let lluniquevalue = GEPi(bcx, v0, [0, 1]); - let lltydesc = Load(bcx, GEPi(bcx, v0, [0, 2])); + let lluniquevalue = GEPi(bcx, v0, [0, abi::trt_field_box]); + let lltydesc = Load(bcx, GEPi(bcx, v0, [0, abi::trt_field_tydesc])); call_tydesc_glue_full(bcx, lluniquevalue, lltydesc, abi::tydesc_field_free_glue, None); bcx @@ -613,13 +613,13 @@ pub fn make_take_glue(bcx: block, v: ValueRef, t: ty::t) { closure::make_closure_glue(bcx, v, t, take_ty) } ty::ty_trait(_, _, ty::BoxTraitStore, _) => { - let llbox = Load(bcx, GEPi(bcx, v, [0u, 1u])); + let llbox = Load(bcx, GEPi(bcx, v, [0u, abi::trt_field_box])); incr_refcnt_of_boxed(bcx, llbox); bcx } ty::ty_trait(_, _, ty::UniqTraitStore, _) => { - let llval = GEPi(bcx, v, [0, 1]); - let lltydesc = Load(bcx, GEPi(bcx, v, [0, 2])); + let llval = GEPi(bcx, v, [0, abi::trt_field_box]); + let lltydesc = Load(bcx, GEPi(bcx, v, [0, abi::trt_field_tydesc])); call_tydesc_glue_full(bcx, llval, lltydesc, abi::tydesc_field_take_glue, None); bcx diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs index 90f9f93be2b48..a90475d9ed085 100644 --- a/src/librustc/middle/trans/meth.rs +++ b/src/librustc/middle/trans/meth.rs @@ -637,14 +637,15 @@ pub fn trans_trait_callee_from_llval(bcx: block, val_str(bcx.ccx().tn, llpair)); let llvtable = Load(bcx, PointerCast(bcx, - GEPi(bcx, llpair, [0u, 0u]), + GEPi(bcx, llpair, + [0u, abi::trt_field_vtable]), T_ptr(T_ptr(T_vtable())))); // Load the box from the @Trait pair and GEP over the box header if // necessary: let mut llself; debug!("(translating trait callee) loading second index from pair"); - let llbox = Load(bcx, GEPi(bcx, llpair, [0u, 1u])); + let llbox = Load(bcx, GEPi(bcx, llpair, [0u, abi::trt_field_box])); // Munge `llself` appropriately for the type of `self` in the method. let self_mode; @@ -845,27 +846,30 @@ pub fn trans_trait_cast(bcx: block, match store { ty::RegionTraitStore(_) | ty::BoxTraitStore => { - let mut llboxdest = GEPi(bcx, lldest, [0u, 1u]); - // Just store the pointer into the pair. + let mut llboxdest = GEPi(bcx, lldest, [0u, abi::trt_field_box]); + // Just store the pointer into the pair. (Region/borrowed + // and boxed trait objects are represented as pairs, and + // have no type descriptor field.) llboxdest = PointerCast(bcx, llboxdest, T_ptr(type_of(bcx.ccx(), v_ty))); bcx = expr::trans_into(bcx, val, SaveIn(llboxdest)); } ty::UniqTraitStore => { - // Translate the uniquely-owned value into the second element of - // the triple. (The first element is the vtable.) - let mut llvaldest = GEPi(bcx, lldest, [0, 1]); + // Translate the uniquely-owned value in the + // triple. (Unique trait objects are represented as + // triples.) + let mut llvaldest = GEPi(bcx, lldest, [0, abi::trt_field_box]); llvaldest = PointerCast(bcx, llvaldest, T_ptr(type_of(bcx.ccx(), v_ty))); bcx = expr::trans_into(bcx, val, SaveIn(llvaldest)); - // Get the type descriptor of the wrapped value and store it into - // the third element of the triple as well. + // Get the type descriptor of the wrapped value and store + // it in the triple as well. let tydesc = get_tydesc(bcx.ccx(), v_ty); glue::lazily_emit_all_tydesc_glue(bcx.ccx(), tydesc); - let lltydescdest = GEPi(bcx, lldest, [0, 2]); + let lltydescdest = GEPi(bcx, lldest, [0, abi::trt_field_tydesc]); Store(bcx, tydesc.tydesc, lltydescdest); } } @@ -875,7 +879,7 @@ pub fn trans_trait_cast(bcx: block, let orig = resolve_vtable_in_fn_ctxt(bcx.fcx, orig); let vtable = get_vtable(bcx.ccx(), orig); Store(bcx, vtable, PointerCast(bcx, - GEPi(bcx, lldest, [0u, 0u]), + GEPi(bcx, lldest, [0u, abi::trt_field_vtable]), T_ptr(val_ty(vtable)))); bcx From 6487cb221bcf7820a9d8458f18080c8f3ef1c2dc Mon Sep 17 00:00:00 2001 From: kud1ing Date: Thu, 2 May 2013 13:37:57 +0300 Subject: [PATCH 41/50] Explain that the source code was generated by this script --- src/etc/unicode.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/etc/unicode.py b/src/etc/unicode.py index 864cf3daee07e..afb3d16848085 100755 --- a/src/etc/unicode.py +++ b/src/etc/unicode.py @@ -235,6 +235,10 @@ def emit_decomp_module(f, canon, compat): rf = open(r, "w") (canon_decomp, compat_decomp, gencats) = load_unicode_data("UnicodeData.txt") + +# Explain that the source code was generated by this script. +rf.write('// The following code was generated by "src/etc/unicode.py"\n\n') + emit_property_module(rf, "general_category", gencats) #emit_decomp_module(rf, canon_decomp, compat_decomp) From 18c46b8fd4029bdcc2836298cca2df05b5c5f314 Mon Sep 17 00:00:00 2001 From: kud1ing Date: Thu, 2 May 2013 13:38:39 +0300 Subject: [PATCH 42/50] The following code was generated by "src/etc/unicode.py" --- src/libcore/unicode.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libcore/unicode.rs b/src/libcore/unicode.rs index a13d66c48ee0c..34175f9888ffa 100644 --- a/src/libcore/unicode.rs +++ b/src/libcore/unicode.rs @@ -10,6 +10,8 @@ #[doc(hidden)]; // FIXME #3538 +// The following code was generated by "src/etc/unicode.py" + pub mod general_category { fn bsearch_range_table(c: char, r: &'static [(char,char)]) -> bool { From 35b91e2f73cec610af4a2a653389562ad7cbc85e Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Fri, 3 May 2013 01:28:53 +0900 Subject: [PATCH 43/50] Use static strings --- src/librustc/middle/borrowck/loan.rs | 6 +- src/librustc/middle/borrowck/mod.rs | 4 +- src/librustc/middle/borrowck/preserve.rs | 2 +- src/librustc/middle/trans/_match.rs | 3 +- src/librustc/middle/trans/base.rs | 9 +- src/librustc/middle/trans/callee.rs | 4 +- src/librustc/middle/trans/consts.rs | 35 +++---- src/librustc/middle/trans/datum.rs | 4 +- src/librustc/middle/trans/debuginfo.rs | 23 ++--- src/librustc/middle/trans/expr.rs | 11 +-- src/librustc/middle/trans/foreign.rs | 2 +- src/librustc/middle/trans/monomorphize.rs | 2 +- src/librustc/middle/trans/reachable.rs | 10 +- src/librustc/middle/trans/tvec.rs | 4 +- src/librustc/middle/typeck/astconv.rs | 31 +++---- src/librustc/middle/typeck/check/_match.rs | 37 ++++---- src/librustc/middle/typeck/check/method.rs | 14 +-- src/librustc/middle/typeck/check/mod.rs | 92 +++++++++---------- src/librustc/middle/typeck/check/regionck.rs | 18 ++-- src/librustc/middle/typeck/check/vtable.rs | 2 +- src/librustc/middle/typeck/coherence.rs | 52 ++++------- src/librustc/middle/typeck/collect.rs | 5 +- .../middle/typeck/infer/region_inference.rs | 8 +- src/librustc/middle/typeck/mod.rs | 8 +- src/libsyntax/ext/base.rs | 2 +- src/libsyntax/parse/parser.rs | 69 +++++++------- 26 files changed, 201 insertions(+), 256 deletions(-) diff --git a/src/librustc/middle/borrowck/loan.rs b/src/librustc/middle/borrowck/loan.rs index 21de29b8f60ad..4dd727390aadc 100644 --- a/src/librustc/middle/borrowck/loan.rs +++ b/src/librustc/middle/borrowck/loan.rs @@ -115,7 +115,7 @@ pub impl LoanContext { if cmt.lp.is_none() { self.bccx.tcx.sess.span_bug( cmt.span, - ~"loan() called with non-lendable value"); + "loan() called with non-lendable value"); } match cmt.cat { @@ -123,7 +123,7 @@ pub impl LoanContext { // should never be loanable self.bccx.tcx.sess.span_bug( cmt.span, - ~"rvalue with a non-none lp"); + "rvalue with a non-none lp"); } cat_local(local_id) | cat_arg(local_id) | cat_self(local_id) => { // FIXME(#4903) @@ -188,7 +188,7 @@ pub impl LoanContext { // Aliased data is simply not lendable. self.bccx.tcx.sess.span_bug( cmt.span, - ~"aliased ptr with a non-none lp"); + "aliased ptr with a non-none lp"); } } } diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index 3746f9c6e60b1..f1d45e6e9aaa7 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -525,11 +525,11 @@ pub impl BorrowckCtxt { self.note_and_explain_bckerr(err); } - fn span_err(&self, s: span, m: ~str) { + fn span_err(&self, s: span, m: &str) { self.tcx.sess.span_err(s, m); } - fn span_note(&self, s: span, m: ~str) { + fn span_note(&self, s: span, m: &str) { self.tcx.sess.span_note(s, m); } diff --git a/src/librustc/middle/borrowck/preserve.rs b/src/librustc/middle/borrowck/preserve.rs index c44920fffa568..62358823f2322 100644 --- a/src/librustc/middle/borrowck/preserve.rs +++ b/src/librustc/middle/borrowck/preserve.rs @@ -124,7 +124,7 @@ pub impl<'self> PreserveCtxt<'self> { if self.root_managed_data { self.tcx().sess.span_bug( cmt.span, - ~"preserve() called with local and !root_managed_data"); + "preserve() called with local and !root_managed_data"); } let local_region = self.tcx().region_maps.encl_region(local_id); self.compare_scope(cmt, local_region) diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index 197b537fe9003..3bd2a8269b763 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -557,8 +557,7 @@ pub fn enter_opt<'r>(bcx: block, struct_id = found_struct_id; } _ => { - tcx.sess.span_bug(p.span, ~"expected enum \ - variant def"); + tcx.sess.span_bug(p.span, "expected enum variant def"); } } diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 0c3e3ba3cb672..87e347d200408 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -1125,10 +1125,11 @@ pub fn init_local(bcx: block, local: @ast::local) -> block { } let llptr = match bcx.fcx.lllocals.find(&local.node.id) { - Some(&local_mem(v)) => v, - _ => { bcx.tcx().sess.span_bug(local.span, - ~"init_local: Someone forgot to document why it's\ - safe to assume local.node.init must be local_mem!"); + Some(&local_mem(v)) => v, + _ => { + bcx.tcx().sess.span_bug(local.span, + "init_local: Someone forgot to document why it's\ + safe to assume local.node.init must be local_mem!"); } }; diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs index ad0fea3b4b4af..ed7f91bf04a6b 100644 --- a/src/librustc/middle/trans/callee.rs +++ b/src/librustc/middle/trans/callee.rs @@ -356,9 +356,7 @@ pub fn trans_method_call(in_cx: block, origin) } None => { - cx.tcx().sess.span_bug(call_ex.span, - ~"method call expr wasn't in \ - method map") + cx.tcx().sess.span_bug(call_ex.span, "method call expr wasn't in method map") } } }, diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index c6c5561854c0d..9d04905bcc978 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -58,8 +58,7 @@ pub fn const_lit(cx: @CrateContext, e: @ast::expr, lit: ast::lit) } _ => { cx.sess.span_bug(lit.span, - ~"floating point literal doesn't have the right \ - type"); + "floating point literal doesn't have the right type"); } } } @@ -281,7 +280,7 @@ fn const_expr_unadjusted(cx: @CrateContext, e: @ast::expr) -> ValueRef { else { llvm::LLVMConstURem(te1, te2) } } ast::and | - ast::or => cx.sess.span_unimpl(e.span, ~"binop logic"), + ast::or => cx.sess.span_unimpl(e.span, "binop logic"), ast::bitxor => llvm::LLVMConstXor(te1, te2), ast::bitand => llvm::LLVMConstAnd(te1, te2), ast::bitor => llvm::LLVMConstOr(te1, te2), @@ -295,7 +294,7 @@ fn const_expr_unadjusted(cx: @CrateContext, e: @ast::expr) -> ValueRef { ast::le | ast::ne | ast::ge | - ast::gt => cx.sess.span_unimpl(e.span, ~"binop comparator") + ast::gt => cx.sess.span_unimpl(e.span, "binop comparator") } } ast::expr_unary(u, e) => { @@ -344,8 +343,7 @@ fn const_expr_unadjusted(cx: @CrateContext, e: @ast::expr) -> ValueRef { const_eval::const_int(i) => i as u64, const_eval::const_uint(u) => u, _ => cx.sess.span_bug(index.span, - ~"index is not an integer-constant \ - expression") + "index is not an integer-constant expression") }; let (arr, len) = match ty::get(bt).sty { ty::ty_evec(_, vstore) | ty::ty_estr(vstore) => @@ -363,12 +361,10 @@ fn const_expr_unadjusted(cx: @CrateContext, e: @ast::expr) -> ValueRef { unit_sz)) }, _ => cx.sess.span_bug(base.span, - ~"index-expr base must be \ - fixed-size or slice") + "index-expr base must be fixed-size or slice") }, _ => cx.sess.span_bug(base.span, - ~"index-expr base must be \ - a vector or string type") + "index-expr base must be a vector or string type") }; let len = llvm::LLVMConstIntGetZExtValue(len) as u64; @@ -380,7 +376,7 @@ fn const_expr_unadjusted(cx: @CrateContext, e: @ast::expr) -> ValueRef { // FIXME #3170: report this earlier on in the const-eval // pass. Reporting here is a bit late. cx.sess.span_err(e.span, - ~"const index-expr is out of bounds"); + "const index-expr is out of bounds"); } const_get_elt(cx, arr, [iv as c_uint]) } @@ -454,8 +450,7 @@ fn const_expr_unadjusted(cx: @CrateContext, e: @ast::expr) -> ValueRef { match fs.find(|f| field_ty.ident == f.node.ident) { Some(ref f) => const_expr(cx, (*f).node.expr), None => { - cx.tcx.sess.span_bug( - e.span, ~"missing struct field"); + cx.tcx.sess.span_bug(e.span, "missing struct field"); } } }); @@ -471,8 +466,7 @@ fn const_expr_unadjusted(cx: @CrateContext, e: @ast::expr) -> ValueRef { ast::expr_lit(ref lit) => { match lit.node { ast::lit_str(*) => { const_expr(cx, sub) } - _ => { cx.sess.span_bug(e.span, - ~"bad const-slice lit") } + _ => { cx.sess.span_bug(e.span, "bad const-slice lit") } } } ast::expr_vec(ref es, ast::m_imm) => { @@ -487,8 +481,7 @@ fn const_expr_unadjusted(cx: @CrateContext, e: @ast::expr) -> ValueRef { let p = const_ptrcast(cx, gv, llunitty); C_struct(~[p, sz]) } - _ => cx.sess.span_bug(e.span, - ~"bad const-slice expr") + _ => cx.sess.span_bug(e.span, "bad const-slice expr") } } ast::expr_path(pth) => { @@ -520,8 +513,7 @@ fn const_expr_unadjusted(cx: @CrateContext, e: @ast::expr) -> ValueRef { C_null(llty) } _ => { - cx.sess.span_bug(e.span, ~"expected a const, fn, \ - struct, or variant def") + cx.sess.span_bug(e.span, "expected a const, fn, struct, or variant def") } } } @@ -542,13 +534,12 @@ fn const_expr_unadjusted(cx: @CrateContext, e: @ast::expr) -> ValueRef { adt::trans_const(cx, repr, vinfo.disr_val, args.map(|a| const_expr(cx, *a))) } - _ => cx.sess.span_bug(e.span, ~"expected a struct or \ - variant def") + _ => cx.sess.span_bug(e.span, "expected a struct or variant def") } } ast::expr_paren(e) => { return const_expr(cx, e); } _ => cx.sess.span_bug(e.span, - ~"bad constant expression type in consts::const_expr") + "bad constant expression type in consts::const_expr") }; } } diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs index fa27f652ac880..cf5fbb6e216ec 100644 --- a/src/librustc/middle/trans/datum.rs +++ b/src/librustc/middle/trans/datum.rs @@ -761,8 +761,8 @@ pub impl Datum { match self.try_deref(bcx, expr.id, derefs, false) { (Some(lvres), bcx) => DatumBlock { bcx: bcx, datum: lvres }, (None, _) => { - bcx.ccx().sess.span_bug( - expr.span, ~"Cannot deref this expression"); + bcx.ccx().sess.span_bug(expr.span, + "Cannot deref this expression"); } } } diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs index 58fc5ea3be647..b7a1a90741c43 100644 --- a/src/librustc/middle/trans/debuginfo.rs +++ b/src/librustc/middle/trans/debuginfo.rs @@ -756,7 +756,7 @@ fn create_ty(cx: @CrateContext, t: ty::t, span: span) } }, ty::ty_enum(_did, ref _substs) => { - cx.sess.span_bug(span, ~"debuginfo for enum NYI") + cx.sess.span_bug(span, "debuginfo for enum NYI") } ty::ty_box(ref mt) | ty::ty_uniq(ref mt) => { let boxed = create_ty(cx, mt.ty, span); @@ -782,7 +782,7 @@ fn create_ty(cx: @CrateContext, t: ty::t, span: span) create_pointer_type(cx, t, span, pointee) }, ty::ty_rptr(ref _region, ref _mt) => { - cx.sess.span_bug(span, ~"debuginfo for rptr NYI") + cx.sess.span_bug(span, "debuginfo for rptr NYI") }, ty::ty_bare_fn(ref barefnty) => { let inputs = do barefnty.sig.inputs.map |a| { a.ty }; @@ -790,10 +790,10 @@ fn create_ty(cx: @CrateContext, t: ty::t, span: span) create_fn_ty(cx, t, inputs, output, span) }, ty::ty_closure(ref _closurety) => { - cx.sess.span_bug(span, ~"debuginfo for closure NYI") + cx.sess.span_bug(span, "debuginfo for closure NYI") }, ty::ty_trait(_did, ref _substs, ref _vstore, _) => { - cx.sess.span_bug(span, ~"debuginfo for trait NYI") + cx.sess.span_bug(span, "debuginfo for trait NYI") }, ty::ty_struct(did, ref substs) => { let fields = ty::struct_fields(cx.tcx, did, substs); @@ -860,14 +860,12 @@ pub fn create_local_var(bcx: block, local: @ast::local) let llptr = match bcx.fcx.lllocals.find(&local.node.id) { option::Some(&local_mem(v)) => v, option::Some(_) => { - bcx.tcx().sess.span_bug(local.span, ~"local is bound to \ - something weird"); + bcx.tcx().sess.span_bug(local.span, "local is bound to something weird"); } option::None => { match *bcx.fcx.lllocals.get(&local.node.pat.id) { local_imm(v) => v, - _ => bcx.tcx().sess.span_bug(local.span, ~"local is bound to \ - something weird") + _ => bcx.tcx().sess.span_bug(local.span, "local is bound to something weird") } } }; @@ -966,8 +964,7 @@ pub fn create_function(fcx: fn_ctxt) -> @Metadata { ast::item_fn(ref decl, _, _, _, _) => { (item.ident, decl.output, item.id) } - _ => fcx.ccx.sess.span_bug(item.span, ~"create_function: item \ - bound to non-function") + _ => fcx.ccx.sess.span_bug(item.span, "create_function: item bound to non-function") } } ast_map::node_method(method, _, _) => { @@ -979,12 +976,10 @@ pub fn create_function(fcx: fn_ctxt) -> @Metadata { ((dbg_cx.names)(~"fn"), decl.output, expr.id) } _ => fcx.ccx.sess.span_bug(expr.span, - ~"create_function: \ - expected an expr_fn_block here") + "create_function: expected an expr_fn_block here") } } - _ => fcx.ccx.sess.bug(~"create_function: unexpected \ - sort of node") + _ => fcx.ccx.sess.bug("create_function: unexpected sort of node") }; debug!("%?", ident); diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index ae510ae6d114f..7cdd7c3799ec4 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -690,7 +690,7 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr, } _ => { bcx.tcx().sess.span_bug(expr.span, - ~"expr_cast of non-trait"); + "expr_cast of non-trait"); } } } @@ -700,8 +700,7 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr, _ => { bcx.tcx().sess.span_bug( expr.span, - fmt!("trans_rvalue_dps_unadjusted reached \ - fall-through case: %?", + fmt!("trans_rvalue_dps_unadjusted reached fall-through case: %?", expr.node)); } } @@ -1202,7 +1201,7 @@ fn trans_rec_or_struct(bcx: block, } None => { tcx.sess.span_bug(field.span, - ~"Couldn't find field in struct type") + "Couldn't find field in struct type") } } }; @@ -1478,7 +1477,7 @@ fn trans_eager_binop(bcx: block, } else { if !ty::type_is_scalar(rhs_t) { bcx.tcx().sess.span_bug(binop_expr.span, - ~"non-scalar comparison"); + "non-scalar comparison"); } let cmpr = base::compare_scalar_types(bcx, lhs, rhs, rhs_t, op); bcx = cmpr.bcx; @@ -1486,7 +1485,7 @@ fn trans_eager_binop(bcx: block, } } _ => { - bcx.tcx().sess.span_bug(binop_expr.span, ~"unexpected binop"); + bcx.tcx().sess.span_bug(binop_expr.span, "unexpected binop"); } }; diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs index c45ba64c58470..0e2bf80cc5e84 100644 --- a/src/librustc/middle/trans/foreign.rs +++ b/src/librustc/middle/trans/foreign.rs @@ -1080,7 +1080,7 @@ pub fn trans_intrinsic(ccx: @CrateContext, _ => { // Could we make this an enum rather than a string? does it get // checked earlier? - ccx.sess.span_bug(item.span, ~"unknown intrinsic"); + ccx.sess.span_bug(item.span, "unknown intrinsic"); } } build_return(bcx); diff --git a/src/librustc/middle/trans/monomorphize.rs b/src/librustc/middle/trans/monomorphize.rs index aa49915d1759b..2640dab1c04fb 100644 --- a/src/librustc/middle/trans/monomorphize.rs +++ b/src/librustc/middle/trans/monomorphize.rs @@ -160,7 +160,7 @@ pub fn monomorphic_fn(ccx: @CrateContext, // causing an infinite expansion. if depth > 30 { ccx.sess.span_fatal( - span, ~"overly deep expansion of inlined function"); + span, "overly deep expansion of inlined function"); } ccx.monomorphizing.insert(fn_id, depth + 1); diff --git a/src/librustc/middle/trans/reachable.rs b/src/librustc/middle/trans/reachable.rs index 234473dd35b60..f301a7d297342 100644 --- a/src/librustc/middle/trans/reachable.rs +++ b/src/librustc/middle/trans/reachable.rs @@ -170,11 +170,13 @@ fn traverse_inline_body(cx: &ctx, body: &blk) { expr_path(_) => { match cx.tcx.def_map.find(&e.id) { Some(&d) => { - traverse_def_id(cx, def_id_of_def(d)); + traverse_def_id(cx, def_id_of_def(d)); } - None => cx.tcx.sess.span_bug(e.span, fmt!("Unbound node \ - id %? while traversing %s", e.id, - expr_to_str(e, cx.tcx.sess.intr()))) + None => cx.tcx.sess.span_bug( + e.span, + fmt!("Unbound node id %? while traversing %s", + e.id, + expr_to_str(e, cx.tcx.sess.intr()))) } } expr_field(_, _, _) => { diff --git a/src/librustc/middle/trans/tvec.rs b/src/librustc/middle/trans/tvec.rs index da1cdfa4db103..e8075c1f2ad1d 100644 --- a/src/librustc/middle/trans/tvec.rs +++ b/src/librustc/middle/trans/tvec.rs @@ -469,7 +469,7 @@ pub fn write_content(bcx: block, } _ => { bcx.tcx().sess.span_bug(content_expr.span, - ~"Unexpected evec content"); + "Unexpected evec content"); } } } @@ -503,7 +503,7 @@ pub fn elements_required(bcx: block, content_expr: @ast::expr) -> uint { ty::eval_repeat_count(bcx.tcx(), count_expr) } _ => bcx.tcx().sess.span_bug(content_expr.span, - ~"Unexpected evec content") + "Unexpected evec content") } } diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs index ffaa6d46d3379..7ef77646f5203 100644 --- a/src/librustc/middle/typeck/astconv.rs +++ b/src/librustc/middle/typeck/astconv.rs @@ -291,10 +291,8 @@ pub fn ast_ty_to_ty( ty::vstore_fixed(*) => { tcx.sess.span_err( path.span, - ~"@trait, ~trait or &trait \ - are the only supported \ - forms of casting-to-\ - trait"); + "@trait, ~trait or &trait are the only supported \ + forms of casting-to-trait"); ty::BoxTraitStore } }; @@ -321,7 +319,7 @@ pub fn ast_ty_to_ty( if path.types.len() > 0u { tcx.sess.span_err( path.span, - ~"type parameters are not allowed on this type"); + "type parameters are not allowed on this type"); } } @@ -329,7 +327,7 @@ pub fn ast_ty_to_ty( if path.rp.is_some() { tcx.sess.span_err( path.span, - ~"region parameters are not allowed on this type"); + "region parameters are not allowed on this type"); } } } @@ -339,9 +337,8 @@ pub fn ast_ty_to_ty( match tcx.ast_ty_to_ty_cache.find(&ast_ty.id) { Some(&ty::atttce_resolved(ty)) => return ty, Some(&ty::atttce_unresolved) => { - tcx.sess.span_fatal(ast_ty.span, ~"illegal recursive type; \ - insert an enum in the cycle, \ - if this is desired"); + tcx.sess.span_fatal(ast_ty.span, "illegal recursive type; \ + insert an enum in the cycle, if this is desired"); } None => { /* go on */ } } @@ -359,11 +356,9 @@ pub fn ast_ty_to_ty( |tmt| ty::mk_uniq(tcx, tmt)) } ast::ty_vec(ref mt) => { - tcx.sess.span_err(ast_ty.span, - ~"bare `[]` is not a type"); + tcx.sess.span_err(ast_ty.span, "bare `[]` is not a type"); // return /something/ so they can at least get more errors - ty::mk_evec(tcx, ast_mt_to_mt(self, rscope, mt), - ty::vstore_uniq) + ty::mk_evec(tcx, ast_mt_to_mt(self, rscope, mt), ty::vstore_uniq) } ast::ty_ptr(ref mt) => { ty::mk_ptr(tcx, ast_mt_to_mt(self, rscope, mt)) @@ -434,7 +429,7 @@ pub fn ast_ty_to_ty( } ast::ty_str => { tcx.sess.span_err(ast_ty.span, - ~"bare `str` is not a type"); + "bare `str` is not a type"); // return /something/ so they can at least get more errors ty::mk_estr(tcx, ty::vstore_uniq) } @@ -454,7 +449,7 @@ pub fn ast_ty_to_ty( } _ => { tcx.sess.span_fatal(ast_ty.span, - ~"found type name used as a variable"); + "found type name used as a variable"); } } } @@ -470,7 +465,7 @@ pub fn ast_ty_to_ty( ty::vstore_fixed(i as uint)), _ => { tcx.sess.span_fatal( - ast_ty.span, ~"expected constant expr for vector length"); + ast_ty.span, "expected constant expr for vector length"); } } } @@ -489,11 +484,11 @@ pub fn ast_ty_to_ty( // routine. self.tcx().sess.span_bug( ast_ty.span, - ~"found `ty_infer` in unexpected place"); + "found `ty_infer` in unexpected place"); } ast::ty_mac(_) => { tcx.sess.span_bug(ast_ty.span, - ~"found `ty_mac` in unexpected place"); + "found `ty_mac` in unexpected place"); } }; diff --git a/src/librustc/middle/typeck/check/_match.rs b/src/librustc/middle/typeck/check/_match.rs index 5ff7105712183..de384d02dc3bc 100644 --- a/src/librustc/middle/typeck/check/_match.rs +++ b/src/librustc/middle/typeck/check/_match.rs @@ -204,8 +204,7 @@ pub fn check_pat_variant(pcx: &pat_ctxt, pat: @ast::pat, path: @ast::Path, if arg_len > 0 { // N-ary variant. if arg_len != subpats_len { - let s = fmt!("this pattern has %u field%s, but the corresponding \ - %s has %u field%s", + let s = fmt!("this pattern has %u field%s, but the corresponding %s has %u field%s", subpats_len, if subpats_len == 1u { ~"" } else { ~"s" }, kind_name, @@ -223,13 +222,12 @@ pub fn check_pat_variant(pcx: &pat_ctxt, pat: @ast::pat, path: @ast::Path, } } } else if subpats_len > 0 { - tcx.sess.span_err - (pat.span, fmt!("this pattern has %u field%s, but the \ - corresponding %s has no fields", - subpats_len, - if subpats_len == 1u { ~"" } - else { ~"s" }, - kind_name)); + tcx.sess.span_err(pat.span, + fmt!("this pattern has %u field%s, but the corresponding %s has no \ + fields", + subpats_len, + if subpats_len == 1u { "" } else { "s" }, + kind_name)); error_happened = true; } @@ -319,20 +317,19 @@ pub fn check_struct_pat(pcx: &pat_ctxt, pat_id: ast::node_id, span: span, Some(&ast::def_struct(*)) | Some(&ast::def_variant(*)) => { let name = pprust::path_to_str(path, tcx.sess.intr()); tcx.sess.span_err(span, - fmt!("mismatched types: expected `%s` but \ - found `%s`", + fmt!("mismatched types: expected `%s` but found `%s`", fcx.infcx().ty_to_str(expected), name)); } _ => { - tcx.sess.span_bug(span, ~"resolve didn't write in class"); + tcx.sess.span_bug(span, "resolve didn't write in class"); } } // Forbid pattern-matching structs with destructors. if ty::has_dtor(tcx, class_id) { - tcx.sess.span_err(span, ~"deconstructing struct not allowed in \ - pattern (it has a destructor)"); + tcx.sess.span_err(span, "deconstructing struct not allowed in pattern \ + (it has a destructor)"); } check_struct_pat_fields(pcx, span, path, fields, class_fields, class_id, @@ -370,7 +367,7 @@ pub fn check_struct_like_enum_variant_pat(pcx: &pat_ctxt, name)); } _ => { - tcx.sess.span_bug(span, ~"resolve didn't write in variant"); + tcx.sess.span_bug(span, "resolve didn't write in variant"); } } } @@ -404,10 +401,9 @@ pub fn check_pat(pcx: &pat_ctxt, pat: @ast::pat, expected: ty::t) { { // no-op } else if !ty::type_is_numeric(b_ty) { - tcx.sess.span_err(pat.span, ~"non-numeric type used in range"); + tcx.sess.span_err(pat.span, "non-numeric type used in range"); } else if !valid_range_bounds(fcx.ccx, begin, end) { - tcx.sess.span_err(begin.span, ~"lower range bound must be less \ - than upper"); + tcx.sess.span_err(begin.span, "lower range bound must be less than upper"); } fcx.write_ty(pat.id, b_ty); } @@ -476,9 +472,8 @@ pub fn check_pat(pcx: &pat_ctxt, pat: @ast::pat, expected: ty::t) { } _ => { tcx.sess.span_err(pat.span, - fmt!("mismatched types: expected `%s` \ - but found struct", - fcx.infcx().ty_to_str(expected))); + fmt!("mismatched types: expected `%s` but found struct", + fcx.infcx().ty_to_str(expected))); error_happened = true; } } diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index fb5b53d9400fb..abaadceb053fc 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -872,7 +872,7 @@ pub impl<'self> LookupContext<'self> { if relevant_candidates.len() > 1 { self.tcx().sess.span_err( self.expr.span, - ~"multiple applicable methods in scope"); + "multiple applicable methods in scope"); for uint::range(0, relevant_candidates.len()) |idx| { self.report_candidate(idx, &relevant_candidates[idx].origin); } @@ -983,12 +983,12 @@ pub impl<'self> LookupContext<'self> { } else if num_method_tps == 0u { tcx.sess.span_err( self.expr.span, - ~"this method does not take type parameters"); + "this method does not take type parameters"); self.fcx.infcx().next_ty_vars(num_method_tps) } else if num_supplied_tps != num_method_tps { tcx.sess.span_err( self.expr.span, - ~"incorrect number of type \ + "incorrect number of type \ parameters given for this method"); self.fcx.infcx().next_ty_vars(num_method_tps) } else { @@ -1082,14 +1082,14 @@ pub impl<'self> LookupContext<'self> { if ty::type_has_self(method_fty) { self.tcx().sess.span_err( self.expr.span, - ~"cannot call a method whose type contains a \ - self-type through a boxed trait"); + "cannot call a method whose type contains a \ + self-type through a boxed trait"); } if candidate.method_ty.generics.has_type_params() { self.tcx().sess.span_err( self.expr.span, - ~"cannot call a generic method through a boxed trait"); + "cannot call a generic method through a boxed trait"); } } @@ -1109,7 +1109,7 @@ pub impl<'self> LookupContext<'self> { if bad { self.tcx().sess.span_err(self.expr.span, - ~"explicit call to destructor"); + "explicit call to destructor"); } } diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index d1c5ae18bc30b..69775771d9697 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -542,17 +542,15 @@ pub fn check_no_duplicate_fields(tcx: ty::ctxt, let (id, sp) = *p; let orig_sp = field_names.find(&id).map_consume(|x| *x); match orig_sp { - Some(orig_sp) => { - tcx.sess.span_err(sp, fmt!("Duplicate field \ - name %s in record type declaration", - *tcx.sess.str_of(id))); - tcx.sess.span_note(orig_sp, ~"First declaration of \ - this field occurred here"); - break; - } - None => { - field_names.insert(id, sp); - } + Some(orig_sp) => { + tcx.sess.span_err(sp, fmt!("Duplicate field name %s in record type declaration", + *tcx.sess.str_of(id))); + tcx.sess.span_note(orig_sp, "First declaration of this field occurred here"); + break; + } + None => { + field_names.insert(id, sp); + } } } } @@ -1252,8 +1250,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, ty::ty_rptr(_, mt) => formal_ty = mt.ty, ty::ty_err => (), _ => { - fcx.ccx.tcx.sess.span_bug(arg.span, - ~"no ref"); + fcx.ccx.tcx.sess.span_bug(arg.span, "no ref"); } } } @@ -1559,8 +1556,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, match ty::get(lhs_resolved_t).sty { ty::ty_bare_fn(_) | ty::ty_closure(_) => { tcx.sess.span_note( - ex.span, ~"did you forget the `do` keyword \ - for the call?"); + ex.span, "did you forget the `do` keyword for the call?"); } _ => () } @@ -1851,9 +1847,9 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, tcx.sess.span_err(span, fmt!("missing field%s: %s", if missing_fields.len() == 1 { - ~"" + "" } else { - ~"s" + "s" }, str::connect(missing_fields, ~", "))); } @@ -1901,7 +1897,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, } _ => { tcx.sess.span_bug(span, - ~"resolve didn't map this to a class"); + "resolve didn't map this to a class"); } } } else { @@ -1989,7 +1985,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, } _ => { tcx.sess.span_bug(span, - ~"resolve didn't map this to an enum"); + "resolve didn't map this to an enum"); } } } else { @@ -2201,7 +2197,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, } } _ => - tcx.sess.span_bug(expr.span, ~"vstore modifier on non-sequence") + tcx.sess.span_bug(expr.span, "vstore modifier on non-sequence") }; fcx.write_ty(ev.id, typ); fcx.write_ty(id, typ); @@ -2284,21 +2280,18 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, ty::ty_enum(*) => { tcx.sess.span_err( expr.span, - ~"can only dereference enums \ - with a single variant which has a \ - single argument"); + "can only dereference enums with a single variant which \ + has a single argument"); } ty::ty_struct(*) => { tcx.sess.span_err( expr.span, - ~"can only dereference structs with \ - one anonymous field"); + "can only dereference structs with one anonymous field"); } _ => { fcx.type_error_message(expr.span, |actual| { - fmt!("type %s cannot be \ - dereferenced", actual) + fmt!("type %s cannot be dereferenced", actual) }, oprnd_t, None); } } @@ -2394,7 +2387,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, result::Err(_) => { tcx.sess.span_err( expr.span, - ~"`return;` in function returning non-nil"); + "`return;` in function returning non-nil"); } }, Some(e) => { @@ -2758,8 +2751,8 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, variant_id, *fields); } _ => { - tcx.sess.span_bug(path.span, ~"structure constructor does \ - not name a structure type"); + tcx.sess.span_bug(path.span, + "structure constructor does not name a structure type"); } } } @@ -2961,7 +2954,7 @@ pub fn check_block_with_expected(fcx: @mut FnCtxt, } _ => false } { - fcx.ccx.tcx.sess.span_warn(s.span, ~"unreachable statement"); + fcx.ccx.tcx.sess.span_warn(s.span, "unreachable statement"); warned = true; } if ty::type_is_bot(s_ty) { @@ -2982,7 +2975,7 @@ pub fn check_block_with_expected(fcx: @mut FnCtxt, }, Some(e) => { if any_bot && !warned { - fcx.ccx.tcx.sess.span_warn(e.span, ~"unreachable expression"); + fcx.ccx.tcx.sess.span_warn(e.span, "unreachable expression"); } check_expr_with_opt_hint(fcx, e, expected); let ety = fcx.expr_ty(e); @@ -3073,8 +3066,8 @@ pub fn check_enum_variants(ccx: @mut CrateCtxt, *disr_val = val as int; } Ok(_) => { - ccx.tcx.sess.span_err(e.span, ~"expected signed integer \ - constant"); + ccx.tcx.sess.span_err(e.span, "expected signed integer \ + constant"); } Err(ref err) => { ccx.tcx.sess.span_err(e.span, @@ -3085,7 +3078,7 @@ pub fn check_enum_variants(ccx: @mut CrateCtxt, } if vec::contains(*disr_vals, &*disr_val) { ccx.tcx.sess.span_err(v.span, - ~"discriminator value already exists"); + "discriminator value already exists"); } disr_vals.push(*disr_val); let ctor_ty = ty::node_id_to_type(ccx.tcx, v.node.id); @@ -3142,9 +3135,9 @@ pub fn check_enum_variants(ccx: @mut CrateCtxt, _ => false } }) { - ccx.tcx.sess.span_err(sp, ~"illegal recursive enum type; \ - wrap the inner value in a box to \ - make it representable"); + ccx.tcx.sess.span_err(sp, + "illegal recursive enum type; \ + wrap the inner value in a box to make it representable"); } // Check that it is possible to instantiate this enum: @@ -3205,26 +3198,25 @@ pub fn ty_param_bounds_and_ty_for_def(fcx: @mut FnCtxt, ast::def_ty(_) | ast::def_prim_ty(_) | ast::def_ty_param(*)=> { - fcx.ccx.tcx.sess.span_bug(sp, ~"expected value but found type"); + fcx.ccx.tcx.sess.span_bug(sp, "expected value but found type"); } ast::def_mod(*) | ast::def_foreign_mod(*) => { - fcx.ccx.tcx.sess.span_bug(sp, ~"expected value but found module"); + fcx.ccx.tcx.sess.span_bug(sp, "expected value but found module"); } ast::def_use(*) => { - fcx.ccx.tcx.sess.span_bug(sp, ~"expected value but found use"); + fcx.ccx.tcx.sess.span_bug(sp, "expected value but found use"); } ast::def_region(*) => { - fcx.ccx.tcx.sess.span_bug(sp, ~"expected value but found region"); + fcx.ccx.tcx.sess.span_bug(sp, "expected value but found region"); } ast::def_typaram_binder(*) => { - fcx.ccx.tcx.sess.span_bug(sp, ~"expected value but found type \ - parameter"); + fcx.ccx.tcx.sess.span_bug(sp, "expected value but found type parameter"); } ast::def_label(*) => { - fcx.ccx.tcx.sess.span_bug(sp, ~"expected value but found label"); + fcx.ccx.tcx.sess.span_bug(sp, "expected value but found label"); } ast::def_self_ty(*) => { - fcx.ccx.tcx.sess.span_bug(sp, ~"expected value but found self ty"); + fcx.ccx.tcx.sess.span_bug(sp, "expected value but found self ty"); } } } @@ -3253,7 +3245,7 @@ pub fn instantiate_path(fcx: @mut FnCtxt, match tpt.generics.region_param { None => { // ...but the type is not lifetime parameterized! fcx.ccx.tcx.sess.span_err - (span, ~"this item is not region-parameterized"); + (span, "this item is not region-parameterized"); None } Some(_) => { // ...and the type is lifetime parameterized, ok. @@ -3273,15 +3265,15 @@ pub fn instantiate_path(fcx: @mut FnCtxt, fcx.infcx().next_ty_vars(ty_param_count) } else if ty_param_count == 0 { fcx.ccx.tcx.sess.span_err - (span, ~"this item does not take type parameters"); + (span, "this item does not take type parameters"); fcx.infcx().next_ty_vars(ty_param_count) } else if ty_substs_len > ty_param_count { fcx.ccx.tcx.sess.span_err - (span, ~"too many type parameters provided for this item"); + (span, "too many type parameters provided for this item"); fcx.infcx().next_ty_vars(ty_param_count) } else if ty_substs_len < ty_param_count { fcx.ccx.tcx.sess.span_err - (span, ~"not enough type parameters provided for this item"); + (span, "not enough type parameters provided for this item"); fcx.infcx().next_ty_vars(ty_param_count) } else { pth.types.map(|aty| fcx.to_ty(*aty)) diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index cb2b854276d6f..3a38d0b1d7ba3 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -490,13 +490,13 @@ fn constrain_auto_ref(rcx: @mut Rcx, expr: @ast::expr) { // reporting an error would be the correct path. tcx.sess.span_err( expr.span, - ~"lifetime of borrowed pointer does not include \ - the expression being borrowed"); + "lifetime of borrowed pointer does not include \ + the expression being borrowed"); note_and_explain_region( tcx, - ~"lifetime of the borrowed pointer is", + "lifetime of the borrowed pointer is", region, - ~""); + ""); rcx.errors_reported += 1; } } @@ -522,17 +522,17 @@ fn constrain_free_variables( result::Err(_) => { tcx.sess.span_err( freevar.span, - ~"captured variable does not outlive the enclosing closure"); + "captured variable does not outlive the enclosing closure"); note_and_explain_region( tcx, - ~"captured variable is valid for ", + "captured variable is valid for ", en_region, - ~""); + ""); note_and_explain_region( tcx, - ~"closure is valid for ", + "closure is valid for ", region, - ~""); + ""); } } } diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs index 44b6212261246..532638faa68c7 100644 --- a/src/librustc/middle/typeck/check/vtable.rs +++ b/src/librustc/middle/typeck/check/vtable.rs @@ -414,7 +414,7 @@ fn lookup_vtable(vcx: &VtableContext, if !is_early { vcx.tcx().sess.span_err( location_info.span, - ~"multiple applicable methods in scope"); + "multiple applicable methods in scope"); } return Some(/*bad*/copy found[0]); } diff --git a/src/librustc/middle/typeck/coherence.rs b/src/librustc/middle/typeck/coherence.rs index 05b2f6f577b82..7fff756b01da5 100644 --- a/src/librustc/middle/typeck/coherence.rs +++ b/src/librustc/middle/typeck/coherence.rs @@ -76,10 +76,8 @@ pub fn get_base_type(inference_context: @mut InferCtxt, } _ => { inference_context.tcx.sess.span_fatal(span, - ~"the type of this value \ - must be known in order \ - to determine the base \ - type"); + "the type of this value must be known in order \ + to determine the base type"); } } @@ -257,9 +255,8 @@ pub impl CoherenceChecker { None => { let session = self.crate_context.tcx.sess; session.span_err(item.span, - ~"no base type found for inherent \ - implementation; implement a \ - trait or new type instead"); + "no base type found for inherent implementation; \ + implement a trait or new type instead"); } Some(_) => { // Nothing to do. @@ -483,11 +480,9 @@ pub impl CoherenceChecker { if self.polytypes_unify(polytype_a, polytype_b) { let session = self.crate_context.tcx.sess; session.span_err(self.span_of_impl(implementation_b), - ~"conflicting implementations for a \ - trait"); + "conflicting implementations for a trait"); session.span_note(self.span_of_impl(implementation_a), - ~"note conflicting implementation \ - here"); + "note conflicting implementation here"); } } } @@ -667,11 +662,9 @@ pub impl CoherenceChecker { // This is an error. let session = self.crate_context.tcx.sess; session.span_err(item.span, - ~"cannot associate methods with \ - a type outside the crate the \ - type is defined in; define \ - and implement a trait or new \ - type instead"); + "cannot associate methods with a type outside the \ + crate the type is defined in; define and implement \ + a trait or new type instead"); } } item_impl(_, Some(trait_ref), _, _) => { @@ -690,10 +683,8 @@ pub impl CoherenceChecker { if trait_def_id.crate != local_crate { let session = self.crate_context.tcx.sess; session.span_err(item.span, - ~"cannot provide an \ - extension implementation \ - for a trait not defined \ - in this crate"); + "cannot provide an extension implementation \ + for a trait not defined in this crate"); } } @@ -765,7 +756,7 @@ pub impl CoherenceChecker { None => { self.crate_context.tcx.sess.span_bug( original_type.span, - ~"resolve didn't resolve this type?!"); + "resolve didn't resolve this type?!"); } Some(&node_item(item, _)) => { match item.node { @@ -849,8 +840,7 @@ pub impl CoherenceChecker { } _ => { self.crate_context.tcx.sess.span_bug(item.span, - ~"can't convert a \ - non-impl to an impl"); + "can't convert a non-impl to an impl"); } } } @@ -862,9 +852,8 @@ pub impl CoherenceChecker { return item.span; } _ => { - self.crate_context.tcx.sess.bug(~"span_of_impl() called on \ - something that wasn't an \ - impl!"); + self.crate_context.tcx.sess.bug("span_of_impl() called on something that \ + wasn't an impl!"); } } } @@ -1045,17 +1034,16 @@ pub impl CoherenceChecker { match tcx.items.find(&impl_info.did.node) { Some(&ast_map::node_item(@ref item, _)) => { tcx.sess.span_err((*item).span, - ~"the Drop trait may only \ - be implemented on \ - structures"); + "the Drop trait may only be implemented on \ + structures"); } _ => { - tcx.sess.bug(~"didn't find impl in ast map"); + tcx.sess.bug("didn't find impl in ast map"); } } } else { - tcx.sess.bug(~"found external impl of Drop trait on \ - something other than a struct"); + tcx.sess.bug("found external impl of Drop trait on \ + something other than a struct"); } } } diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index 10537711b3289..45d61bb735728 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -151,7 +151,7 @@ impl AstConv for CrateCtxt { fn ty_infer(&self, span: span) -> ty::t { self.tcx.sess.span_bug(span, - ~"found `ty_infer` in unexpected place"); + "found `ty_infer` in unexpected place"); } } @@ -416,8 +416,7 @@ pub fn ensure_supertraits(ccx: &CrateCtxt, if ty_trait_refs.any(|other_trait| other_trait.def_id == trait_ref.def_id) { // This means a trait inherited from the same supertrait more // than once. - tcx.sess.span_err(sp, ~"Duplicate supertrait in trait \ - declaration"); + tcx.sess.span_err(sp, "Duplicate supertrait in trait declaration"); break; } else { ty_trait_refs.push(trait_ref); diff --git a/src/librustc/middle/typeck/infer/region_inference.rs b/src/librustc/middle/typeck/infer/region_inference.rs index e12a3f2e97522..25e65d7a18bca 100644 --- a/src/librustc/middle/typeck/infer/region_inference.rs +++ b/src/librustc/middle/typeck/infer/region_inference.rs @@ -1587,9 +1587,9 @@ pub impl RegionVarBindings { note_and_explain_region( self.tcx, - ~"first, the lifetime cannot outlive ", + "first, the lifetime cannot outlive ", upper_bound.region, - ~"..."); + "..."); self.tcx.sess.span_note( upper_bound.span, @@ -1597,9 +1597,9 @@ pub impl RegionVarBindings { note_and_explain_region( self.tcx, - ~"but, the lifetime must be valid for ", + "but, the lifetime must be valid for ", lower_bound.region, - ~"..."); + "..."); self.tcx.sess.span_note( lower_bound.span, diff --git a/src/librustc/middle/typeck/mod.rs b/src/librustc/middle/typeck/mod.rs index b0e0b7319ac84..0012eb700302a 100644 --- a/src/librustc/middle/typeck/mod.rs +++ b/src/librustc/middle/typeck/mod.rs @@ -214,7 +214,7 @@ pub fn lookup_def_tcx(tcx: ty::ctxt, sp: span, id: ast::node_id) -> ast::def { match tcx.def_map.find(&id) { Some(&x) => x, _ => { - tcx.sess.span_fatal(sp, ~"internal error looking up a definition") + tcx.sess.span_fatal(sp, "internal error looking up a definition") } } } @@ -301,8 +301,7 @@ fn check_main_fn_ty(ccx: @mut CrateCtxt, if ps.is_parameterized() => { tcx.sess.span_err( main_span, - ~"main function is not allowed \ - to have type parameters"); + "main function is not allowed to have type parameters"); return; } _ => () @@ -343,8 +342,7 @@ fn check_start_fn_ty(ccx: @mut CrateCtxt, if ps.is_parameterized() => { tcx.sess.span_err( start_span, - ~"start function is not allowed to have type \ - parameters"); + "start function is not allowed to have type parameters"); return; } _ => () diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 1c03ad1375919..db4912d213108 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -342,7 +342,7 @@ pub fn expr_to_str(cx: @ext_ctxt, expr: @ast::expr, err_msg: ~str) -> ~str { pub fn expr_to_ident(cx: @ext_ctxt, expr: @ast::expr, - err_msg: ~str) -> ast::ident { + err_msg: &str) -> ast::ident { match expr.node { ast::expr_path(p) => { if vec::len(p.types) > 0u || vec::len(p.idents) != 1u { diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 8459fc8c6273d..5ae101a567cc2 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -308,22 +308,22 @@ pub impl Parser { } return copy self.buffer[(*self.buffer_start + dist - 1) & 3].tok; } - fn fatal(&self, m: ~str) -> ! { + fn fatal(&self, m: &str) -> ! { self.sess.span_diagnostic.span_fatal(*copy self.span, m) } - fn span_fatal(&self, sp: span, m: ~str) -> ! { + fn span_fatal(&self, sp: span, m: &str) -> ! { self.sess.span_diagnostic.span_fatal(sp, m) } - fn span_note(&self, sp: span, m: ~str) { + fn span_note(&self, sp: span, m: &str) { self.sess.span_diagnostic.span_note(sp, m) } - fn bug(&self, m: ~str) -> ! { + fn bug(&self, m: &str) -> ! { self.sess.span_diagnostic.span_bug(*copy self.span, m) } - fn warn(&self, m: ~str) { + fn warn(&self, m: &str) { self.sess.span_diagnostic.span_warn(*copy self.span, m) } - fn span_err(&self, sp: span, m: ~str) { + fn span_err(&self, sp: span, m: &str) { self.sess.span_diagnostic.span_err(sp, m) } fn abort_if_errors(&self) { @@ -2029,8 +2029,7 @@ pub impl Parser { // This is a 'continue' expression if opt_ident.is_some() { self.span_err(*self.last_span, - ~"a label may not be used with a `loop` \ - expression"); + "a label may not be used with a `loop` expression"); } let lo = self.span.lo; @@ -2167,7 +2166,7 @@ pub impl Parser { @ast::pat { node: pat_wild, _ } => (), @ast::pat { node: pat_ident(_, _, _), _ } => (), @ast::pat { span, _ } => self.span_fatal( - span, ~"expected an identifier or `_`" + span, "expected an identifier or `_`" ) } slice = Some(subpat); @@ -2459,7 +2458,7 @@ pub impl Parser { -> ast::pat_ { if !is_plain_ident(&*self.token) { self.span_fatal(*self.last_span, - ~"expected identifier, found path"); + "expected identifier, found path"); } // why a path here, and not just an identifier? let name = self.parse_path_without_tps(); @@ -2478,7 +2477,7 @@ pub impl Parser { if *self.token == token::LPAREN { self.span_fatal( *self.last_span, - ~"expected identifier, found enum pattern"); + "expected identifier, found enum pattern"); } pat_ident(binding_mode, name, sub) @@ -2609,19 +2608,19 @@ pub impl Parser { match self.parse_item_or_view_item(/*bad*/ copy item_attrs, false) { - iovi_item(i) => { - let hi = i.span.hi; - let decl = @spanned(lo, hi, decl_item(i)); - return @spanned(lo, hi, stmt_decl(decl, self.get_id())); - } - iovi_view_item(vi) => { - self.span_fatal(vi.span, ~"view items must be declared at \ - the top of the block"); - } - iovi_foreign_item(_) => { - self.fatal(~"foreign items are not allowed here"); - } - iovi_none() => { /* fallthrough */ } + iovi_item(i) => { + let hi = i.span.hi; + let decl = @spanned(lo, hi, decl_item(i)); + return @spanned(lo, hi, stmt_decl(decl, self.get_id())); + } + iovi_view_item(vi) => { + self.span_fatal(vi.span, + "view items must be declared at the top of the block"); + } + iovi_foreign_item(_) => { + self.fatal(~"foreign items are not allowed here"); + } + iovi_none() => { /* fallthrough */ } } check_expected_item(self, item_attrs); @@ -2822,8 +2821,7 @@ pub impl Parser { result.push(RegionTyParamBound); } else { self.span_err(*self.span, - ~"`'static` is the only permissible \ - region bound here"); + "`'static` is the only permissible region bound here"); } self.bump(); } @@ -3238,7 +3236,7 @@ pub impl Parser { }) } _ => { - self.span_err(*self.span, ~"not a trait"); + self.span_err(*self.span, "not a trait"); None } }; @@ -3467,9 +3465,8 @@ pub impl Parser { ) { iovi_item(item) => items.push(item), iovi_view_item(view_item) => { - self.span_fatal(view_item.span, ~"view items must be \ - declared at the top of the \ - module"); + self.span_fatal(view_item.span, "view items must be declared at the top of the \ + module"); } _ => { self.fatal( @@ -3762,7 +3759,7 @@ pub impl Parser { } if opt_abis.is_some() { - self.span_err(*self.span, ~"an ABI may not be specified here"); + self.span_err(*self.span, "an ABI may not be specified here"); } // extern mod foo; @@ -4397,9 +4394,7 @@ pub impl Parser { view_item_extern_mod(*) if !extern_mod_allowed => { self.span_err(view_item.span, - ~"\"extern mod\" \ - declarations are not \ - allowed here"); + "\"extern mod\" declarations are not allowed here"); } view_item_extern_mod(*) => {} } @@ -4425,8 +4420,7 @@ pub impl Parser { iovi_none => break, iovi_view_item(view_item) => { self.span_err(view_item.span, - ~"`use` and `extern mod` declarations \ - must precede items"); + "`use` and `extern mod` declarations must precede items"); } iovi_item(item) => { items.push(item) @@ -4461,8 +4455,7 @@ pub impl Parser { iovi_view_item(view_item) => { // I think this can't occur: self.span_err(view_item.span, - ~"`use` and `extern mod` declarations \ - must precede items"); + "`use` and `extern mod` declarations must precede items"); } iovi_item(_) => { // FIXME #5668: this will occur for a macro invocation: From 7aa10e616bc3d4d43051c04a074d7e573fb72886 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Thu, 2 May 2013 12:46:58 -0400 Subject: [PATCH 44/50] make link_args use spaces as separators Lots of linking arguments need to be passed as -Wl,--foo so giving the comma meaning at the rustc layer makes those flags impossible to pass. Multiple arguments can now be passed from a shell by quoting the argument: --link-args='-lfoo -Wl,--as-needed'. --- src/librustc/driver/driver.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index afd0e0acfe910..5e5d0640d808e 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -653,7 +653,7 @@ pub fn build_session_options(binary: @~str, let linker_args = getopts::opt_strs(matches, ~"link-args").flat_map( |a| { let mut args = ~[]; - for str::each_split_char(*a, ',') |arg| { + for str::each_split_char(*a, ' ') |arg| { args.push(str::from_slice(arg)); } args @@ -760,7 +760,7 @@ pub fn optgroups() -> ~[getopts::groups::OptGroup] { optmulti("L", "", "Add a directory to the library search path", "PATH"), optflag("", "lib", "Compile a library crate"), - optmulti("", "link-args", "FLAGS is a comma-separated list of flags + optmulti("", "link-args", "FLAGS is a space-separated list of flags passed to the linker", "FLAGS"), optflag("", "ls", "List the symbols defined by a library crate"), optflag("", "no-trans", From 544ac620ba131d0f58eb48f631338c0ea64e0f10 Mon Sep 17 00:00:00 2001 From: gareth Date: Tue, 30 Apr 2013 23:00:07 +0100 Subject: [PATCH 45/50] Convert most of rust_run_program.cpp to rust (issue #2674). --- src/libcore/libc.rs | 131 ++++++++++++- src/libcore/os.rs | 2 +- src/libcore/run.rs | 353 +++++++++++++++++++++++++++++++----- src/rt/rust_run_program.cpp | 200 ++------------------ src/rt/rustrt.def.in | 4 +- 5 files changed, 454 insertions(+), 236 deletions(-) diff --git a/src/libcore/libc.rs b/src/libcore/libc.rs index 53aaf5726dd0b..59b06faf5a251 100644 --- a/src/libcore/libc.rs +++ b/src/libcore/libc.rs @@ -582,12 +582,16 @@ pub mod types { pub type LPWSTR = *mut WCHAR; pub type LPSTR = *mut CHAR; + pub type LPTSTR = *mut CHAR; // Not really, but opaque to us. pub type LPSECURITY_ATTRIBUTES = LPVOID; pub type LPVOID = *mut c_void; + pub type LPBYTE = *mut BYTE; pub type LPWORD = *mut WORD; + pub type LPDWORD = *mut DWORD; + pub type LPHANDLE = *mut HANDLE; pub type LRESULT = LONG_PTR; pub type PBOOL = *mut BOOL; @@ -596,6 +600,36 @@ pub mod types { pub type time64_t = i64; pub type int64 = i64; + + pub struct STARTUPINFO { + cb: DWORD, + lpReserved: LPTSTR, + lpDesktop: LPTSTR, + lpTitle: LPTSTR, + dwX: DWORD, + dwY: DWORD, + dwXSize: DWORD, + dwYSize: DWORD, + dwXCountChars: DWORD, + dwYCountCharts: DWORD, + dwFillAttribute: DWORD, + dwFlags: DWORD, + wShowWindow: WORD, + cbReserved2: WORD, + lpReserved2: LPBYTE, + hStdInput: HANDLE, + hStdOutput: HANDLE, + hStdError: HANDLE + } + pub type LPSTARTUPINFO = *mut STARTUPINFO; + + pub struct PROCESS_INFORMATION { + hProcess: HANDLE, + hThread: HANDLE, + dwProcessId: DWORD, + dwThreadId: DWORD + } + pub type LPPROCESS_INFORMATION = *mut PROCESS_INFORMATION; } } } @@ -848,6 +882,11 @@ pub mod consts { pub mod bsd44 { } pub mod extra { + use libc::types::os::arch::extra::{DWORD, BOOL}; + + pub static TRUE : BOOL = 1; + pub static FALSE : BOOL = 0; + pub static O_TEXT : int = 16384; pub static O_BINARY : int = 32768; pub static O_NOINHERIT: int = 128; @@ -855,6 +894,50 @@ pub mod consts { pub static ERROR_SUCCESS : int = 0; pub static ERROR_INSUFFICIENT_BUFFER : int = 122; pub static INVALID_HANDLE_VALUE: int = -1; + + pub static DELETE : DWORD = 0x00010000; + pub static READ_CONTROL : DWORD = 0x00020000; + pub static SYNCHRONIZE : DWORD = 0x00100000; + pub static WRITE_DAC : DWORD = 0x00040000; + pub static WRITE_OWNER : DWORD = 0x00080000; + + pub static PROCESS_CREATE_PROCESS : DWORD = 0x0080; + pub static PROCESS_CREATE_THREAD : DWORD = 0x0002; + pub static PROCESS_DUP_HANDLE : DWORD = 0x0040; + pub static PROCESS_QUERY_INFORMATION : DWORD = 0x0400; + pub static PROCESS_QUERY_LIMITED_INFORMATION : DWORD = 0x1000; + pub static PROCESS_SET_INFORMATION : DWORD = 0x0200; + pub static PROCESS_SET_QUOTA : DWORD = 0x0100; + pub static PROCESS_SUSPEND_RESUME : DWORD = 0x0800; + pub static PROCESS_TERMINATE : DWORD = 0x0001; + pub static PROCESS_VM_OPERATION : DWORD = 0x0008; + pub static PROCESS_VM_READ : DWORD = 0x0010; + pub static PROCESS_VM_WRITE : DWORD = 0x0020; + + pub static STARTF_FORCEONFEEDBACK : DWORD = 0x00000040; + pub static STARTF_FORCEOFFFEEDBACK : DWORD = 0x00000080; + pub static STARTF_PREVENTPINNING : DWORD = 0x00002000; + pub static STARTF_RUNFULLSCREEN : DWORD = 0x00000020; + pub static STARTF_TITLEISAPPID : DWORD = 0x00001000; + pub static STARTF_TITLEISLINKNAME : DWORD = 0x00000800; + pub static STARTF_USECOUNTCHARS : DWORD = 0x00000008; + pub static STARTF_USEFILLATTRIBUTE : DWORD = 0x00000010; + pub static STARTF_USEHOTKEY : DWORD = 0x00000200; + pub static STARTF_USEPOSITION : DWORD = 0x00000004; + pub static STARTF_USESHOWWINDOW : DWORD = 0x00000001; + pub static STARTF_USESIZE : DWORD = 0x00000002; + pub static STARTF_USESTDHANDLES : DWORD = 0x00000100; + + pub static WAIT_ABANDONED : DWORD = 0x00000080; + pub static WAIT_OBJECT_0 : DWORD = 0x00000000; + pub static WAIT_TIMEOUT : DWORD = 0x00000102; + pub static WAIT_FAILED : DWORD = -1; + + pub static DUPLICATE_CLOSE_SOURCE : DWORD = 0x00000001; + pub static DUPLICATE_SAME_ACCESS : DWORD = 0x00000002; + + pub static INFINITE : DWORD = -1; + pub static STILL_ACTIVE : DWORD = 259; } } @@ -1751,12 +1834,24 @@ pub mod funcs { unsafe fn sysctlnametomib(name: *c_char, mibp: *mut c_int, sizep: *mut size_t) -> c_int; + + unsafe fn getdtablesize() -> c_int; } } #[cfg(target_os = "linux")] #[cfg(target_os = "android")] + pub mod bsd44 { + use libc::types::os::arch::c95::{c_int}; + + #[abi = "cdecl"] + pub extern { + unsafe fn getdtablesize() -> c_int; + } + } + + #[cfg(target_os = "win32")] pub mod bsd44 { } @@ -1790,9 +1885,11 @@ pub mod funcs { pub mod kernel32 { use libc::types::os::arch::c95::{c_uint}; use libc::types::os::arch::extra::{BOOL, DWORD, HMODULE}; - use libc::types::os::arch::extra::{LPCWSTR, LPWSTR, LPTCH}; - use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES}; - use libc::types::os::arch::extra::{HANDLE}; + use libc::types::os::arch::extra::{LPCWSTR, LPWSTR, LPCTSTR, + LPTSTR, LPTCH, LPDWORD, LPVOID}; + use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, LPSTARTUPINFO, + LPPROCESS_INFORMATION}; + use libc::types::os::arch::extra::{HANDLE, LPHANDLE}; #[abi = "stdcall"] pub extern "stdcall" { @@ -1829,19 +1926,45 @@ pub mod funcs { findFileData: HANDLE) -> BOOL; unsafe fn FindClose(findFile: HANDLE) -> BOOL; + unsafe fn DuplicateHandle(hSourceProcessHandle: HANDLE, + hSourceHandle: HANDLE, + hTargetProcessHandle: HANDLE, + lpTargetHandle: LPHANDLE, + dwDesiredAccess: DWORD, + bInheritHandle: BOOL, + dwOptions: DWORD) -> BOOL; unsafe fn CloseHandle(hObject: HANDLE) -> BOOL; + unsafe fn OpenProcess(dwDesiredAccess: DWORD, + bInheritHandle: BOOL, + dwProcessId: DWORD) -> HANDLE; + unsafe fn GetCurrentProcess() -> HANDLE; + unsafe fn CreateProcessA(lpApplicationName: LPCTSTR, + lpCommandLine: LPTSTR, + lpProcessAttributes: LPSECURITY_ATTRIBUTES, + lpThreadAttributes: LPSECURITY_ATTRIBUTES, + bInheritHandles: BOOL, + dwCreationFlags: DWORD, + lpEnvironment: LPVOID, + lpCurrentDirectory: LPCTSTR, + lpStartupInfo: LPSTARTUPINFO, + lpProcessInformation: LPPROCESS_INFORMATION) -> BOOL; + unsafe fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD; unsafe fn TerminateProcess(hProcess: HANDLE, uExitCode: c_uint) -> BOOL; + unsafe fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: LPDWORD) -> BOOL; } } pub mod msvcrt { - use libc::types::os::arch::c95::c_int; + use libc::types::os::arch::c95::{c_int, c_long}; #[abi = "cdecl"] #[nolink] pub extern { #[link_name = "_commit"] unsafe fn commit(fd: c_int) -> c_int; + + #[link_name = "_get_osfhandle"] + unsafe fn get_osfhandle(fd: c_int) -> c_long; } } } diff --git a/src/libcore/os.rs b/src/libcore/os.rs index 8efae3e0e6890..c4b03d76cefec 100644 --- a/src/libcore/os.rs +++ b/src/libcore/os.rs @@ -373,7 +373,7 @@ pub fn pipe() -> Pipe { // inheritance has to be handled in a different way that I do not // fully understand. Here we explicitly make the pipe non-inheritable, // which means to pass it to a subprocess they need to be duplicated - // first, as in rust_run_program. + // first, as in core::run. let mut fds = Pipe {in: 0 as c_int, out: 0 as c_int }; let res = libc::pipe(&mut fds.in, 1024 as ::libc::c_uint, diff --git a/src/libcore/run.rs b/src/libcore/run.rs index d0c495dd19e44..0a8ccc3c5e9b9 100644 --- a/src/libcore/run.rs +++ b/src/libcore/run.rs @@ -22,31 +22,6 @@ use str; use task; use vec; -pub mod rustrt { - use libc::{c_int, c_void}; - use libc; - use run; - - #[abi = "cdecl"] - pub extern { - unsafe fn rust_run_program(argv: **libc::c_char, - envp: *c_void, - dir: *libc::c_char, - in_fd: c_int, - out_fd: c_int, - err_fd: c_int) -> run::RunProgramResult; - unsafe fn rust_process_wait(pid: c_int) -> c_int; - } -} - -pub struct RunProgramResult { - // the process id of the program, or -1 if in case of errors - pid: pid_t, - // a handle to the process - on unix this will always be NULL, but on windows it will be a - // HANDLE to the process, which will prevent the pid being re-used until the handle is closed. - handle: *(), -} - /// A value representing a child process pub struct Program { priv pid: pid_t, @@ -191,21 +166,262 @@ pub fn spawn_process(prog: &str, args: &[~str], return res.pid; } +struct RunProgramResult { + // the process id of the program (this should never be negative) + pid: pid_t, + // a handle to the process - on unix this will always be NULL, but on windows it will be a + // HANDLE to the process, which will prevent the pid being re-used until the handle is closed. + handle: *(), +} + +#[cfg(windows)] fn spawn_process_internal(prog: &str, args: &[~str], env: &Option<~[(~str,~str)]>, dir: &Option<~str>, in_fd: c_int, out_fd: c_int, err_fd: c_int) -> RunProgramResult { + + use libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO}; + use libc::consts::os::extra::{ + TRUE, FALSE, + STARTF_USESTDHANDLES, + INVALID_HANDLE_VALUE, + DUPLICATE_SAME_ACCESS + }; + use libc::funcs::extra::kernel32::{ + GetCurrentProcess, + DuplicateHandle, + CloseHandle, + CreateProcessA + }; + use libc::funcs::extra::msvcrt::get_osfhandle; + unsafe { - do with_argv(prog, args) |argv| { - do with_envp(env) |envp| { - do with_dirp(dir) |dirp| { - rustrt::rust_run_program(argv, envp, dirp, in_fd, out_fd, err_fd) + + let mut si = zeroed_startupinfo(); + si.cb = sys::size_of::() as DWORD; + si.dwFlags = STARTF_USESTDHANDLES; + + let cur_proc = GetCurrentProcess(); + + let orig_std_in = get_osfhandle(if in_fd > 0 { in_fd } else { 0 }) as HANDLE; + if orig_std_in == INVALID_HANDLE_VALUE as HANDLE { + fail!(fmt!("failure in get_osfhandle: %s", os::last_os_error())); + } + if DuplicateHandle(cur_proc, orig_std_in, cur_proc, &mut si.hStdInput, + 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE { + fail!(fmt!("failure in DuplicateHandle: %s", os::last_os_error())); + } + + let orig_std_out = get_osfhandle(if out_fd > 0 { out_fd } else { 1 }) as HANDLE; + if orig_std_out == INVALID_HANDLE_VALUE as HANDLE { + fail!(fmt!("failure in get_osfhandle: %s", os::last_os_error())); + } + if DuplicateHandle(cur_proc, orig_std_out, cur_proc, &mut si.hStdOutput, + 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE { + fail!(fmt!("failure in DuplicateHandle: %s", os::last_os_error())); + } + + let orig_std_err = get_osfhandle(if err_fd > 0 { err_fd } else { 2 }) as HANDLE; + if orig_std_err as HANDLE == INVALID_HANDLE_VALUE as HANDLE { + fail!(fmt!("failure in get_osfhandle: %s", os::last_os_error())); + } + if DuplicateHandle(cur_proc, orig_std_err, cur_proc, &mut si.hStdError, + 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE { + fail!(fmt!("failure in DuplicateHandle: %s", os::last_os_error())); + } + + let cmd = make_command_line(prog, args); + let mut pi = zeroed_process_information(); + let mut create_err = None; + + do with_envp(env) |envp| { + do with_dirp(dir) |dirp| { + do str::as_c_str(cmd) |cmdp| { + let created = CreateProcessA(ptr::null(), cast::transmute(cmdp), + ptr::mut_null(), ptr::mut_null(), TRUE, + 0, envp, dirp, &mut si, &mut pi); + if created == FALSE { + create_err = Some(os::last_os_error()); + } } } } + + CloseHandle(si.hStdInput); + CloseHandle(si.hStdOutput); + CloseHandle(si.hStdError); + + for create_err.each |msg| { + fail!(fmt!("failure in CreateProcess: %s", *msg)); + } + + // We close the thread handle because we don't care about keeping the thread id valid, + // and we aren't keeping the thread handle around to be able to close it later. We don't + // close the process handle however because we want the process id to stay valid at least + // until the calling code closes the process handle. + CloseHandle(pi.hThread); + + RunProgramResult { + pid: pi.dwProcessId as pid_t, + handle: pi.hProcess as *() + } + } +} + +#[cfg(windows)] +fn zeroed_startupinfo() -> libc::types::os::arch::extra::STARTUPINFO { + libc::types::os::arch::extra::STARTUPINFO { + cb: 0, + lpReserved: ptr::mut_null(), + lpDesktop: ptr::mut_null(), + lpTitle: ptr::mut_null(), + dwX: 0, + dwY: 0, + dwXSize: 0, + dwYSize: 0, + dwXCountChars: 0, + dwYCountCharts: 0, + dwFillAttribute: 0, + dwFlags: 0, + wShowWindow: 0, + cbReserved2: 0, + lpReserved2: ptr::mut_null(), + hStdInput: ptr::mut_null(), + hStdOutput: ptr::mut_null(), + hStdError: ptr::mut_null() + } +} + +#[cfg(windows)] +fn zeroed_process_information() -> libc::types::os::arch::extra::PROCESS_INFORMATION { + libc::types::os::arch::extra::PROCESS_INFORMATION { + hProcess: ptr::mut_null(), + hThread: ptr::mut_null(), + dwProcessId: 0, + dwThreadId: 0 } } +// FIXME: this is only pub so it can be tested (see issue #4536) +#[cfg(windows)] +pub fn make_command_line(prog: &str, args: &[~str]) -> ~str { + + let mut cmd = ~""; + append_arg(&mut cmd, prog); + for args.each |arg| { + cmd.push_char(' '); + append_arg(&mut cmd, *arg); + } + return cmd; + + fn append_arg(cmd: &mut ~str, arg: &str) { + let quote = arg.any(|c| c == ' ' || c == '\t'); + if quote { + cmd.push_char('"'); + } + for uint::range(0, arg.len()) |i| { + append_char_at(cmd, arg, i); + } + if quote { + cmd.push_char('"'); + } + } + + fn append_char_at(cmd: &mut ~str, arg: &str, i: uint) { + match arg[i] as char { + '"' => { + // Escape quotes. + cmd.push_str("\\\""); + } + '\\' => { + if backslash_run_ends_in_quote(arg, i) { + // Double all backslashes that are in runs before quotes. + cmd.push_str("\\\\"); + } else { + // Pass other backslashes through unescaped. + cmd.push_char('\\'); + } + } + c => { + cmd.push_char(c); + } + } + } + + fn backslash_run_ends_in_quote(s: &str, mut i: uint) -> bool { + while i < s.len() && s[i] as char == '\\' { + i += 1; + } + return i < s.len() && s[i] as char == '"'; + } +} + +#[cfg(unix)] +fn spawn_process_internal(prog: &str, args: &[~str], + env: &Option<~[(~str,~str)]>, + dir: &Option<~str>, + in_fd: c_int, out_fd: c_int, err_fd: c_int) -> RunProgramResult { + + use libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp}; + use libc::funcs::bsd44::getdtablesize; + + mod rustrt { + use libc::c_void; + + #[abi = "cdecl"] + pub extern { + unsafe fn rust_unset_sigprocmask(); + unsafe fn rust_set_environ(envp: *c_void); + } + } + + unsafe { + + let pid = fork(); + if pid < 0 { + fail!(fmt!("failure in fork: %s", os::last_os_error())); + } else if pid > 0 { + return RunProgramResult {pid: pid, handle: ptr::null()}; + } + + rustrt::rust_unset_sigprocmask(); + + if in_fd > 0 && dup2(in_fd, 0) == -1 { + fail!(fmt!("failure in dup2(in_fd, 0): %s", os::last_os_error())); + } + if out_fd > 0 && dup2(out_fd, 1) == -1 { + fail!(fmt!("failure in dup2(out_fd, 1): %s", os::last_os_error())); + } + if err_fd > 0 && dup2(err_fd, 2) == -1 { + fail!(fmt!("failure in dup3(err_fd, 2): %s", os::last_os_error())); + } + // close all other fds + for int::range_rev(getdtablesize() as int - 1, 2) |fd| { + close(fd as c_int); + } + + for dir.each |dir| { + do str::as_c_str(*dir) |dirp| { + if chdir(dirp) == -1 { + fail!(fmt!("failure in chdir: %s", os::last_os_error())); + } + } + } + + do with_envp(env) |envp| { + if !envp.is_null() { + rustrt::rust_set_environ(envp); + } + do with_argv(prog, args) |argv| { + execvp(*argv, argv); + // execvp only returns if an error occurred + fail!(fmt!("failure in execvp: %s", os::last_os_error())); + } + } + } +} + +#[cfg(unix)] fn with_argv(prog: &str, args: &[~str], cb: &fn(**libc::c_char) -> T) -> T { let mut argptrs = str::as_c_str(prog, |b| ~[b]); @@ -246,7 +462,7 @@ fn with_envp(env: &Option<~[(~str,~str)]>, #[cfg(windows)] fn with_envp(env: &Option<~[(~str,~str)]>, - cb: &fn(*c_void) -> T) -> T { + cb: &fn(*mut c_void) -> T) -> T { // On win32 we pass an "environment block" which is not a char**, but // rather a concatenation of null-terminated k=v\0 sequences, with a final // \0 to terminate. @@ -264,11 +480,12 @@ fn with_envp(env: &Option<~[(~str,~str)]>, blk += ~[0_u8]; vec::as_imm_buf(blk, |p, _len| cb(::cast::transmute(p))) } - _ => cb(ptr::null()) + _ => cb(ptr::mut_null()) } } } +#[cfg(windows)] fn with_dirp(d: &Option<~str>, cb: &fn(*libc::c_char) -> T) -> T { match *d { @@ -312,8 +529,6 @@ priv fn free_handle(_handle: *()) { pub fn run_program(prog: &str, args: &[~str]) -> int { let res = spawn_process_internal(prog, args, &None, &None, 0i32, 0i32, 0i32); - if res.pid == -1 as pid_t { fail!(); } - let code = waitpid(res.pid); free_handle(res.handle); return code; @@ -345,7 +560,6 @@ pub fn start_program(prog: &str, args: &[~str]) -> Program { pipe_err.out); unsafe { - if res.pid == -1 as pid_t { fail!(); } libc::close(pipe_input.in); libc::close(pipe_output.out); libc::close(pipe_err.out); @@ -398,13 +612,6 @@ pub fn program_output(prog: &str, args: &[~str]) -> ProgramOutput { os::close(pipe_in.in); os::close(pipe_out.out); os::close(pipe_err.out); - if res.pid == -1i32 { - os::close(pipe_in.out); - os::close(pipe_out.in); - os::close(pipe_err.in); - fail!(); - } - os::close(pipe_in.out); // Spawn two entire schedulers to read both stdout and sterr @@ -485,11 +692,46 @@ pub fn waitpid(pid: pid_t) -> int { #[cfg(windows)] fn waitpid_os(pid: pid_t) -> int { - let status = unsafe { rustrt::rust_process_wait(pid) }; - if status < 0 { - fail!(fmt!("failure in rust_process_wait: %s", os::last_os_error())); + + use libc::types::os::arch::extra::DWORD; + use libc::consts::os::extra::{ + SYNCHRONIZE, + PROCESS_QUERY_INFORMATION, + FALSE, + STILL_ACTIVE, + INFINITE, + WAIT_FAILED + }; + use libc::funcs::extra::kernel32::{ + OpenProcess, + GetExitCodeProcess, + CloseHandle, + WaitForSingleObject + }; + + unsafe { + + let proc = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, pid as DWORD); + if proc.is_null() { + fail!(fmt!("failure in OpenProcess: %s", os::last_os_error())); + } + + loop { + let mut status = 0; + if GetExitCodeProcess(proc, &mut status) == FALSE { + CloseHandle(proc); + fail!(fmt!("failure in GetExitCodeProcess: %s", os::last_os_error())); + } + if status != STILL_ACTIVE { + CloseHandle(proc); + return status as int; + } + if WaitForSingleObject(proc, INFINITE) == WAIT_FAILED { + CloseHandle(proc); + fail!(fmt!("failure in WaitForSingleObject: %s", os::last_os_error())); + } + } } - return status as int; } #[cfg(unix)] @@ -543,6 +785,27 @@ mod tests { use run::{readclose, writeclose}; use run; + #[test] + #[cfg(windows)] + fn test_make_command_line() { + assert_eq!( + run::make_command_line("prog", [~"aaa", ~"bbb", ~"ccc"]), + ~"prog aaa bbb ccc" + ); + assert_eq!( + run::make_command_line("C:\\Program Files\\blah\\blah.exe", [~"aaa"]), + ~"\"C:\\Program Files\\blah\\blah.exe\" aaa" + ); + assert_eq!( + run::make_command_line("C:\\Program Files\\test", [~"aa\"bb"]), + ~"\"C:\\Program Files\\test\" aa\\\"bb" + ); + assert_eq!( + run::make_command_line("echo", [~"a b c"]), + ~"echo \"a b c\"" + ); + } + // Regression test for memory leaks #[test] fn test_leaks() { diff --git a/src/rt/rust_run_program.cpp b/src/rt/rust_run_program.cpp index cf4beed1a00c6..0ba7607869140 100644 --- a/src/rt/rust_run_program.cpp +++ b/src/rt/rust_run_program.cpp @@ -15,212 +15,44 @@ #include #endif -struct RunProgramResult { - pid_t pid; - void* handle; -}; - #if defined(__WIN32__) -#include -#include - -bool backslash_run_ends_in_quote(char const *c) { - while (*c == '\\') ++c; - return *c == '"'; -} - -void append_first_char(char *&buf, char const *c) { - switch (*c) { - - case '"': - // Escape quotes. - *buf++ = '\\'; - *buf++ = '"'; - break; - - - case '\\': - if (backslash_run_ends_in_quote(c)) { - // Double all backslashes that are in runs before quotes. - *buf++ = '\\'; - *buf++ = '\\'; - } else { - // Pass other backslashes through unescaped. - *buf++ = '\\'; - } - break; - - default: - *buf++ = *c; - } +extern "C" CDECL void +rust_unset_sigprocmask() { + // empty stub for windows to keep linker happy } -bool contains_whitespace(char const *arg) { - while (*arg) { - switch (*arg++) { - case ' ': - case '\t': - return true; - } - } - return false; -} - -void append_arg(char *& buf, char const *arg, bool last) { - bool quote = contains_whitespace(arg); - if (quote) - *buf++ = '"'; - while (*arg) - append_first_char(buf, arg++); - if (quote) - *buf++ = '"'; - - if (! last) { - *buf++ = ' '; - } else { - *buf++ = '\0'; - } -} - -extern "C" CDECL RunProgramResult -rust_run_program(const char* argv[], - void* envp, - const char* dir, - int in_fd, int out_fd, int err_fd) { - STARTUPINFO si; - ZeroMemory(&si, sizeof(STARTUPINFO)); - si.cb = sizeof(STARTUPINFO); - si.dwFlags = STARTF_USESTDHANDLES; - - RunProgramResult result = {-1, NULL}; - - HANDLE curproc = GetCurrentProcess(); - HANDLE origStdin = (HANDLE)_get_osfhandle(in_fd ? in_fd : 0); - if (!DuplicateHandle(curproc, origStdin, - curproc, &si.hStdInput, 0, 1, DUPLICATE_SAME_ACCESS)) - return result; - HANDLE origStdout = (HANDLE)_get_osfhandle(out_fd ? out_fd : 1); - if (!DuplicateHandle(curproc, origStdout, - curproc, &si.hStdOutput, 0, 1, DUPLICATE_SAME_ACCESS)) - return result; - HANDLE origStderr = (HANDLE)_get_osfhandle(err_fd ? err_fd : 2); - if (!DuplicateHandle(curproc, origStderr, - curproc, &si.hStdError, 0, 1, DUPLICATE_SAME_ACCESS)) - return result; - - size_t cmd_len = 0; - for (const char** arg = argv; *arg; arg++) { - cmd_len += strlen(*arg); - cmd_len += 3; // Two quotes plus trailing space or \0 - } - cmd_len *= 2; // Potentially backslash-escape everything. - - char* cmd = (char*)malloc(cmd_len); - char* pos = cmd; - for (const char** arg = argv; *arg; arg++) { - append_arg(pos, *arg, *(arg+1) == NULL); - } - - PROCESS_INFORMATION pi; - BOOL created = CreateProcess(NULL, cmd, NULL, NULL, TRUE, - 0, envp, dir, &si, &pi); - - CloseHandle(si.hStdInput); - CloseHandle(si.hStdOutput); - CloseHandle(si.hStdError); - free(cmd); - - if (!created) { - return result; - } - - // We close the thread handle because we don't care about keeping the thread id valid, - // and we aren't keeping the thread handle around to be able to close it later. We don't - // close the process handle however because we want the process id to stay valid at least - // until the calling rust code closes the process handle. - CloseHandle(pi.hThread); - result.pid = pi.dwProcessId; - result.handle = pi.hProcess; - return result; -} - -extern "C" CDECL int -rust_process_wait(int pid) { - - HANDLE proc = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, pid); - if (proc == NULL) { - return -1; - } - - DWORD status; - while (true) { - if (!GetExitCodeProcess(proc, &status)) { - CloseHandle(proc); - return -1; - } - if (status != STILL_ACTIVE) { - CloseHandle(proc); - return (int) status; - } - WaitForSingleObject(proc, INFINITE); - } +extern "C" CDECL void +rust_set_environ(void* envp) { + // empty stub for windows to keep linker happy } #elif defined(__GNUC__) -#include #include -#include #include -#include #ifdef __FreeBSD__ extern char **environ; #endif -extern "C" CDECL RunProgramResult -rust_run_program(const char* argv[], - void* envp, - const char* dir, - int in_fd, int out_fd, int err_fd) { - int pid = fork(); - if (pid != 0) { - RunProgramResult result = {pid, NULL}; - return result; - } - +extern "C" CDECL void +rust_unset_sigprocmask() { + // this can't be safely converted to rust code because the + // representation of sigset_t is platform-dependent sigset_t sset; sigemptyset(&sset); sigprocmask(SIG_SETMASK, &sset, NULL); +} - if (in_fd) dup2(in_fd, 0); - if (out_fd) dup2(out_fd, 1); - if (err_fd) dup2(err_fd, 2); - /* Close all other fds. */ - for (int fd = getdtablesize() - 1; fd >= 3; fd--) close(fd); - if (dir) { - int result = chdir(dir); - // FIXME (#2674): need error handling - assert(!result && "chdir failed"); - } - - if (envp) { +extern "C" CDECL void +rust_set_environ(void* envp) { + // FIXME: this could actually be converted to rust (see issue #2674) #ifdef __APPLE__ - *_NSGetEnviron() = (char **)envp; + *_NSGetEnviron() = (char **) envp; #else - environ = (char **)envp; + environ = (char **) envp; #endif - } - - execvp(argv[0], (char * const *)argv); - exit(1); -} - -extern "C" CDECL int -rust_process_wait(int pid) { - // FIXME: stub; exists to placate linker. (#2692) - return 0; } #else diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index 977e0248ca206..408e2e9a81671 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -37,8 +37,8 @@ rust_list_dir_wfd_size rust_list_dir_wfd_fp_buf rust_log_console_on rust_log_console_off -rust_process_wait -rust_run_program +rust_set_environ +rust_unset_sigprocmask rust_sched_current_nonlazy_threads rust_sched_threads rust_set_exit_status From 23e97ae89303f7e8933dce0c42de08d214bad066 Mon Sep 17 00:00:00 2001 From: gareth Date: Wed, 1 May 2013 22:20:26 +0100 Subject: [PATCH 46/50] Remove errant trailing whitespace. --- src/libcore/run.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/run.rs b/src/libcore/run.rs index 0a8ccc3c5e9b9..0850121d6020d 100644 --- a/src/libcore/run.rs +++ b/src/libcore/run.rs @@ -374,7 +374,7 @@ fn spawn_process_internal(prog: &str, args: &[~str], unsafe fn rust_set_environ(envp: *c_void); } } - + unsafe { let pid = fork(); From cce97ab8cb225c9ae5b9e8dca4f96bd750eebdb7 Mon Sep 17 00:00:00 2001 From: Luqman Aden Date: Thu, 2 May 2013 11:33:57 -0700 Subject: [PATCH 47/50] Add test for drop for newtype structs. --- src/test/run-pass/newtype-struct-drop-run.rs | 28 ++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/test/run-pass/newtype-struct-drop-run.rs diff --git a/src/test/run-pass/newtype-struct-drop-run.rs b/src/test/run-pass/newtype-struct-drop-run.rs new file mode 100644 index 0000000000000..dd5da3b09bb69 --- /dev/null +++ b/src/test/run-pass/newtype-struct-drop-run.rs @@ -0,0 +1,28 @@ +// 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. + +// Make sure the destructor is run for newtype structs. + +struct Foo(@mut int); + +#[unsafe_destructor] +impl Drop for Foo { + fn finalize(&self) { + ***self = 23; + } +} + +fn main() { + let y = @mut 32; + { + let x = Foo(y); + } + assert_eq!(*y, 23); +} From bd979c1fbf11ffb5b3ff42ca31aa2bfae3082ec6 Mon Sep 17 00:00:00 2001 From: gareth Date: Thu, 2 May 2013 21:19:12 +0100 Subject: [PATCH 48/50] Fix some issues with test_destroy_actually_kills: - it is now cross platform, instead of just unix - it now avoids sleeping (fixing issue #6156) - it now calls force_destroy() when force = true (was a bug) --- src/libcore/run.rs | 52 +++++++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/src/libcore/run.rs b/src/libcore/run.rs index 0850121d6020d..7e73b3a3f80be 100644 --- a/src/libcore/run.rs +++ b/src/libcore/run.rs @@ -781,7 +781,6 @@ mod tests { use libc; use option::None; use os; - use path::Path; use run::{readclose, writeclose}; use run; @@ -870,34 +869,59 @@ mod tests { p.destroy(); // ...and nor should this (and nor should the destructor) } - #[cfg(unix)] // there is no way to sleep on windows from inside libcore... fn test_destroy_actually_kills(force: bool) { - let path = Path(fmt!("test/core-run-test-destroy-actually-kills-%?.tmp", force)); - os::remove_file(&path); + #[cfg(unix)] + static BLOCK_COMMAND: &'static str = "cat"; - let cmd = fmt!("sleep 5 && echo MurderDeathKill > %s", path.to_str()); - let mut p = run::start_program("sh", [~"-c", cmd]); + #[cfg(windows)] + static BLOCK_COMMAND: &'static str = "cmd"; - p.destroy(); // destroy the program before it has a chance to echo its message + #[cfg(unix)] + fn process_exists(pid: libc::pid_t) -> bool { + run::program_output("ps", [~"-p", pid.to_str()]).out.contains(pid.to_str()) + } - unsafe { - // wait to ensure the program is really destroyed and not just waiting itself - libc::sleep(10); + #[cfg(windows)] + fn process_exists(pid: libc::pid_t) -> bool { + + use libc::types::os::arch::extra::DWORD; + use libc::funcs::extra::kernel32::{CloseHandle, GetExitCodeProcess, OpenProcess}; + use libc::consts::os::extra::{FALSE, PROCESS_QUERY_INFORMATION, STILL_ACTIVE }; + + unsafe { + let proc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid as DWORD); + if proc.is_null() { + return false; + } + // proc will be non-null if the process is alive, or if it died recently + let mut status = 0; + GetExitCodeProcess(proc, &mut status); + CloseHandle(proc); + return status == STILL_ACTIVE; + } } - // the program should not have had chance to echo its message - assert!(!path.exists()); + // this program will stay alive indefinitely trying to read from stdin + let mut p = run::start_program(BLOCK_COMMAND, []); + + assert!(process_exists(p.get_id())); + + if force { + p.force_destroy(); + } else { + p.destroy(); + } + + assert!(!process_exists(p.get_id())); } #[test] - #[cfg(unix)] fn test_unforced_destroy_actually_kills() { test_destroy_actually_kills(false); } #[test] - #[cfg(unix)] fn test_forced_destroy_actually_kills() { test_destroy_actually_kills(true); } From db6a62c537852a30f030f866598c358d01fb95cd Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 2 May 2013 18:42:07 -0700 Subject: [PATCH 49/50] 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 4c5a17056b2ea..4025835495b58 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 50/50] 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 215fc95f8c7d7..4a8d9fe27d915 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) -> ! {