auto merge of #5960 : brson/rust/io, r=pcwalton

r?

This pull request is a grab bag of work on the new scheduler.

The most important commit here is where I [outline](https://github.com/brson/rust/blob/io/src/libcore/rt/io/mod.rs) a fairly complete I/O API, based on `Reader` and `Writer` types, as in the current `core::io` module. I've organized this version into a number of modules with declarations for Files, TCP, UDP, Unix sockets, blocking/non-blocking implementations, memory buffers, compression adapters. I'm trying to get this into shape to present on the mailing list.

This branch also wires up `spawn` to the new scheduler, and simplifies the core scheduler operations.
This commit is contained in:
bors 2013-04-19 15:57:50 -07:00
commit 6510fd9254
25 changed files with 1905 additions and 219 deletions

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use prelude::*;
use super::{Reader, Writer};
struct PortReader<P>;
impl<P: GenericPort<~[u8]>> PortReader<P> {
pub fn new(_port: P) -> PortReader<P> { fail!() }
}
impl<P: GenericPort<~[u8]>> Reader for PortReader<P> {
fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { fail!() }
fn eof(&mut self) -> bool { fail!() }
}
struct ChanWriter<C>;
impl<C: GenericChan<~[u8]>> ChanWriter<C> {
pub fn new(_chan: C) -> ChanWriter<C> { fail!() }
}
impl<C: GenericChan<~[u8]>> Writer for ChanWriter<C> {
pub fn write(&mut self, _buf: &[u8]) { fail!() }
pub fn flush(&mut self) { fail!() }
}
struct ReaderPort<R>;
impl<R: Reader> ReaderPort<R> {
pub fn new(_reader: R) -> ReaderPort<R> { fail!() }
}
impl<R: Reader> GenericPort<~[u8]> for ReaderPort<R> {
fn recv(&self) -> ~[u8] { fail!() }
fn try_recv(&self) -> Option<~[u8]> { fail!() }
}
struct WriterChan<W>;
impl<W: Writer> WriterChan<W> {
pub fn new(_writer: W) -> WriterChan<W> { fail!() }
}
impl<W: Writer> GenericChan<~[u8]> for WriterChan<W> {
fn send(&self, _x: ~[u8]) { fail!() }
}

View file

@ -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<P: PathLike>(_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<P: PathLike>(_path: &P,
_mode: FileMode,
_access: FileAccess
) -> Result<FileStream, IoError> {
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<uint> {
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());
}

121
src/libcore/rt/io/flate.rs Normal file
View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! 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<W> {
inner_writer: W
}
impl<W: Writer> DeflateWriter<W> {
pub fn new(inner_writer: W) -> DeflateWriter<W> {
DeflateWriter {
inner_writer: inner_writer
}
}
}
impl<W: Writer> Writer for DeflateWriter<W> {
fn write(&mut self, _buf: &[u8]) { fail!() }
fn flush(&mut self) { fail!() }
}
impl<W: Writer> Decorator<W> for DeflateWriter<W> {
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<R> {
inner_reader: R
}
impl<R: Reader> InflateReader<R> {
pub fn new(inner_reader: R) -> InflateReader<R> {
InflateReader {
inner_reader: inner_reader
}
}
}
impl<R: Reader> Reader for InflateReader<R> {
fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { fail!() }
fn eof(&mut self) -> bool { fail!() }
}
impl<R: Reader> Decorator<R> for InflateReader<R> {
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);
}
}

166
src/libcore/rt/io/mem.rs Normal file
View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! 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<uint> { 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<uint> { 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!() }
}

42
src/libcore/rt/io/misc.rs Normal file
View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use path::*;
pub trait PathLike {
fn path_as_str<T>(&self, f: &fn(&str) -> T) -> T;
}
impl<'self> PathLike for &'self str {
fn path_as_str<T>(&self, f: &fn(&str) -> T) -> T {
f(*self)
}
}
impl PathLike for Path {
fn path_as_str<T>(&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));
}
}

View file

@ -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;
// 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.
condition! {
stream_error: super::StreamError -> bool;
/// 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 { }
}
}
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.
/// 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 `I/O` operations on error.
condition! {
io_error: super::IoError -> ();
}
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<uint>;
/// 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<T> {
/// 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;
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! 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<uint> { 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<uint> { 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!() }
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! 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 {
}
}
}

View file

@ -0,0 +1,15 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
pub enum IpAddr {
Ipv4(u8, u8, u8, u8, u16),
Ipv6
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
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<S> {
/// 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<S>;
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use prelude::*;
use super::*;
use super::super::*;
use super::ip::IpAddr;
pub struct TcpStream;
impl TcpStream {
pub fn connect(_addr: IpAddr) -> Result<TcpStream, IoError> {
fail!()
}
}
impl Reader for TcpStream {
fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { 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<TcpStream> for TcpListener {
fn accept(&mut self) -> Option<TcpStream> { fail!() }
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use prelude::*;
use super::*;
use super::super::*;
use super::ip::IpAddr;
pub struct UdpStream;
impl UdpStream {
pub fn connect(_addr: IpAddr) -> Result<UdpStream, IoError> {
fail!()
}
}
impl Reader for UdpStream {
fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { 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<UdpStream> for UdpListener {
fn accept(&mut self) -> Option<UdpStream> { fail!() }
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use prelude::*;
use super::*;
use super::super::*;
use super::super::misc::PathLike;
pub struct UnixStream;
impl UnixStream {
pub fn connect<P: PathLike>(_path: &P) -> Result<UnixStream, IoError> {
fail!()
}
}
impl Reader for UnixStream {
fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { 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<P: PathLike>(_path: &P) -> UnixListener {
fail!()
}
}
impl Listener<UnixStream> for UnixListener {
fn accept(&mut self) -> Option<UnixStream> { fail!() }
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
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<uint> { 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!() }
}

469
src/libcore/rt/io/util.rs Normal file
View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! 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);
}

View file

@ -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();
}
}

View file

@ -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<uint, ()>;
fn write(&mut self, buf: &[u8]) -> Result<(), ()>;
}
pub enum IpAddr {
Ipv4(u8, u8, u8, u8, u16),
Ipv6
}

View file

@ -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<tls::Key> {
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();
}

View file

@ -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);
local_sched::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);
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();
}
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)
}
}
/// 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,19 +141,60 @@ pub impl Scheduler {
rtdebug!("ending running task");
let dead_task = self.current_task.swap_unwrap();
self.enqueue_cleanup_job(RecycleTask(dead_task));
local::put(self);
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);
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);
}
}
// Control never reaches here
}
fn schedule_new_task(~self, task: ~Task) {
let mut self = self;
assert!(self.in_task_context());
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
/// blocked task to a closure.
///
@ -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);
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 = unsafe { local::borrow() };
sched.run_cleanup_job();
// 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());
}

View file

@ -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;

View file

@ -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<Option<~StreamObject>> = &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<uint, ()>> = &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<(), ()>> = &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;

View file

@ -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<util::NonCopyable>,
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<A:Owned>(&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(||())
}
}

View file

@ -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);

View file

@ -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;

View file

@ -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