diff --git a/src/libcore/rt/io/comm_adapters.rs b/src/libcore/rt/io/comm_adapters.rs new file mode 100644 index 0000000000000..1d6893b3ca616 --- /dev/null +++ b/src/libcore/rt/io/comm_adapters.rs @@ -0,0 +1,59 @@ +// 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::*; +use super::{Reader, Writer}; + +struct PortReader

; + +impl> PortReader

{ + pub fn new(_port: P) -> PortReader

{ fail!() } +} + +impl> Reader for PortReader

{ + fn read(&mut self, _buf: &mut [u8]) -> Option { fail!() } + + fn eof(&mut self) -> bool { fail!() } +} + +struct ChanWriter; + +impl> ChanWriter { + pub fn new(_chan: C) -> ChanWriter { fail!() } +} + +impl> Writer for ChanWriter { + pub fn write(&mut self, _buf: &[u8]) { fail!() } + + pub fn flush(&mut self) { fail!() } +} + +struct ReaderPort; + +impl ReaderPort { + pub fn new(_reader: R) -> ReaderPort { fail!() } +} + +impl GenericPort<~[u8]> for ReaderPort { + fn recv(&self) -> ~[u8] { fail!() } + + fn try_recv(&self) -> Option<~[u8]> { fail!() } +} + +struct WriterChan; + +impl WriterChan { + pub fn new(_writer: W) -> WriterChan { fail!() } +} + +impl GenericChan<~[u8]> for WriterChan { + fn send(&self, _x: ~[u8]) { fail!() } +} + diff --git a/src/libcore/rt/io/file.rs b/src/libcore/rt/io/file.rs index b7f3ed280a845..e041183b58452 100644 --- a/src/libcore/rt/io/file.rs +++ b/src/libcore/rt/io/file.rs @@ -9,35 +9,79 @@ // except according to those terms. use prelude::*; -use super::Stream; +use super::misc::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 { + /// Opens an existing file. IoError if file does not exist. + Open, + /// Creates a file. IoError if file exists. + Create, + /// Opens an existing file or creates a new one. + OpenOrCreate, + /// Opens an existing file or creates a new one, positioned at EOF. + Append, + /// Opens an existing file, truncating it to 0 bytes. + Truncate, + /// Opens an existing file or creates a new one, truncating it to 0 bytes. + CreateOrTruncate, +} + +enum FileAccess { + Read, + Write, + ReadWrite +} pub struct FileStream; -pub impl FileStream { - fn new(_path: Path) -> FileStream { +impl FileStream { + pub fn open(_path: &P, + _mode: FileMode, + _access: FileAccess + ) -> Result { fail!() } } -impl Stream for FileStream { - fn read(&mut self, _buf: &mut [u8]) -> uint { +impl Reader for FileStream { + fn read(&mut self, _buf: &mut [u8]) -> Option { fail!() } fn eof(&mut self) -> bool { fail!() } +} - fn write(&mut self, _v: &const [u8]) { - fail!() - } +impl Writer for FileStream { + fn write(&mut self, _v: &[u8]) { fail!() } + + fn flush(&mut self) { fail!() } +} + +impl Seek for FileStream { + fn tell(&self) -> u64 { fail!() } + + fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() } +} + +impl Close for FileStream { + fn close(&mut self) { fail!() } } #[test] #[ignore] fn super_simple_smoke_test_lets_go_read_some_files_and_have_a_good_time() { let message = "it's alright. have a good time"; - let filename = Path("test.txt"); - let mut outstream = FileStream::new(filename); + let filename = &Path("test.txt"); + let mut outstream = FileStream::open(filename, Create, Read).unwrap(); outstream.write(message.to_bytes()); } diff --git a/src/libcore/rt/io/flate.rs b/src/libcore/rt/io/flate.rs new file mode 100644 index 0000000000000..24537bef329ab --- /dev/null +++ b/src/libcore/rt/io/flate.rs @@ -0,0 +1,121 @@ +// 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. + +//! Some various other I/O types + +// NOTE: These ultimately belong somewhere else + +use prelude::*; +use super::*; + +/// A Writer decorator that compresses using the 'deflate' scheme +pub struct DeflateWriter { + inner_writer: W +} + +impl DeflateWriter { + pub fn new(inner_writer: W) -> DeflateWriter { + DeflateWriter { + inner_writer: inner_writer + } + } +} + +impl Writer for DeflateWriter { + fn write(&mut self, _buf: &[u8]) { fail!() } + + fn flush(&mut self) { fail!() } +} + +impl Decorator for DeflateWriter { + fn inner(self) -> W { + match self { + DeflateWriter { inner_writer: w } => w + } + } + + fn inner_ref<'a>(&'a self) -> &'a W { + match *self { + DeflateWriter { inner_writer: ref w } => w + } + } + + fn inner_mut_ref<'a>(&'a mut self) -> &'a mut W { + match *self { + DeflateWriter { inner_writer: ref mut w } => w + } + } +} + +/// A Reader decorator that decompresses using the 'deflate' scheme +pub struct InflateReader { + inner_reader: R +} + +impl InflateReader { + pub fn new(inner_reader: R) -> InflateReader { + InflateReader { + inner_reader: inner_reader + } + } +} + +impl Reader for InflateReader { + fn read(&mut self, _buf: &mut [u8]) -> Option { fail!() } + + fn eof(&mut self) -> bool { fail!() } +} + +impl Decorator for InflateReader { + fn inner(self) -> R { + match self { + InflateReader { inner_reader: r } => r + } + } + + fn inner_ref<'a>(&'a self) -> &'a R { + match *self { + InflateReader { inner_reader: ref r } => r + } + } + + fn inner_mut_ref<'a>(&'a mut self) -> &'a mut R { + match *self { + InflateReader { inner_reader: ref mut r } => r + } + } +} + +#[cfg(test)] +mod test { + use prelude::*; + use super::*; + use super::super::mem::*; + use super::super::Decorator; + + #[test] + #[ignore] + fn smoke_test() { + let mem_writer = MemWriter::new(); + let mut deflate_writer = DeflateWriter::new(mem_writer); + let in_msg = "test"; + let in_bytes = in_msg.to_bytes(); + deflate_writer.write(in_bytes); + deflate_writer.flush(); + let buf = deflate_writer.inner().inner(); + let mem_reader = MemReader::new(buf); + let mut inflate_reader = InflateReader::new(mem_reader); + let mut out_bytes = [0, .. 100]; + let bytes_read = inflate_reader.read(out_bytes).get(); + assert!(bytes_read == in_bytes.len()); + let out_msg = str::from_bytes(out_bytes); + assert!(in_msg == out_msg); + } +} \ No newline at end of file diff --git a/src/libcore/rt/io/mem.rs b/src/libcore/rt/io/mem.rs new file mode 100644 index 0000000000000..600968a3c7105 --- /dev/null +++ b/src/libcore/rt/io/mem.rs @@ -0,0 +1,166 @@ +// 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. + +//! Readers and Writers for in-memory buffers +//! +//! # XXX +//! +//! * Should probably have something like this for strings. +//! * Should they implement Closable? Would take extra state. + +use prelude::*; +use super::*; + + +/// Writes to an owned, growable byte vector +pub struct MemWriter { + buf: ~[u8] +} + +impl MemWriter { + pub fn new() -> MemWriter { MemWriter { buf: ~[] } } +} + +impl Writer for MemWriter { + fn write(&mut self, _buf: &[u8]) { fail!() } + + fn flush(&mut self) { /* no-op */ } +} + +impl Seek for MemWriter { + fn tell(&self) -> u64 { fail!() } + + fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() } +} + +impl Decorator<~[u8]> for MemWriter { + + fn inner(self) -> ~[u8] { + match self { + MemWriter { buf: buf } => buf + } + } + + fn inner_ref<'a>(&'a self) -> &'a ~[u8] { + match *self { + MemWriter { buf: ref buf } => buf + } + } + + fn inner_mut_ref<'a>(&'a mut self) -> &'a mut ~[u8] { + match *self { + MemWriter { buf: ref mut buf } => buf + } + } +} + +/// Reads from an owned byte vector +pub struct MemReader { + buf: ~[u8], + pos: uint +} + +impl MemReader { + pub fn new(buf: ~[u8]) -> MemReader { + MemReader { + buf: buf, + pos: 0 + } + } +} + +impl Reader for MemReader { + fn read(&mut self, _buf: &mut [u8]) -> Option { fail!() } + + fn eof(&mut self) -> bool { fail!() } +} + +impl Seek for MemReader { + fn tell(&self) -> u64 { fail!() } + + fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() } +} + +impl Decorator<~[u8]> for MemReader { + + fn inner(self) -> ~[u8] { + match self { + MemReader { buf: buf, _ } => buf + } + } + + fn inner_ref<'a>(&'a self) -> &'a ~[u8] { + match *self { + MemReader { buf: ref buf, _ } => buf + } + } + + fn inner_mut_ref<'a>(&'a mut self) -> &'a mut ~[u8] { + match *self { + MemReader { buf: ref mut buf, _ } => buf + } + } +} + + +/// Writes to a fixed-size byte slice +struct BufWriter<'self> { + buf: &'self mut [u8], + pos: uint +} + +impl<'self> BufWriter<'self> { + pub fn new<'a>(buf: &'a mut [u8]) -> BufWriter<'a> { + BufWriter { + buf: buf, + pos: 0 + } + } +} + +impl<'self> Writer for BufWriter<'self> { + fn write(&mut self, _buf: &[u8]) { fail!() } + + fn flush(&mut self) { fail!() } +} + +impl<'self> Seek for BufWriter<'self> { + fn tell(&self) -> u64 { fail!() } + + fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() } +} + + +/// Reads from a fixed-size byte slice +struct BufReader<'self> { + buf: &'self [u8], + pos: uint +} + +impl<'self> BufReader<'self> { + pub fn new<'a>(buf: &'a [u8]) -> BufReader<'a> { + BufReader { + buf: buf, + pos: 0 + } + } +} + +impl<'self> Reader for BufReader<'self> { + fn read(&mut self, _buf: &mut [u8]) -> Option { fail!() } + + fn eof(&mut self) -> bool { fail!() } +} + +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 diff --git a/src/libcore/rt/io/misc.rs b/src/libcore/rt/io/misc.rs new file mode 100644 index 0000000000000..7bace5d6df2cc --- /dev/null +++ b/src/libcore/rt/io/misc.rs @@ -0,0 +1,42 @@ +// 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 path::*; + +pub trait PathLike { + fn path_as_str(&self, f: &fn(&str) -> T) -> T; +} + +impl<'self> PathLike for &'self str { + fn path_as_str(&self, f: &fn(&str) -> T) -> T { + f(*self) + } +} + +impl PathLike for Path { + fn path_as_str(&self, f: &fn(&str) -> T) -> T { + let s = self.to_str(); + f(s) + } +} + +#[cfg(test)] +mod test { + use path::*; + use super::PathLike; + + #[test] + fn path_like_smoke_test() { + let expected = "/home"; + let path = Path(expected); + path.path_as_str(|p| assert!(p == expected)); + path.path_as_str(|p| assert!(p == expected)); + } +} diff --git a/src/libcore/rt/io/mod.rs b/src/libcore/rt/io/mod.rs index 1634e7443b479..b035532144c44 100644 --- a/src/libcore/rt/io/mod.rs +++ b/src/libcore/rt/io/mod.rs @@ -8,35 +8,276 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +/*! Synchronous I/O + +This module defines the Rust interface for synchronous I/O. +It supports file access, + +This will likely live in core::io, not core::rt::io. + +# Examples + +Some examples of obvious things you might want to do + +* Read lines from stdin + + for stdin().each_line |line| { + println(line) + } + +* Read a complete file to a string, (converting newlines?) + + let contents = open("message.txt").read_to_str(); // read_to_str?? + +* Write a line to a file + + let file = FileStream::open("message.txt", Create, Write); + file.write_line("hello, file!"); + +* Iterate over the lines of a file + +* Pull the lines of a file into a vector of strings + +* 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 /"); + +# 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'? + +# Error Handling + +# Resource management + +* `close` vs. RAII + +# Paths and URLs + +# std + +Some I/O things don't belong in core + + - url + - net - `fn connect` + - http + - flate + +# XXX + +* Should default constructors take `Path` or `&str`? `Path` makes simple cases verbose. + Overloading would be nice. +* Add overloading for Path and &str and Url &str +* stdin/err/out +* print, println, etc. +* fsync +* relationship with filesystem querying, Directory, File types etc. +* Rename Reader/Writer to ByteReader/Writer, make Reader/Writer generic? +* Trait for things that are both readers and writers, Stream? +* How to handle newline conversion +* String conversion +* File vs. FileStream? File is shorter but could also be used for getting file info + - maybe File is for general file querying and *also* has a static `open` method +* open vs. connect for generic stream opening +* Do we need `close` at all? dtors might be good enough +* How does I/O relate to the Iterator trait? +* std::base64 filters + +*/ + +use prelude::*; + +// Reexports +pub use self::stdio::stdin; +pub use self::stdio::stdout; +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; +pub use self::net::tcp::TcpListener; +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; + +/// Synchronous, non-blocking file I/O. pub mod file; -// FIXME #5370 Strongly want this to be StreamError(&mut Stream) -pub struct StreamError; +/// Synchronous, non-blocking network I/O. +#[path = "net/mod.rs"] +pub mod net; + +/// Readers and Writers for memory buffers and strings. +#[cfg(not(stage0))] // XXX Using unsnapshotted features +pub mod mem; + +/// Non-blocking access to stdin, stdout, stderr +pub mod stdio; + +/// Basic stream compression. XXX: Belongs with other flate code +#[cfg(not(stage0))] // XXX Using unsnapshotted features +pub mod flate; + +/// Interop between byte streams and pipes. Not sure where it belongs +#[cfg(not(stage0))] // XXX " +pub mod comm_adapters; + +/// Extension traits +mod util; + +/// Non-I/O things needed by the I/O module +mod misc; + +/// Thread-blocking implementations +pub mod native { + /// Posix file I/O + pub mod file; + /// # XXX - implement this + pub mod stdio { } + /// Sockets + /// # XXX - implement this + pub mod net { + pub mod tcp { } + pub mod udp { } + #[cfg(unix)] + pub mod unix { } + } +} + + +/// The type passed to I/O condition handlers to indicate error +/// +/// # XXX +/// +/// Is something like this sufficient? It's kind of archaic +pub struct IoError { + kind: IoErrorKind, + desc: &'static str, + detail: Option<~str> +} + +pub enum IoErrorKind { + FileNotFound, + FilePermission, + ConnectionFailed, + Closed, + OtherIoError +} // XXX: Can't put doc comments on macros -// Raised by `Stream` instances on error. Returning `true` from the handler -// indicates that the `Stream` should continue, `false` that it should fail. +// Raised by `I/O` operations on error. condition! { - stream_error: super::StreamError -> bool; + io_error: super::IoError -> (); } -pub trait Stream { - /// Read bytes, up to the length of `buf` and place them in `buf`, - /// returning the number of bytes read or an `IoError`. Reads - /// 0 bytes on EOF. +pub trait Reader { + /// Read bytes, up to the length of `buf` and place them in `buf`. + /// Returns the number of bytes read, or `None` on EOF. /// /// # Failure /// - /// Raises the `reader_error` condition on error - fn read(&mut self, buf: &mut [u8]) -> uint; + /// Raises the `io_error` condition on error, then returns `None`. + /// + /// # XXX + /// + /// This doesn't take a `len` argument like the old `read`. + /// Will people often need to slice their vectors to call this + /// and will that be annoying? + fn read(&mut self, buf: &mut [u8]) -> Option; - /// Return whether the Reader has reached the end of the stream + /// Return whether the Reader has reached the end of the stream. + /// + /// # Example + /// + /// let reader = FileStream::new() + /// while !reader.eof() { + /// println(reader.read_line()); + /// } + /// + /// # XXX + /// + /// What does this return if the Reader is in an error state? fn eof(&mut self) -> bool; +} +pub trait Writer { /// Write the given buffer /// /// # Failure /// - /// Raises the `writer_error` condition on error - fn write(&mut self, v: &const [u8]); + /// Raises the `io_error` condition on error + fn write(&mut self, buf: &[u8]); + + /// Flush output + fn flush(&mut self); +} + +/// I/O types that may be closed +/// +/// Any further operations performed on a closed resource will raise +/// on `io_error` +pub trait Close { + /// Close the I/O resource + fn close(&mut self); +} + +pub trait Stream: Reader + Writer + Close { } + +pub enum SeekStyle { + /// Seek from the beginning of the stream + SeekSet, + /// Seek from the end of the stream + SeekEnd, + /// Seek from the current position + SeekCur, +} + +/// # XXX +/// * Are `u64` and `i64` the right choices? +pub trait Seek { + fn tell(&self) -> u64; + fn seek(&mut self, pos: i64, style: SeekStyle); +} + +/// Common trait for decorator types. +/// +/// Provides accessors to get the inner, 'decorated' values. The I/O library +/// uses decorators to add functionality like compression and encryption to I/O +/// streams. +/// +/// # XXX +/// +/// Is this worth having a trait for? May be overkill +pub trait Decorator { + /// Destroy the decorator and extract the decorated value + /// + /// # XXX + /// + /// Because this takes `self' one could never 'undecorate' a Reader/Writer + /// that has been boxed. Is that ok? This feature is mostly useful for + /// extracting the buffer from MemWriter + fn inner(self) -> T; + + /// Take an immutable reference to the decorated value + fn inner_ref<'a>(&'a self) -> &'a T; + + /// Take a mutable reference to the decorated value + fn inner_mut_ref<'a>(&'a mut self) -> &'a mut T; } diff --git a/src/libcore/rt/io/native/file.rs b/src/libcore/rt/io/native/file.rs new file mode 100644 index 0000000000000..e203df815f2f4 --- /dev/null +++ b/src/libcore/rt/io/native/file.rs @@ -0,0 +1,82 @@ +// 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. + +//! Blocking posix-based file I/O + +use prelude::*; +use super::super::*; +use libc::{c_int, FILE}; + +#[allow(non_camel_case_types)] +pub type fd_t = c_int; + +// Make this a newtype so we can't do I/O on arbitrary integers +pub struct FileDesc(fd_t); + +impl FileDesc { + /// Create a `FileDesc` from an open C file descriptor. + /// + /// The `FileDesc` takes ownership of the file descriptor + /// and will close it upon destruction. + pub fn new(_fd: fd_t) -> FileDesc { fail!() } +} + +impl Reader for FileDesc { + fn read(&mut self, _buf: &mut [u8]) -> Option { fail!() } + + fn eof(&mut self) -> bool { fail!() } +} + +impl Writer for FileDesc { + fn write(&mut self, _buf: &[u8]) { fail!() } + + fn flush(&mut self) { fail!() } +} + +impl Close for FileDesc { + fn close(&mut self) { fail!() } +} + +impl Seek for FileDesc { + fn tell(&self) -> u64 { fail!() } + + fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() } +} + +pub struct CFile(*FILE); + +impl CFile { + /// Create a `CFile` from an open `FILE` pointer. + /// + /// The `CFile` takes ownership of the file descriptor + /// and will close it upon destruction. + pub fn new(_file: *FILE) -> CFile { fail!() } +} + +impl Reader for CFile { + fn read(&mut self, _buf: &mut [u8]) -> Option { fail!() } + + fn eof(&mut self) -> bool { fail!() } +} + +impl Writer for CFile { + fn write(&mut self, _buf: &[u8]) { fail!() } + + fn flush(&mut self) { fail!() } +} + +impl Close for CFile { + fn close(&mut self) { fail!() } +} + +impl Seek for CFile { + fn tell(&self) -> u64 { fail!() } + fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() } +} diff --git a/src/libcore/rt/io/net/http.rs b/src/libcore/rt/io/net/http.rs new file mode 100644 index 0000000000000..c693cfaab678b --- /dev/null +++ b/src/libcore/rt/io/net/http.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. + +//! Simple HTTP client and server + +// XXX This should not be in core + +struct HttpServer; + +#[cfg(test)] +mod test { + use unstable::run_in_bare_thread; + + #[test] #[ignore] + fn smoke_test() { + do run_in_bare_thread { + } + + do run_in_bare_thread { + } + } +} diff --git a/src/libcore/rt/io/net/ip.rs b/src/libcore/rt/io/net/ip.rs new file mode 100644 index 0000000000000..d9b7f4e6e4011 --- /dev/null +++ b/src/libcore/rt/io/net/ip.rs @@ -0,0 +1,15 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub enum IpAddr { + Ipv4(u8, u8, u8, u8, u16), + Ipv6 +} + diff --git a/src/libcore/rt/io/net/mod.rs b/src/libcore/rt/io/net/mod.rs new file mode 100644 index 0000000000000..130ff6b38fa82 --- /dev/null +++ b/src/libcore/rt/io/net/mod.rs @@ -0,0 +1,31 @@ +// 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; +} diff --git a/src/libcore/rt/io/net/tcp.rs b/src/libcore/rt/io/net/tcp.rs new file mode 100644 index 0000000000000..e3f71dca8c827 --- /dev/null +++ b/src/libcore/rt/io/net/tcp.rs @@ -0,0 +1,50 @@ +// 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::*; +use super::*; +use super::super::*; +use super::ip::IpAddr; + +pub struct TcpStream; + +impl TcpStream { + pub fn connect(_addr: IpAddr) -> Result { + fail!() + } +} + +impl Reader for TcpStream { + fn read(&mut self, _buf: &mut [u8]) -> Option { fail!() } + + fn eof(&mut self) -> bool { fail!() } +} + +impl Writer for TcpStream { + fn write(&mut self, _buf: &[u8]) { fail!() } + + fn flush(&mut self) { fail!() } +} + +impl Close for TcpStream { + fn close(&mut self) { fail!() } +} + +pub struct TcpListener; + +impl TcpListener { + pub fn new(_addr: IpAddr) -> TcpListener { + fail!() + } +} + +impl Listener for TcpListener { + fn accept(&mut self) -> Option { fail!() } +} diff --git a/src/libcore/rt/io/net/udp.rs b/src/libcore/rt/io/net/udp.rs new file mode 100644 index 0000000000000..f76bb58a45eb9 --- /dev/null +++ b/src/libcore/rt/io/net/udp.rs @@ -0,0 +1,51 @@ +// 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::*; +use super::*; +use super::super::*; +use super::ip::IpAddr; + +pub struct UdpStream; + +impl UdpStream { + pub fn connect(_addr: IpAddr) -> Result { + fail!() + } +} + +impl Reader for UdpStream { + fn read(&mut self, _buf: &mut [u8]) -> Option { fail!() } + + fn eof(&mut self) -> bool { fail!() } +} + +impl Writer for UdpStream { + fn write(&mut self, _buf: &[u8]) { fail!() } + + fn flush(&mut self) { fail!() } +} + +impl Close for UdpStream { + fn close(&mut self) { fail!() } +} + +pub struct UdpListener; + +impl UdpListener { + pub fn new(_addr: IpAddr) -> UdpListener { + fail!() + } +} + +impl Listener for UdpListener { + fn accept(&mut self) -> Option { fail!() } +} + diff --git a/src/libcore/rt/io/net/unix.rs b/src/libcore/rt/io/net/unix.rs new file mode 100644 index 0000000000000..35eabe21b2a6b --- /dev/null +++ b/src/libcore/rt/io/net/unix.rs @@ -0,0 +1,51 @@ +// 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::*; +use super::*; +use super::super::*; +use super::super::misc::PathLike; + +pub struct UnixStream; + +impl UnixStream { + pub fn connect(_path: &P) -> Result { + fail!() + } +} + +impl Reader for UnixStream { + fn read(&mut self, _buf: &mut [u8]) -> Option { fail!() } + + fn eof(&mut self) -> bool { fail!() } +} + +impl Writer for UnixStream { + fn write(&mut self, _v: &[u8]) { fail!() } + + fn flush(&mut self) { fail!() } +} + +impl Close for UnixStream { + fn close(&mut self) { fail!() } +} + +pub struct UnixListener; + +impl UnixListener { + pub fn new(_path: &P) -> UnixListener { + fail!() + } +} + +impl Listener for UnixListener { + fn accept(&mut self) -> Option { fail!() } +} + diff --git a/src/libcore/rt/io/stdio.rs b/src/libcore/rt/io/stdio.rs new file mode 100644 index 0000000000000..21989f5e873d0 --- /dev/null +++ b/src/libcore/rt/io/stdio.rs @@ -0,0 +1,60 @@ +// 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::*; +use super::{Reader, Writer, Close}; + +pub fn stdin() -> StdReader { fail!() } + +pub fn stdout() -> StdWriter { fail!() } + +pub fn stderr() -> StdReader { fail!() } + +pub fn print(s: &str) { fail!() } + +pub fn println(s: &str) { fail!() } + +pub enum StdStream { + StdIn, + StdOut, + StdErr +} + +pub struct StdReader; + +impl StdReader { + pub fn new(_stream: StdStream) -> StdReader { fail!() } +} + +impl Reader for StdReader { + fn read(&mut self, _buf: &mut [u8]) -> Option { fail!() } + + fn eof(&mut self) -> bool { fail!() } +} + +impl Close for StdReader { + fn close(&mut self) { fail!() } +} + +pub struct StdWriter; + +impl StdWriter { + pub fn new(_stream: StdStream) -> StdWriter { fail!() } +} + +impl Writer for StdWriter { + fn write(&mut self, _buf: &[u8]) { fail!() } + + fn flush(&mut self) { fail!() } +} + +impl Close for StdWriter { + fn close(&mut self) { fail!() } +} diff --git a/src/libcore/rt/io/util.rs b/src/libcore/rt/io/util.rs new file mode 100644 index 0000000000000..cff224a80bee2 --- /dev/null +++ b/src/libcore/rt/io/util.rs @@ -0,0 +1,469 @@ +// 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. + +//! Utility mixins that apply to all Readers and Writers + +// XXX: Not sure how this should be structured +// XXX: Iteration should probably be considered seperately + +pub trait ReaderUtil { + + /// Reads `len` bytes and gives you back a new vector + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. Returns an empty + /// vector if the condition is handled. + fn read_bytes(&mut self, len: uint) -> ~[u8]; + + /// Reads all remaining bytes from the stream. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. Returns an empty + /// vector if the condition is handled. + fn read_to_end(&mut self) -> ~[u8]; + +} + +pub trait ReaderByteConversions { + /// Reads `n` little-endian unsigned integer bytes. + /// + /// `n` must be between 1 and 8, inclusive. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. Returns `0` if + /// the condition is handled. + fn read_le_uint_n(&mut self, nbytes: uint) -> u64; + + /// Reads `n` little-endian signed integer bytes. + /// + /// `n` must be between 1 and 8, inclusive. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. Returns `0` if + /// the condition is handled. + fn read_le_int_n(&mut self, nbytes: uint) -> i64; + + /// Reads `n` big-endian unsigned integer bytes. + /// + /// `n` must be between 1 and 8, inclusive. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. Returns `0` if + /// the condition is handled. + fn read_be_uint_n(&mut self, nbytes: uint) -> u64; + + /// Reads `n` big-endian signed integer bytes. + /// + /// `n` must be between 1 and 8, inclusive. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. Returns `0` if + /// the condition is handled. + fn read_be_int_n(&mut self, nbytes: uint) -> i64; + + /// Reads a little-endian unsigned integer. + /// + /// The number of bytes returned is system-dependant. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. Returns `0` if + /// the condition is handled. + fn read_le_uint(&mut self) -> uint; + + /// Reads a little-endian integer. + /// + /// The number of bytes returned is system-dependant. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. Returns `0` if + /// the condition is handled. + fn read_le_int(&mut self) -> int; + + /// Reads a big-endian unsigned integer. + /// + /// The number of bytes returned is system-dependant. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. Returns `0` if + /// the condition is handled. + fn read_be_uint(&mut self) -> uint; + + /// Reads a big-endian integer. + /// + /// The number of bytes returned is system-dependant. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. Returns `0` if + /// the condition is handled. + fn read_be_int(&mut self) -> int; + + /// Reads a big-endian `u64`. + /// + /// `u64`s are 8 bytes long. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. Returns `0` if + /// the condition is handled. + fn read_be_u64(&mut self) -> u64; + + /// Reads a big-endian `u32`. + /// + /// `u32`s are 4 bytes long. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. Returns `0` if + /// the condition is handled. + fn read_be_u32(&mut self) -> u32; + + /// Reads a big-endian `u16`. + /// + /// `u16`s are 2 bytes long. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. Returns `0` if + /// the condition is handled. + fn read_be_u16(&mut self) -> u16; + + /// Reads a big-endian `i64`. + /// + /// `i64`s are 8 bytes long. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. Returns `0` if + /// the condition is handled. + fn read_be_i64(&mut self) -> i64; + + /// Reads a big-endian `i32`. + /// + /// `i32`s are 4 bytes long. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. Returns `0` if + /// the condition is handled. + fn read_be_i32(&mut self) -> i32; + + /// Reads a big-endian `i16`. + /// + /// `i16`s are 2 bytes long. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. Returns `0` if + /// the condition is handled. + fn read_be_i16(&mut self) -> i16; + + /// Reads a big-endian `f64`. + /// + /// `f64`s are 8 byte, IEEE754 double-precision floating point numbers. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. Returns `0` if + /// the condition is handled. + fn read_be_f64(&mut self) -> f64; + + /// Reads a big-endian `f32`. + /// + /// `f32`s are 4 byte, IEEE754 single-precision floating point numbers. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. Returns `0` if + /// the condition is handled. + fn read_be_f32(&mut self) -> f32; + + /// Reads a little-endian `u64`. + /// + /// `u64`s are 8 bytes long. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. Returns `0` if + /// the condition is handled. + fn read_le_u64(&mut self) -> u64; + + /// Reads a little-endian `u32`. + /// + /// `u32`s are 4 bytes long. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. Returns `0` if + /// the condition is handled. + fn read_le_u32(&mut self) -> u32; + + /// Reads a little-endian `u16`. + /// + /// `u16`s are 2 bytes long. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. Returns `0` if + /// the condition is handled. + fn read_le_u16(&mut self) -> u16; + + /// Reads a little-endian `i64`. + /// + /// `i64`s are 8 bytes long. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. Returns `0` if + /// the condition is handled. + fn read_le_i64(&mut self) -> i64; + + /// Reads a little-endian `i32`. + /// + /// `i32`s are 4 bytes long. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. Returns `0` if + /// the condition is handled. + fn read_le_i32(&mut self) -> i32; + + /// Reads a little-endian `i16`. + /// + /// `i16`s are 2 bytes long. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. Returns `0` if + /// the condition is handled. + fn read_le_i16(&mut self) -> i16; + + /// Reads a little-endian `f64`. + /// + /// `f64`s are 8 byte, IEEE754 double-precision floating point numbers. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. Returns `0` if + /// the condition is handled. + fn read_le_f64(&mut self) -> f64; + + /// Reads a little-endian `f32`. + /// + /// `f32`s are 4 byte, IEEE754 single-precision floating point numbers. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. Returns `0` if + /// the condition is handled. + fn read_le_f32(&mut self) -> f32; + + /// Read a u8. + /// + /// `u8`s are 1 byte. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. Returns `0` if + /// the condition is handled. + fn read_u8(&mut self) -> u8; + + /// Read an i8. + /// + /// `i8`s are 1 byte. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. Returns `0` if + /// the condition is handled. + fn read_i8(&mut self) -> i8; + +} + +pub trait WriterByteConversions { + /// Write the result of passing n through `int::to_str_bytes`. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. + fn write_int(&mut self, n: int); + + /// Write the result of passing n through `uint::to_str_bytes`. + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. + fn write_uint(&mut self, n: uint); + + /// Write a little-endian uint (number of bytes depends on system). + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. + fn write_le_uint(&mut self, n: uint); + + /// Write a little-endian int (number of bytes depends on system). + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. + fn write_le_int(&mut self, n: int); + + /// Write a big-endian uint (number of bytes depends on system). + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. + fn write_be_uint(&mut self, n: uint); + + /// Write a big-endian int (number of bytes depends on system). + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. + fn write_be_int(&mut self, n: int); + + /// Write a big-endian u64 (8 bytes). + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. + fn write_be_u64(&mut self, n: u64); + + /// Write a big-endian u32 (4 bytes). + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. + fn write_be_u32(&mut self, n: u32); + + /// Write a big-endian u16 (2 bytes). + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. + fn write_be_u16(&mut self, n: u16); + + /// Write a big-endian i64 (8 bytes). + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. + fn write_be_i64(&mut self, n: i64); + + /// Write a big-endian i32 (4 bytes). + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. + fn write_be_i32(&mut self, n: i32); + + /// Write a big-endian i16 (2 bytes). + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. + fn write_be_i16(&mut self, n: i16); + + /// Write a big-endian IEEE754 double-precision floating-point (8 bytes). + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. + fn write_be_f64(&mut self, f: f64); + + /// Write a big-endian IEEE754 single-precision floating-point (4 bytes). + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. + fn write_be_f32(&mut self, f: f32); + + /// Write a little-endian u64 (8 bytes). + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. + fn write_le_u64(&mut self, n: u64); + + /// Write a little-endian u32 (4 bytes). + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. + fn write_le_u32(&mut self, n: u32); + + /// Write a little-endian u16 (2 bytes). + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. + fn write_le_u16(&mut self, n: u16); + + /// Write a little-endian i64 (8 bytes). + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. + fn write_le_i64(&mut self, n: i64); + + /// Write a little-endian i32 (4 bytes). + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. + fn write_le_i32(&mut self, n: i32); + + /// Write a little-endian i16 (2 bytes). + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. + fn write_le_i16(&mut self, n: i16); + + /// Write a little-endian IEEE754 double-precision floating-point + /// (8 bytes). + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. + fn write_le_f64(&mut self, f: f64); + + /// Write a litten-endian IEEE754 single-precision floating-point + /// (4 bytes). + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. + fn write_le_f32(&mut self, f: f32); + + /// Write a u8 (1 byte). + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. + fn write_u8(&mut self, n: u8); + + /// Write a i8 (1 byte). + /// + /// # Failure + /// + /// Raises the `io_error` condition on error. + fn write_i8(&mut self, n: i8); +} diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs index b1227af5f4c33..e93e0c6fc6cc9 100644 --- a/src/libcore/rt/mod.rs +++ b/src/libcore/rt/mod.rs @@ -88,3 +88,96 @@ pub fn start(main: *u8, _argc: int, _argv: **c_char, _crate_map: *u8) -> int { fn rust_call_nullary_fn(f: *u8); } } + +/// Possible contexts in which Rust code may be executing. +/// Different runtime services are available depending on context. +#[deriving(Eq)] +pub enum RuntimeContext { + // Only default services, e.g. exchange heap + GlobalContext, + // The scheduler may be accessed + SchedulerContext, + // Full task services, e.g. local heap, unwinding + TaskContext, + // Running in an old-style task + OldTaskContext +} + +pub fn context() -> RuntimeContext { + + use task::rt::rust_task; + use self::sched::local_sched; + + // XXX: Hitting TLS twice to check if the scheduler exists + // then to check for the task is not good for perf + if unsafe { rust_try_get_task().is_not_null() } { + return OldTaskContext; + } else { + if local_sched::exists() { + let context = ::cell::empty_cell(); + do local_sched::borrow |sched| { + if sched.in_task_context() { + context.put_back(TaskContext); + } else { + context.put_back(SchedulerContext); + } + } + return context.take(); + } else { + return GlobalContext; + } + } + + pub extern { + #[rust_stack] + fn rust_try_get_task() -> *rust_task; + } +} + +#[test] +fn test_context() { + use unstable::run_in_bare_thread; + use self::sched::{local_sched, Task}; + use self::uvio::UvEventLoop; + use cell::Cell; + + assert!(context() == OldTaskContext); + do run_in_bare_thread { + assert!(context() == GlobalContext); + let mut sched = ~UvEventLoop::new_scheduler(); + let task = ~do Task::new(&mut sched.stack_pool) { + assert!(context() == TaskContext); + let sched = local_sched::take(); + do sched.deschedule_running_task_and_then() |task| { + assert!(context() == SchedulerContext); + let task = Cell(task); + do local_sched::borrow |sched| { + sched.task_queue.push_back(task.take()); + } + } + }; + sched.task_queue.push_back(task); + 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(); + } +} diff --git a/src/libcore/rt/rtio.rs b/src/libcore/rt/rtio.rs index 6a7c3970c0091..66eb79ba6ae4e 100644 --- a/src/libcore/rt/rtio.rs +++ b/src/libcore/rt/rtio.rs @@ -11,6 +11,8 @@ use option::*; use result::*; +use super::io::net::ip::IpAddr; + // XXX: ~object doesn't work currently so these are some placeholder // types to use instead pub type EventLoopObject = super::uvio::UvEventLoop; @@ -43,8 +45,3 @@ pub trait Stream { fn read(&mut self, buf: &mut [u8]) -> Result; fn write(&mut self, buf: &[u8]) -> Result<(), ()>; } - -pub enum IpAddr { - Ipv4(u8, u8, u8, u8, u16), - Ipv6 -} diff --git a/src/libcore/rt/sched/local.rs b/src/libcore/rt/sched/local_sched.rs similarity index 61% rename from src/libcore/rt/sched/local.rs rename to src/libcore/rt/sched/local_sched.rs index d800101111464..2ab50252ac69e 100644 --- a/src/libcore/rt/sched/local.rs +++ b/src/libcore/rt/sched/local_sched.rs @@ -10,11 +10,13 @@ //! Access to the thread-local Scheduler +use prelude::*; use ptr::mut_null; use libc::c_void; use cast::transmute; use super::Scheduler; +use super::super::rtio::IoFactoryObject; use tls = super::super::thread_local_storage; #[cfg(test)] use super::super::uvio::UvEventLoop; @@ -39,11 +41,31 @@ pub fn take() -> ~Scheduler { } } +/// Check whether there is a thread-local Scheduler attached to the running thread +pub fn exists() -> bool { + unsafe { + match maybe_tls_key() { + Some(key) => tls::get(key).is_not_null(), + None => false + } + } +} + +/// Borrow the thread-local scheduler from thread-local storage. +/// While the scheduler is borrowed it is not available in TLS. +pub fn borrow(f: &fn(&mut Scheduler)) { + let mut sched = take(); + f(sched); + put(sched); +} + /// Borrow a mutable reference to the thread-local Scheduler +/// /// # Safety Note +/// /// Because this leaves the Scheduler in thread-local storage it is possible /// For the Scheduler pointer to be aliased -pub unsafe fn borrow() -> &mut Scheduler { +pub unsafe fn unsafe_borrow() -> &mut Scheduler { unsafe { let key = tls_key(); let mut void_sched: *mut c_void = tls::get(key); @@ -59,11 +81,39 @@ pub unsafe fn borrow() -> &mut Scheduler { } } +pub unsafe fn unsafe_borrow_io() -> &mut IoFactoryObject { + unsafe { + let sched = unsafe_borrow(); + return sched.event_loop.io().unwrap(); + } +} + fn tls_key() -> tls::Key { + maybe_tls_key().get() +} + +fn maybe_tls_key() -> Option { unsafe { let key: *mut c_void = rust_get_sched_tls_key(); let key: &mut tls::Key = transmute(key); - return *key; + let key = *key; + // Check that the key has been initialized. + + // NB: This is a little racy because, while the key is + // initalized under a mutex and it's assumed to be initalized + // in the Scheduler ctor by any thread that needs to use it, + // we are not accessing the key under a mutex. Threads that + // are not using the new Scheduler but still *want to check* + // whether they are running under a new Scheduler may see a 0 + // value here that is in the process of being initialized in + // another thread. I think this is fine since the only action + // they could take if it was initialized would be to check the + // thread-local value and see that it's not set. + if key != 0 { + return Some(key); + } else { + return None; + } } } @@ -93,7 +143,7 @@ fn borrow_smoke_test() { let scheduler = ~UvEventLoop::new_scheduler(); put(scheduler); unsafe { - let _scheduler = borrow(); + let _scheduler = unsafe_borrow(); } let _scheduler = take(); } diff --git a/src/libcore/rt/sched/mod.rs b/src/libcore/rt/sched/mod.rs index f157e6a80e0c4..28946281628b1 100644 --- a/src/libcore/rt/sched/mod.rs +++ b/src/libcore/rt/sched/mod.rs @@ -14,15 +14,16 @@ use cast::transmute; use super::work_queue::WorkQueue; use super::stack::{StackPool, StackSegment}; -use super::rtio::{EventLoop, EventLoopObject, IoFactoryObject}; +use super::rtio::{EventLoop, EventLoopObject}; use super::context::Context; +use cell::Cell; #[cfg(test)] use super::uvio::UvEventLoop; #[cfg(test)] use unstable::run_in_bare_thread; #[cfg(test)] use int; -#[cfg(test)] use cell::Cell; -mod local; +// A more convenient name for external callers, e.g. `local_sched::take()` +pub mod local_sched; /// The Scheduler is responsible for coordinating execution of Tasks /// on a single thread. When the scheduler is running it is owned by @@ -57,13 +58,13 @@ impl ClosureConverter for UnsafeTaskReceiver { enum CleanupJob { DoNothing, - RescheduleTask(~Task), - RecycleTask(~Task), GiveTask(~Task, UnsafeTaskReceiver) } pub impl Scheduler { + fn in_task_context(&self) -> bool { self.current_task.is_some() } + fn new(event_loop: ~EventLoopObject) -> Scheduler { // Lazily initialize the global state, currently the scheduler TLS key @@ -90,47 +91,25 @@ pub impl Scheduler { assert!(!self.in_task_context()); // Give ownership of the scheduler (self) to the thread - local::put(self); - - let scheduler = unsafe { local::borrow() }; - fn run_scheduler_once() { - let scheduler = Scheduler::take_local(); - if scheduler.resume_task_from_queue() { - // Ok, a task ran. Nice! We'll do it again later - do Scheduler::borrow_local |scheduler| { - scheduler.event_loop.callback(run_scheduler_once); + local_sched::put(self); + + unsafe { + let scheduler = local_sched::unsafe_borrow(); + fn run_scheduler_once() { + let scheduler = local_sched::take(); + if scheduler.resume_task_from_queue() { + // Ok, a task ran. Nice! We'll do it again later + do local_sched::borrow |scheduler| { + scheduler.event_loop.callback(run_scheduler_once); + } } } - } - - scheduler.event_loop.callback(run_scheduler_once); - scheduler.event_loop.run(); - return local::take(); - } - - /// Get a mutable pointer to the thread-local I/O - /// # Safety Note - /// This allows other mutable aliases to the scheduler, both in the current - /// execution context and other execution contexts. - unsafe fn borrow_local_io() -> &mut IoFactoryObject { - unsafe { - let io = local::borrow().event_loop.io().unwrap(); - transmute::<&mut IoFactoryObject, &mut IoFactoryObject>(io) + scheduler.event_loop.callback(run_scheduler_once); + scheduler.event_loop.run(); } - } - - /// Borrow the thread-local scheduler from thread-local storage. - /// While the scheduler is borrowed it is not available in TLS. - fn borrow_local(f: &fn(&mut Scheduler)) { - let mut sched = local::take(); - f(sched); - local::put(sched); - } - /// Take ownership of the scheduler from thread local storage - fn take_local() -> ~Scheduler { - local::take() + return local_sched::take(); } // * Scheduler-context operations @@ -146,41 +125,12 @@ pub impl Scheduler { } None => { rtdebug!("no tasks in queue"); - local::put(self); + local_sched::put(self); return false; } } } - fn resume_task_immediately(~self, task: ~Task) { - let mut self = self; - assert!(!self.in_task_context()); - - rtdebug!("scheduling a task"); - - // Store the task in the scheduler so it can be grabbed later - self.current_task = Some(task); - self.enqueue_cleanup_job(DoNothing); - - local::put(self); - - // Take pointers to both the task and scheduler's saved registers. - let sched = unsafe { local::borrow() }; - let (sched_context, _, next_task_context) = sched.get_contexts(); - let next_task_context = next_task_context.unwrap(); - // Context switch to the task, restoring it's registers - // and saving the scheduler's - Context::swap(sched_context, next_task_context); - - let sched = unsafe { local::borrow() }; - // The running task should have passed ownership elsewhere - assert!(sched.current_task.is_none()); - - // Running tasks may have asked us to do some cleanup - sched.run_cleanup_job(); - } - - // * Task-context operations /// Called by a running task to end execution, after which it will @@ -191,17 +141,58 @@ pub impl Scheduler { rtdebug!("ending running task"); - let dead_task = self.current_task.swap_unwrap(); - self.enqueue_cleanup_job(RecycleTask(dead_task)); + do self.deschedule_running_task_and_then |dead_task| { + let dead_task = Cell(dead_task); + do local_sched::borrow |sched| { + dead_task.take().recycle(&mut sched.stack_pool); + } + } - local::put(self); + // Control never reaches here + } - let sched = unsafe { local::borrow() }; - let (sched_context, last_task_context, _) = sched.get_contexts(); - let last_task_context = last_task_context.unwrap(); - Context::swap(last_task_context, sched_context); + fn schedule_new_task(~self, task: ~Task) { + let mut self = self; + assert!(self.in_task_context()); - // Control never reaches here + do self.switch_running_tasks_and_then(task) |last_task| { + let last_task = Cell(last_task); + do local_sched::borrow |sched| { + sched.task_queue.push_front(last_task.take()); + } + } + } + + // Core scheduling ops + + fn resume_task_immediately(~self, task: ~Task) { + let mut self = self; + assert!(!self.in_task_context()); + + rtdebug!("scheduling a task"); + + // Store the task in the scheduler so it can be grabbed later + self.current_task = Some(task); + self.enqueue_cleanup_job(DoNothing); + + local_sched::put(self); + + // Take pointers to both the task and scheduler's saved registers. + unsafe { + let sched = local_sched::unsafe_borrow(); + let (sched_context, _, next_task_context) = sched.get_contexts(); + let next_task_context = next_task_context.unwrap(); + // Context switch to the task, restoring it's registers + // and saving the scheduler's + Context::swap(sched_context, next_task_context); + + let sched = local_sched::unsafe_borrow(); + // The running task should have passed ownership elsewhere + assert!(sched.current_task.is_none()); + + // Running tasks may have asked us to do some cleanup + sched.run_cleanup_job(); + } } /// Block a running task, context switch to the scheduler, then pass the @@ -223,47 +214,51 @@ pub impl Scheduler { let f_opaque = ClosureConverter::from_fn(f_fake_region); self.enqueue_cleanup_job(GiveTask(blocked_task, f_opaque)); - local::put(self); + local_sched::put(self); - let sched = unsafe { local::borrow() }; + let sched = unsafe { local_sched::unsafe_borrow() }; let (sched_context, last_task_context, _) = sched.get_contexts(); let last_task_context = last_task_context.unwrap(); Context::swap(last_task_context, sched_context); // We could be executing in a different thread now - let sched = unsafe { local::borrow() }; + let sched = unsafe { local_sched::unsafe_borrow() }; sched.run_cleanup_job(); } /// Switch directly to another task, without going through the scheduler. /// You would want to think hard about doing this, e.g. if there are /// pending I/O events it would be a bad idea. - fn resume_task_from_running_task_direct(~self, next_task: ~Task) { + fn switch_running_tasks_and_then(~self, next_task: ~Task, f: &fn(~Task)) { let mut self = self; assert!(self.in_task_context()); rtdebug!("switching tasks"); let old_running_task = self.current_task.swap_unwrap(); - self.enqueue_cleanup_job(RescheduleTask(old_running_task)); + let f_fake_region = unsafe { transmute::<&fn(~Task), &fn(~Task)>(f) }; + let f_opaque = ClosureConverter::from_fn(f_fake_region); + self.enqueue_cleanup_job(GiveTask(old_running_task, f_opaque)); self.current_task = Some(next_task); - local::put(self); + local_sched::put(self); - let sched = unsafe { local::borrow() }; - let (_, last_task_context, next_task_context) = sched.get_contexts(); - let last_task_context = last_task_context.unwrap(); - let next_task_context = next_task_context.unwrap(); - Context::swap(last_task_context, next_task_context); - - // We could be executing in a different thread now - let sched = unsafe { local::borrow() }; - sched.run_cleanup_job(); + unsafe { + let sched = local_sched::unsafe_borrow(); + let (_, last_task_context, next_task_context) = sched.get_contexts(); + let last_task_context = last_task_context.unwrap(); + let next_task_context = next_task_context.unwrap(); + Context::swap(last_task_context, next_task_context); + + // We could be executing in a different thread now + let sched = local_sched::unsafe_borrow(); + sched.run_cleanup_job(); + } } - // * Other stuff - fn in_task_context(&self) -> bool { self.current_task.is_some() } + + // * Other stuff fn enqueue_cleanup_job(&mut self, job: CleanupJob) { assert!(self.cleanup_job.is_none()); @@ -278,11 +273,6 @@ pub impl Scheduler { let cleanup_job = self.cleanup_job.swap_unwrap(); match cleanup_job { DoNothing => { } - RescheduleTask(task) => { - // NB: Pushing to the *front* of the queue - self.task_queue.push_front(task); - } - RecycleTask(task) => task.recycle(&mut self.stack_pool), GiveTask(task, f) => (f.to_fn())(task) } } @@ -300,8 +290,6 @@ pub impl Scheduler { Option<&'a mut Context>, Option<&'a mut Context>) { let last_task = match self.cleanup_job { - Some(RescheduleTask(~ref task)) | - Some(RecycleTask(~ref task)) | Some(GiveTask(~ref task, _)) => { Some(task) } @@ -358,12 +346,14 @@ pub impl Task { // This is the first code to execute after the initial // context switch to the task. The previous context may // have asked us to do some cleanup. - let sched = unsafe { local::borrow() }; - sched.run_cleanup_job(); + unsafe { + let sched = local_sched::unsafe_borrow(); + sched.run_cleanup_job(); + } start(); - let sched = Scheduler::take_local(); + let sched = local_sched::take(); sched.terminate_current_task(); }; return wrapper; @@ -415,7 +405,7 @@ fn test_several_tasks() { } #[test] -fn test_swap_tasks() { +fn test_swap_tasks_then() { do run_in_bare_thread { let mut count = 0; let count_ptr: *mut int = &mut count; @@ -423,12 +413,17 @@ fn test_swap_tasks() { let mut sched = ~UvEventLoop::new_scheduler(); let task1 = ~do Task::new(&mut sched.stack_pool) { unsafe { *count_ptr = *count_ptr + 1; } - let mut sched = Scheduler::take_local(); + let mut sched = local_sched::take(); let task2 = ~do Task::new(&mut sched.stack_pool) { unsafe { *count_ptr = *count_ptr + 1; } }; // Context switch directly to the new task - sched.resume_task_from_running_task_direct(task2); + do sched.switch_running_tasks_and_then(task2) |task1| { + let task1 = Cell(task1); + do local_sched::borrow |sched| { + sched.task_queue.push_front(task1.take()); + } + } unsafe { *count_ptr = *count_ptr + 1; } }; sched.task_queue.push_back(task1); @@ -455,7 +450,7 @@ fn test_run_a_lot_of_tasks_queued() { assert!(count == MAX); fn run_task(count_ptr: *mut int) { - do Scheduler::borrow_local |sched| { + do local_sched::borrow |sched| { let task = ~do Task::new(&mut sched.stack_pool) { unsafe { *count_ptr = *count_ptr + 1; @@ -470,49 +465,16 @@ fn test_run_a_lot_of_tasks_queued() { } } -#[bench] #[test] #[ignore(reason = "too much stack allocation")] -fn test_run_a_lot_of_tasks_direct() { - do run_in_bare_thread { - static MAX: int = 100000; - let mut count = 0; - let count_ptr: *mut int = &mut count; - - let mut sched = ~UvEventLoop::new_scheduler(); - - let start_task = ~do Task::new(&mut sched.stack_pool) { - run_task(count_ptr); - }; - sched.task_queue.push_back(start_task); - sched.run(); - - assert!(count == MAX); - - fn run_task(count_ptr: *mut int) { - let mut sched = Scheduler::take_local(); - let task = ~do Task::new(&mut sched.stack_pool) { - unsafe { - *count_ptr = *count_ptr + 1; - if *count_ptr != MAX { - run_task(count_ptr); - } - } - }; - // Context switch directly to the new task - sched.resume_task_from_running_task_direct(task); - }; - } -} - #[test] fn test_block_task() { do run_in_bare_thread { let mut sched = ~UvEventLoop::new_scheduler(); let task = ~do Task::new(&mut sched.stack_pool) { - let sched = Scheduler::take_local(); + let sched = local_sched::take(); assert!(sched.in_task_context()); do sched.deschedule_running_task_and_then() |task| { let task = Cell(task); - do Scheduler::borrow_local |sched| { + do local_sched::borrow |sched| { assert!(!sched.in_task_context()); sched.task_queue.push_back(task.take()); } diff --git a/src/libcore/rt/uv/net.rs b/src/libcore/rt/uv/net.rs index 4b9ff4504ddac..0dc1a4d86cbc9 100644 --- a/src/libcore/rt/uv/net.rs +++ b/src/libcore/rt/uv/net.rs @@ -17,7 +17,7 @@ use super::{Loop, Watcher, Request, UvError, Buf, Callback, NativeHandle, NullCa loop_from_watcher, status_to_maybe_uv_error, install_watcher_data, get_watcher_data, drop_watcher_data, vec_to_uv_buf, vec_from_uv_buf}; -use super::super::rtio::{IpAddr, Ipv4, Ipv6}; +use super::super::io::net::ip::{IpAddr, Ipv4, Ipv6}; #[cfg(test)] use unstable::run_in_bare_thread; diff --git a/src/libcore/rt/uvio.rs b/src/libcore/rt/uvio.rs index fe7b0a71dbfe6..ff5397398354a 100644 --- a/src/libcore/rt/uvio.rs +++ b/src/libcore/rt/uvio.rs @@ -11,12 +11,13 @@ use option::*; use result::*; +use super::io::net::ip::{IpAddr, Ipv4}; use super::uv::*; use super::rtio::*; use ops::Drop; use cell::{Cell, empty_cell}; use cast::transmute; -use super::sched::Scheduler; +use super::sched::{Scheduler, local_sched}; #[cfg(test)] use super::sched::Task; #[cfg(test)] use unstable::run_in_bare_thread; @@ -120,14 +121,14 @@ impl IoFactory for UvIoFactory { let result_cell = empty_cell(); let result_cell_ptr: *Cell> = &result_cell; - let scheduler = Scheduler::take_local(); + let scheduler = local_sched::take(); assert!(scheduler.in_task_context()); // Block this task and take ownership, switch to scheduler context do scheduler.deschedule_running_task_and_then |task| { rtdebug!("connect: entered scheduler context"); - do Scheduler::borrow_local |scheduler| { + do local_sched::borrow |scheduler| { assert!(!scheduler.in_task_context()); } let mut tcp_watcher = TcpWatcher::new(self.uv_loop()); @@ -149,7 +150,7 @@ impl IoFactory for UvIoFactory { unsafe { (*result_cell_ptr).put_back(maybe_stream); } // Context switch - let scheduler = Scheduler::take_local(); + let scheduler = local_sched::take(); scheduler.resume_task_immediately(task_cell.take()); } } @@ -194,7 +195,7 @@ impl TcpListener for UvTcpListener { let server_tcp_watcher = self.watcher(); - let scheduler = Scheduler::take_local(); + let scheduler = local_sched::take(); assert!(scheduler.in_task_context()); do scheduler.deschedule_running_task_and_then |task| { @@ -217,7 +218,7 @@ impl TcpListener for UvTcpListener { rtdebug!("resuming task from listen"); // Context switch - let scheduler = Scheduler::take_local(); + let scheduler = local_sched::take(); scheduler.resume_task_immediately(task_cell.take()); } } @@ -257,13 +258,13 @@ impl Stream for UvStream { let result_cell = empty_cell(); let result_cell_ptr: *Cell> = &result_cell; - let scheduler = Scheduler::take_local(); + let scheduler = local_sched::take(); assert!(scheduler.in_task_context()); let watcher = self.watcher(); let buf_ptr: *&mut [u8] = &buf; do scheduler.deschedule_running_task_and_then |task| { rtdebug!("read: entered scheduler context"); - do Scheduler::borrow_local |scheduler| { + do local_sched::borrow |scheduler| { assert!(!scheduler.in_task_context()); } let mut watcher = watcher; @@ -291,7 +292,7 @@ impl Stream for UvStream { unsafe { (*result_cell_ptr).put_back(result); } - let scheduler = Scheduler::take_local(); + let scheduler = local_sched::take(); scheduler.resume_task_immediately(task_cell.take()); } } @@ -303,7 +304,7 @@ impl Stream for UvStream { fn write(&mut self, buf: &[u8]) -> Result<(), ()> { let result_cell = empty_cell(); let result_cell_ptr: *Cell> = &result_cell; - let scheduler = Scheduler::take_local(); + let scheduler = local_sched::take(); assert!(scheduler.in_task_context()); let watcher = self.watcher(); let buf_ptr: *&[u8] = &buf; @@ -322,7 +323,7 @@ impl Stream for UvStream { unsafe { (*result_cell_ptr).put_back(result); } - let scheduler = Scheduler::take_local(); + let scheduler = local_sched::take(); scheduler.resume_task_immediately(task_cell.take()); } } @@ -338,7 +339,7 @@ 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 { Scheduler::borrow_local_io() }; + let io = unsafe { local_sched::unsafe_borrow_io() }; let addr = Ipv4(127, 0, 0, 1, 2926); let maybe_chan = io.connect(addr); assert!(maybe_chan.is_none()); @@ -356,25 +357,29 @@ fn test_simple_tcp_server_and_client() { let addr = Ipv4(127, 0, 0, 1, 2929); let client_task = ~do Task::new(&mut sched.stack_pool) { - let io = unsafe { Scheduler::borrow_local_io() }; - let mut stream = io.connect(addr).unwrap(); - stream.write([0, 1, 2, 3, 4, 5, 6, 7]); - stream.close(); + 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) { - let io = unsafe { Scheduler::borrow_local_io() }; - let mut listener = io.bind(addr).unwrap(); - let mut stream = listener.listen().unwrap(); - let mut buf = [0, .. 2048]; - let nread = stream.read(buf).unwrap(); - assert!(nread == 8); - for uint::range(0, nread) |i| { - rtdebug!("%u", buf[i] as uint); - assert!(buf[i] == i as u8); + 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 nread = stream.read(buf).unwrap(); + assert!(nread == 8); + for uint::range(0, nread) |i| { + rtdebug!("%u", buf[i] as uint); + assert!(buf[i] == i as u8); + } + stream.close(); + listener.close(); } - stream.close(); - listener.close(); }; // Start the server first so it listens before the client connects @@ -391,7 +396,7 @@ fn test_read_and_block() { let addr = Ipv4(127, 0, 0, 1, 2930); let client_task = ~do Task::new(&mut sched.stack_pool) { - let io = unsafe { Scheduler::borrow_local_io() }; + 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]); @@ -401,7 +406,7 @@ fn test_read_and_block() { }; let server_task = ~do Task::new(&mut sched.stack_pool) { - let io = unsafe { Scheduler::borrow_local_io() }; + let io = unsafe { local_sched::unsafe_borrow_io() }; let mut listener = io.bind(addr).unwrap(); let mut stream = listener.listen().unwrap(); let mut buf = [0, .. 2048]; @@ -419,13 +424,13 @@ fn test_read_and_block() { } reads += 1; - let scheduler = Scheduler::take_local(); + let scheduler = local_sched::take(); // Yield to the other task in hopes that it // will trigger a read callback while we are // not ready for it do scheduler.deschedule_running_task_and_then |task| { let task = Cell(task); - do Scheduler::borrow_local |scheduler| { + do local_sched::borrow |scheduler| { scheduler.task_queue.push_back(task.take()); } } @@ -452,7 +457,7 @@ fn test_read_read_read() { let addr = Ipv4(127, 0, 0, 1, 2931); let client_task = ~do Task::new(&mut sched.stack_pool) { - let io = unsafe { Scheduler::borrow_local_io() }; + 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; diff --git a/src/libcore/task/mod.rs b/src/libcore/task/mod.rs index 55546514e4fa5..a6c03638713ed 100644 --- a/src/libcore/task/mod.rs +++ b/src/libcore/task/mod.rs @@ -175,7 +175,7 @@ pub struct TaskOpts { // FIXME (#3724): Replace the 'consumed' bit with move mode on self pub struct TaskBuilder { opts: TaskOpts, - gen_body: @fn(v: ~fn()) -> ~fn(), + mut gen_body: Option<~fn(v: ~fn()) -> ~fn()>, can_not_copy: Option, mut consumed: bool, } @@ -188,7 +188,7 @@ pub struct TaskBuilder { pub fn task() -> TaskBuilder { TaskBuilder { opts: default_task_opts(), - gen_body: |body| body, // Identity function + gen_body: None, can_not_copy: None, mut consumed: false, } @@ -201,6 +201,7 @@ priv impl TaskBuilder { fail!(~"Cannot copy a task_builder"); // Fake move mode on self } self.consumed = true; + let gen_body = replace(&mut self.gen_body, None); let notify_chan = replace(&mut self.opts.notify_chan, None); TaskBuilder { opts: TaskOpts { @@ -209,7 +210,7 @@ priv impl TaskBuilder { notify_chan: notify_chan, sched: self.opts.sched }, - gen_body: self.gen_body, + gen_body: gen_body, can_not_copy: None, consumed: false } @@ -341,8 +342,23 @@ pub impl TaskBuilder { * generator by applying the task body which results from the * existing body generator to the new body generator. */ - fn add_wrapper(&self, wrapper: @fn(v: ~fn()) -> ~fn()) -> TaskBuilder { - let prev_gen_body = self.gen_body; + fn add_wrapper(&self, wrapper: ~fn(v: ~fn()) -> ~fn()) -> TaskBuilder { + let prev_gen_body = replace(&mut self.gen_body, None); + let prev_gen_body = match prev_gen_body { + Some(gen) => gen, + None => { + let f: ~fn(~fn()) -> ~fn() = |body| body; + f + } + }; + let prev_gen_body = Cell(prev_gen_body); + let next_gen_body = { + let f: ~fn(~fn()) -> ~fn() = |body| { + let prev_gen_body = prev_gen_body.take(); + wrapper(prev_gen_body(body)) + }; + f + }; let notify_chan = replace(&mut self.opts.notify_chan, None); TaskBuilder { opts: TaskOpts { @@ -351,7 +367,7 @@ pub impl TaskBuilder { notify_chan: notify_chan, sched: self.opts.sched }, - gen_body: |body| { wrapper(prev_gen_body(body)) }, + gen_body: Some(next_gen_body), can_not_copy: None, .. self.consume() } @@ -370,6 +386,7 @@ pub impl TaskBuilder { * must be greater than zero. */ fn spawn(&self, f: ~fn()) { + let gen_body = replace(&mut self.gen_body, None); let notify_chan = replace(&mut self.opts.notify_chan, None); let x = self.consume(); let opts = TaskOpts { @@ -378,7 +395,15 @@ pub impl TaskBuilder { notify_chan: notify_chan, sched: x.opts.sched }; - spawn::spawn_raw(opts, (x.gen_body)(f)); + let f = match gen_body { + Some(gen) => { + gen(f) + } + None => { + f + } + }; + spawn::spawn_raw(opts, f); } /// Runs a task, while transfering ownership of one argument to the child. fn spawn_with(&self, arg: A, f: ~fn(v: A)) { @@ -1201,3 +1226,12 @@ fn test_spawn_thread_on_demand() { port.recv(); } + +#[test] +fn test_simple_newsched_spawn() { + use rt::run_in_newsched_task; + + do run_in_newsched_task { + spawn(||()) + } +} diff --git a/src/libcore/task/spawn.rs b/src/libcore/task/spawn.rs index c71f7d26d40f1..118c4cc23125b 100644 --- a/src/libcore/task/spawn.rs +++ b/src/libcore/task/spawn.rs @@ -531,6 +531,34 @@ fn gen_child_taskgroup(linked: bool, supervised: bool) } pub fn spawn_raw(opts: TaskOpts, f: ~fn()) { + use rt::*; + + match context() { + OldTaskContext => { + spawn_raw_oldsched(opts, f) + } + TaskContext => { + spawn_raw_newsched(opts, f) + } + SchedulerContext => { + fail!(~"can't spawn from scheduler context") + } + GlobalContext => { + fail!(~"can't spawn from global context") + } + } +} + +fn spawn_raw_newsched(opts: TaskOpts, f: ~fn()) { + use rt::sched::*; + + let mut sched = local_sched::take(); + let task = ~Task::new(&mut sched.stack_pool, f); + sched.schedule_new_task(task); +} + +fn spawn_raw_oldsched(opts: TaskOpts, f: ~fn()) { + let (child_tg, ancestors, is_main) = gen_child_taskgroup(opts.linked, opts.supervised); diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index 475c030e8f250..a0db6f64f69fc 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -539,6 +539,11 @@ rust_get_task() { return rust_get_current_task(); } +extern "C" rust_task * +rust_try_get_task() { + return rust_try_get_current_task(); +} + extern "C" CDECL stk_seg * rust_get_stack_segment() { return rust_get_current_task()->stk; diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index f63e3f53a7caf..5a556ed2107df 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -47,6 +47,7 @@ rust_env_pairs rust_task_yield rust_task_is_unwinding rust_get_task +rust_try_get_task rust_get_stack_segment rust_log_str start_task