From 6aadc9d18856f8e7ea8038e2c4b2ba0f9507e26a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 12 Dec 2013 17:54:53 -0800 Subject: native: Introduce libnative This commit introduces a new crate called "native" which will be the crate that implements the 1:1 runtime of rust. This currently entails having an implementation of std::rt::Runtime inside of libnative as well as moving all of the native I/O implementations to libnative. The current snag is that the start lang item must currently be defined in libnative in order to start running, but this will change in the future. Cool fact about this crate, there are no extra features that are enabled. Note that this commit does not include any makefile support necessary for building libnative, that's all coming in a later commit. --- src/libnative/io/file.rs | 958 ++++++++++++++++++++++++++++++++++++++++++++ src/libnative/io/mod.rs | 225 +++++++++++ src/libnative/io/process.rs | 652 ++++++++++++++++++++++++++++++ src/libnative/lib.rs | 61 +++ src/libnative/task.rs | 257 ++++++++++++ 5 files changed, 2153 insertions(+) create mode 100644 src/libnative/io/file.rs create mode 100644 src/libnative/io/mod.rs create mode 100644 src/libnative/io/process.rs create mode 100644 src/libnative/lib.rs create mode 100644 src/libnative/task.rs (limited to 'src/libnative') diff --git a/src/libnative/io/file.rs b/src/libnative/io/file.rs new file mode 100644 index 00000000000..eaa4403f7bf --- /dev/null +++ b/src/libnative/io/file.rs @@ -0,0 +1,958 @@ +// 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 std::c_str::CString; +use std::io::IoError; +use std::io; +use std::libc::c_int; +use std::libc; +use std::os; +use std::rt::rtio; +use std::unstable::intrinsics; +use std::vec; + +use super::IoResult; + +#[cfg(windows)] use std::os::win32::{as_utf16_p, fill_utf16_buf_and_decode}; +#[cfg(windows)] use std::ptr; +#[cfg(windows)] use std::str; + +fn keep_going(data: &[u8], f: |*u8, uint| -> i64) -> i64 { + #[cfg(windows)] static eintr: int = 0; // doesn't matter + #[cfg(not(windows))] static eintr: int = libc::EINTR as int; + + let origamt = data.len(); + let mut data = data.as_ptr(); + let mut amt = origamt; + while amt > 0 { + let mut ret; + loop { + ret = f(data, amt); + if cfg!(not(windows)) { break } // windows has no eintr + // if we get an eintr, then try again + if ret != -1 || os::errno() as int != eintr { break } + } + if ret == 0 { + break + } else if ret != -1 { + amt -= ret as uint; + data = unsafe { data.offset(ret as int) }; + } else { + return ret; + } + } + return (origamt - amt) as i64; +} + +pub type fd_t = libc::c_int; + +pub struct FileDesc { + priv fd: fd_t, + priv close_on_drop: bool, +} + +impl FileDesc { + /// Create a `FileDesc` from an open C file descriptor. + /// + /// The `FileDesc` will take ownership of the specified file descriptor and + /// close it upon destruction if the `close_on_drop` flag is true, otherwise + /// it will not close the file descriptor when this `FileDesc` is dropped. + /// + /// Note that all I/O operations done on this object will be *blocking*, but + /// they do not require the runtime to be active. + pub fn new(fd: fd_t, close_on_drop: bool) -> FileDesc { + FileDesc { fd: fd, close_on_drop: close_on_drop } + } + + fn inner_read(&mut self, buf: &mut [u8]) -> Result { + #[cfg(windows)] type rlen = libc::c_uint; + #[cfg(not(windows))] type rlen = libc::size_t; + let ret = keep_going(buf, |buf, len| { + unsafe { + libc::read(self.fd, buf as *mut libc::c_void, len as rlen) as i64 + } + }); + if ret == 0 { + Err(io::standard_error(io::EndOfFile)) + } else if ret < 0 { + Err(super::last_error()) + } else { + Ok(ret as uint) + } + } + fn inner_write(&mut self, buf: &[u8]) -> Result<(), IoError> { + #[cfg(windows)] type wlen = libc::c_uint; + #[cfg(not(windows))] type wlen = libc::size_t; + let ret = keep_going(buf, |buf, len| { + unsafe { + libc::write(self.fd, buf as *libc::c_void, len as wlen) as i64 + } + }); + if ret < 0 { + Err(super::last_error()) + } else { + Ok(()) + } + } +} + +impl io::Reader for FileDesc { + fn read(&mut self, buf: &mut [u8]) -> Option { + match self.inner_read(buf) { Ok(n) => Some(n), Err(..) => None } + } + fn eof(&mut self) -> bool { false } +} + +impl io::Writer for FileDesc { + fn write(&mut self, buf: &[u8]) { + self.inner_write(buf); + } +} + +impl rtio::RtioFileStream for FileDesc { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner_read(buf).map(|i| i as int) + } + fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { + self.inner_write(buf) + } + fn pread(&mut self, buf: &mut [u8], offset: u64) -> Result { + return os_pread(self.fd, buf.as_ptr(), buf.len(), offset); + + #[cfg(windows)] + fn os_pread(fd: c_int, buf: *u8, amt: uint, offset: u64) -> IoResult { + unsafe { + let mut overlap: libc::OVERLAPPED = intrinsics::init(); + let handle = libc::get_osfhandle(fd) as libc::HANDLE; + let mut bytes_read = 0; + overlap.Offset = offset as libc::DWORD; + overlap.OffsetHigh = (offset >> 32) as libc::DWORD; + + match libc::ReadFile(handle, buf as libc::LPVOID, + amt as libc::DWORD, + &mut bytes_read, &mut overlap) { + 0 => Err(super::last_error()), + _ => Ok(bytes_read as int) + } + } + } + + #[cfg(unix)] + fn os_pread(fd: c_int, buf: *u8, amt: uint, offset: u64) -> IoResult { + match unsafe { + libc::pread(fd, buf as *libc::c_void, amt as libc::size_t, + offset as libc::off_t) + } { + -1 => Err(super::last_error()), + n => Ok(n as int) + } + } + } + fn pwrite(&mut self, buf: &[u8], offset: u64) -> Result<(), IoError> { + return os_pwrite(self.fd, buf.as_ptr(), buf.len(), offset); + + #[cfg(windows)] + fn os_pwrite(fd: c_int, buf: *u8, amt: uint, offset: u64) -> IoResult<()> { + unsafe { + let mut overlap: libc::OVERLAPPED = intrinsics::init(); + let handle = libc::get_osfhandle(fd) as libc::HANDLE; + overlap.Offset = offset as libc::DWORD; + overlap.OffsetHigh = (offset >> 32) as libc::DWORD; + + match libc::WriteFile(handle, buf as libc::LPVOID, + amt as libc::DWORD, + ptr::mut_null(), &mut overlap) { + 0 => Err(super::last_error()), + _ => Ok(()), + } + } + } + + #[cfg(unix)] + fn os_pwrite(fd: c_int, buf: *u8, amt: uint, offset: u64) -> IoResult<()> { + super::mkerr_libc(unsafe { + libc::pwrite(fd, buf as *libc::c_void, amt as libc::size_t, + offset as libc::off_t) + } as c_int) + } + } + #[cfg(windows)] + fn seek(&mut self, pos: i64, style: io::SeekStyle) -> Result { + let whence = match style { + io::SeekSet => libc::FILE_BEGIN, + io::SeekEnd => libc::FILE_END, + io::SeekCur => libc::FILE_CURRENT, + }; + unsafe { + let handle = libc::get_osfhandle(self.fd) as libc::HANDLE; + let mut newpos = 0; + match libc::SetFilePointerEx(handle, pos, &mut newpos, whence) { + 0 => Err(super::last_error()), + _ => Ok(newpos as u64), + } + } + } + #[cfg(unix)] + fn seek(&mut self, pos: i64, whence: io::SeekStyle) -> Result { + let whence = match whence { + io::SeekSet => libc::SEEK_SET, + io::SeekEnd => libc::SEEK_END, + io::SeekCur => libc::SEEK_CUR, + }; + let n = unsafe { libc::lseek(self.fd, pos as libc::off_t, whence) }; + if n < 0 { + Err(super::last_error()) + } else { + Ok(n as u64) + } + } + fn tell(&self) -> Result { + let n = unsafe { libc::lseek(self.fd, 0, libc::SEEK_CUR) }; + if n < 0 { + Err(super::last_error()) + } else { + Ok(n as u64) + } + } + fn fsync(&mut self) -> Result<(), IoError> { + return os_fsync(self.fd); + + #[cfg(windows)] + fn os_fsync(fd: c_int) -> IoResult<()> { + super::mkerr_winbool(unsafe { + let handle = libc::get_osfhandle(fd); + libc::FlushFileBuffers(handle as libc::HANDLE) + }) + } + #[cfg(unix)] + fn os_fsync(fd: c_int) -> IoResult<()> { + super::mkerr_libc(unsafe { libc::fsync(fd) }) + } + } + #[cfg(windows)] + fn datasync(&mut self) -> Result<(), IoError> { return self.fsync(); } + + #[cfg(not(windows))] + fn datasync(&mut self) -> Result<(), IoError> { + return super::mkerr_libc(os_datasync(self.fd)); + + #[cfg(target_os = "macos")] + fn os_datasync(fd: c_int) -> c_int { + unsafe { libc::fcntl(fd, libc::F_FULLFSYNC) } + } + #[cfg(target_os = "linux")] + fn os_datasync(fd: c_int) -> c_int { unsafe { libc::fdatasync(fd) } } + #[cfg(not(target_os = "macos"), not(target_os = "linux"))] + fn os_datasync(fd: c_int) -> c_int { unsafe { libc::fsync(fd) } } + } + + #[cfg(windows)] + fn truncate(&mut self, offset: i64) -> Result<(), IoError> { + let orig_pos = match self.tell() { Ok(i) => i, Err(e) => return Err(e) }; + match self.seek(offset, io::SeekSet) { + Ok(_) => {}, Err(e) => return Err(e), + }; + let ret = unsafe { + let handle = libc::get_osfhandle(self.fd) as libc::HANDLE; + match libc::SetEndOfFile(handle) { + 0 => Err(super::last_error()), + _ => Ok(()) + } + }; + self.seek(orig_pos as i64, io::SeekSet); + return ret; + } + #[cfg(unix)] + fn truncate(&mut self, offset: i64) -> Result<(), IoError> { + super::mkerr_libc(unsafe { + libc::ftruncate(self.fd, offset as libc::off_t) + }) + } +} + +impl rtio::RtioPipe for FileDesc { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner_read(buf) + } + fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { + self.inner_write(buf) + } +} + +impl rtio::RtioTTY for FileDesc { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner_read(buf) + } + fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { + self.inner_write(buf) + } + fn set_raw(&mut self, _raw: bool) -> Result<(), IoError> { + Err(super::unimpl()) + } + fn get_winsize(&mut self) -> Result<(int, int), IoError> { + Err(super::unimpl()) + } + fn isatty(&self) -> bool { false } +} + +impl Drop for FileDesc { + fn drop(&mut self) { + // closing stdio file handles makes no sense, so never do it + if self.close_on_drop && self.fd > libc::STDERR_FILENO { + unsafe { libc::close(self.fd); } + } + } +} + +pub struct CFile { + priv file: *libc::FILE, + priv fd: FileDesc, +} + +impl CFile { + /// Create a `CFile` from an open `FILE` pointer. + /// + /// The `CFile` takes ownership of the `FILE` pointer and will close it upon + /// destruction. + pub fn new(file: *libc::FILE) -> CFile { + CFile { + file: file, + fd: FileDesc::new(unsafe { libc::fileno(file) }, false) + } + } + + pub fn flush(&mut self) -> Result<(), IoError> { + super::mkerr_libc(unsafe { libc::fflush(self.file) }) + } +} + +impl rtio::RtioFileStream for CFile { + fn read(&mut self, buf: &mut [u8]) -> Result { + let ret = keep_going(buf, |buf, len| { + unsafe { + libc::fread(buf as *mut libc::c_void, 1, len as libc::size_t, + self.file) as i64 + } + }); + if ret == 0 { + Err(io::standard_error(io::EndOfFile)) + } else if ret < 0 { + Err(super::last_error()) + } else { + Ok(ret as int) + } + } + + fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { + let ret = keep_going(buf, |buf, len| { + unsafe { + libc::fwrite(buf as *libc::c_void, 1, len as libc::size_t, + self.file) as i64 + } + }); + if ret < 0 { + Err(super::last_error()) + } else { + Ok(()) + } + } + + fn pread(&mut self, buf: &mut [u8], offset: u64) -> Result { + self.flush(); + self.fd.pread(buf, offset) + } + fn pwrite(&mut self, buf: &[u8], offset: u64) -> Result<(), IoError> { + self.flush(); + self.fd.pwrite(buf, offset) + } + fn seek(&mut self, pos: i64, style: io::SeekStyle) -> Result { + let whence = match style { + io::SeekSet => libc::SEEK_SET, + io::SeekEnd => libc::SEEK_END, + io::SeekCur => libc::SEEK_CUR, + }; + let n = unsafe { libc::fseek(self.file, pos as libc::c_long, whence) }; + if n < 0 { + Err(super::last_error()) + } else { + Ok(n as u64) + } + } + fn tell(&self) -> Result { + let ret = unsafe { libc::ftell(self.file) }; + if ret < 0 { + Err(super::last_error()) + } else { + Ok(ret as u64) + } + } + fn fsync(&mut self) -> Result<(), IoError> { + self.flush(); + self.fd.fsync() + } + fn datasync(&mut self) -> Result<(), IoError> { + self.flush(); + self.fd.fsync() + } + fn truncate(&mut self, offset: i64) -> Result<(), IoError> { + self.flush(); + self.fd.truncate(offset) + } +} + +impl Drop for CFile { + fn drop(&mut self) { + unsafe { libc::fclose(self.file); } + } +} + +pub fn open(path: &CString, fm: io::FileMode, fa: io::FileAccess) + -> IoResult { + let flags = match fm { + io::Open => 0, + io::Append => libc::O_APPEND, + io::Truncate => libc::O_TRUNC, + }; + // Opening with a write permission must silently create the file. + let (flags, mode) = match fa { + io::Read => (flags | libc::O_RDONLY, 0), + io::Write => (flags | libc::O_WRONLY | libc::O_CREAT, + libc::S_IRUSR | libc::S_IWUSR), + io::ReadWrite => (flags | libc::O_RDWR | libc::O_CREAT, + libc::S_IRUSR | libc::S_IWUSR), + }; + + return match os_open(path, flags, mode) { + -1 => Err(super::last_error()), + fd => Ok(FileDesc::new(fd, true)), + }; + + #[cfg(windows)] + fn os_open(path: &CString, flags: c_int, mode: c_int) -> c_int { + as_utf16_p(path.as_str().unwrap(), |path| { + unsafe { libc::wopen(path, flags, mode) } + }) + } + + #[cfg(unix)] + fn os_open(path: &CString, flags: c_int, mode: c_int) -> c_int { + unsafe { libc::open(path.with_ref(|p| p), flags, mode) } + } +} + +pub fn mkdir(p: &CString, mode: io::FilePermission) -> IoResult<()> { + return os_mkdir(p, mode as c_int); + + #[cfg(windows)] + fn os_mkdir(p: &CString, _mode: c_int) -> IoResult<()> { + super::mkerr_winbool(unsafe { + // FIXME: turn mode into something useful? #2623 + as_utf16_p(p.as_str().unwrap(), |buf| { + libc::CreateDirectoryW(buf, ptr::mut_null()) + }) + }) + } + + #[cfg(unix)] + fn os_mkdir(p: &CString, mode: c_int) -> IoResult<()> { + super::mkerr_libc(unsafe { + libc::mkdir(p.with_ref(|p| p), mode as libc::mode_t) + }) + } +} + +pub fn readdir(p: &CString) -> IoResult<~[Path]> { + fn prune(root: &CString, dirs: ~[Path]) -> ~[Path] { + let root = unsafe { CString::new(root.with_ref(|p| p), false) }; + let root = Path::new(root); + + dirs.move_iter().filter(|path| { + path.as_vec() != bytes!(".") && path.as_vec() != bytes!("..") + }).map(|path| root.join(path)).collect() + } + + unsafe { + #[cfg(not(windows))] + unsafe fn get_list(p: &CString) -> IoResult<~[Path]> { + use std::libc::{dirent_t}; + use std::libc::{opendir, readdir, closedir}; + extern { + fn rust_list_dir_val(ptr: *dirent_t) -> *libc::c_char; + } + debug!("os::list_dir -- BEFORE OPENDIR"); + + let dir_ptr = p.with_ref(|buf| opendir(buf)); + + if (dir_ptr as uint != 0) { + let mut paths = ~[]; + debug!("os::list_dir -- opendir() SUCCESS"); + let mut entry_ptr = readdir(dir_ptr); + while (entry_ptr as uint != 0) { + let cstr = CString::new(rust_list_dir_val(entry_ptr), false); + paths.push(Path::new(cstr)); + entry_ptr = readdir(dir_ptr); + } + closedir(dir_ptr); + Ok(paths) + } else { + Err(super::last_error()) + } + } + + #[cfg(windows)] + unsafe fn get_list(p: &CString) -> IoResult<~[Path]> { + use std::libc::consts::os::extra::INVALID_HANDLE_VALUE; + use std::libc::{wcslen, free}; + use std::libc::funcs::extra::kernel32::{ + FindFirstFileW, + FindNextFileW, + FindClose, + }; + use std::libc::types::os::arch::extra::HANDLE; + use os::win32::{ + as_utf16_p + }; + use rt::global_heap::malloc_raw; + + #[nolink] + extern { + fn rust_list_dir_wfd_size() -> libc::size_t; + fn rust_list_dir_wfd_fp_buf(wfd: *libc::c_void) -> *u16; + } + let p = CString::new(p.with_ref(|p| p), false); + let p = Path::new(p); + let star = p.join("*"); + as_utf16_p(star.as_str().unwrap(), |path_ptr| { + let wfd_ptr = malloc_raw(rust_list_dir_wfd_size() as uint); + let find_handle = FindFirstFileW(path_ptr, wfd_ptr as HANDLE); + if find_handle as libc::c_int != INVALID_HANDLE_VALUE { + let mut paths = ~[]; + let mut more_files = 1 as libc::c_int; + while more_files != 0 { + let fp_buf = rust_list_dir_wfd_fp_buf(wfd_ptr); + if fp_buf as uint == 0 { + fail!("os::list_dir() failure: got null ptr from wfd"); + } + else { + let fp_vec = vec::from_buf( + fp_buf, wcslen(fp_buf) as uint); + let fp_str = str::from_utf16(fp_vec); + paths.push(Path::new(fp_str)); + } + more_files = FindNextFileW(find_handle, wfd_ptr as HANDLE); + } + FindClose(find_handle); + free(wfd_ptr); + Ok(paths) + } else { + Err(super::last_error()) + } + }) + } + + get_list(p).map(|paths| prune(p, paths)) + } +} + +pub fn unlink(p: &CString) -> IoResult<()> { + return os_unlink(p); + + #[cfg(windows)] + fn os_unlink(p: &CString) -> IoResult<()> { + super::mkerr_winbool(unsafe { + as_utf16_p(p.as_str().unwrap(), |buf| { + libc::DeleteFileW(buf) + }) + }) + } + + #[cfg(unix)] + fn os_unlink(p: &CString) -> IoResult<()> { + super::mkerr_libc(unsafe { libc::unlink(p.with_ref(|p| p)) }) + } +} + +pub fn rename(old: &CString, new: &CString) -> IoResult<()> { + return os_rename(old, new); + + #[cfg(windows)] + fn os_rename(old: &CString, new: &CString) -> IoResult<()> { + super::mkerr_winbool(unsafe { + as_utf16_p(old.as_str().unwrap(), |old| { + as_utf16_p(new.as_str().unwrap(), |new| { + libc::MoveFileExW(old, new, libc::MOVEFILE_REPLACE_EXISTING) + }) + }) + }) + } + + #[cfg(unix)] + fn os_rename(old: &CString, new: &CString) -> IoResult<()> { + super::mkerr_libc(unsafe { + libc::rename(old.with_ref(|p| p), new.with_ref(|p| p)) + }) + } +} + +pub fn chmod(p: &CString, mode: io::FilePermission) -> IoResult<()> { + return super::mkerr_libc(os_chmod(p, mode as c_int)); + + #[cfg(windows)] + fn os_chmod(p: &CString, mode: c_int) -> c_int { + unsafe { + as_utf16_p(p.as_str().unwrap(), |p| libc::wchmod(p, mode)) + } + } + + #[cfg(unix)] + fn os_chmod(p: &CString, mode: c_int) -> c_int { + unsafe { libc::chmod(p.with_ref(|p| p), mode as libc::mode_t) } + } +} + +pub fn rmdir(p: &CString) -> IoResult<()> { + return super::mkerr_libc(os_rmdir(p)); + + #[cfg(windows)] + fn os_rmdir(p: &CString) -> c_int { + unsafe { + as_utf16_p(p.as_str().unwrap(), |p| libc::wrmdir(p)) + } + } + + #[cfg(unix)] + fn os_rmdir(p: &CString) -> c_int { + unsafe { libc::rmdir(p.with_ref(|p| p)) } + } +} + +pub fn chown(p: &CString, uid: int, gid: int) -> IoResult<()> { + return super::mkerr_libc(os_chown(p, uid, gid)); + + // libuv has this as a no-op, so seems like this should as well? + #[cfg(windows)] + fn os_chown(_p: &CString, _uid: int, _gid: int) -> c_int { 0 } + + #[cfg(unix)] + fn os_chown(p: &CString, uid: int, gid: int) -> c_int { + unsafe { + libc::chown(p.with_ref(|p| p), uid as libc::uid_t, + gid as libc::gid_t) + } + } +} + +pub fn readlink(p: &CString) -> IoResult { + return os_readlink(p); + + // XXX: I have a feeling that this reads intermediate symlinks as well. + #[cfg(windows)] + fn os_readlink(p: &CString) -> IoResult { + let handle = unsafe { + as_utf16_p(p.as_str().unwrap(), |p| { + libc::CreateFileW(p, + libc::GENERIC_READ, + libc::FILE_SHARE_READ, + ptr::mut_null(), + libc::OPEN_EXISTING, + libc::FILE_ATTRIBUTE_NORMAL, + ptr::mut_null()) + }) + }; + if handle == ptr::mut_null() { return Err(super::last_error()) } + let ret = fill_utf16_buf_and_decode(|buf, sz| { + unsafe { + libc::GetFinalPathNameByHandleW(handle, buf as *u16, sz, + libc::VOLUME_NAME_NT) + } + }); + let ret = match ret { + Some(s) => Ok(Path::new(s)), + None => Err(super::last_error()), + }; + unsafe { libc::CloseHandle(handle) }; + return ret; + + } + + #[cfg(unix)] + fn os_readlink(p: &CString) -> IoResult { + let p = p.with_ref(|p| p); + let mut len = unsafe { libc::pathconf(p, libc::_PC_NAME_MAX) }; + if len == -1 { + len = 1024; // XXX: read PATH_MAX from C ffi? + } + let mut buf = vec::with_capacity::(len as uint); + match unsafe { + libc::readlink(p, buf.as_ptr() as *mut libc::c_char, + len as libc::size_t) + } { + -1 => Err(super::last_error()), + n => { + assert!(n > 0); + unsafe { buf.set_len(n as uint); } + Ok(Path::new(buf)) + } + } + } +} + +pub fn symlink(src: &CString, dst: &CString) -> IoResult<()> { + return os_symlink(src, dst); + + #[cfg(windows)] + fn os_symlink(src: &CString, dst: &CString) -> IoResult<()> { + super::mkerr_winbool(as_utf16_p(src.as_str().unwrap(), |src| { + as_utf16_p(dst.as_str().unwrap(), |dst| { + unsafe { libc::CreateSymbolicLinkW(dst, src, 0) } + }) + })) + } + + #[cfg(unix)] + fn os_symlink(src: &CString, dst: &CString) -> IoResult<()> { + super::mkerr_libc(unsafe { + libc::symlink(src.with_ref(|p| p), dst.with_ref(|p| p)) + }) + } +} + +pub fn link(src: &CString, dst: &CString) -> IoResult<()> { + return os_link(src, dst); + + #[cfg(windows)] + fn os_link(src: &CString, dst: &CString) -> IoResult<()> { + super::mkerr_winbool(as_utf16_p(src.as_str().unwrap(), |src| { + as_utf16_p(dst.as_str().unwrap(), |dst| { + unsafe { libc::CreateHardLinkW(dst, src, ptr::mut_null()) } + }) + })) + } + + #[cfg(unix)] + fn os_link(src: &CString, dst: &CString) -> IoResult<()> { + super::mkerr_libc(unsafe { + libc::link(src.with_ref(|p| p), dst.with_ref(|p| p)) + }) + } +} + +#[cfg(windows)] +fn mkstat(stat: &libc::stat, path: &CString) -> io::FileStat { + let path = unsafe { CString::new(path.with_ref(|p| p), false) }; + let kind = match (stat.st_mode as c_int) & libc::S_IFMT { + libc::S_IFREG => io::TypeFile, + libc::S_IFDIR => io::TypeDirectory, + libc::S_IFIFO => io::TypeNamedPipe, + libc::S_IFBLK => io::TypeBlockSpecial, + libc::S_IFLNK => io::TypeSymlink, + _ => io::TypeUnknown, + }; + + io::FileStat { + path: Path::new(path), + size: stat.st_size as u64, + kind: kind, + perm: (stat.st_mode) as io::FilePermission & io::AllPermissions, + created: stat.st_ctime as u64, + modified: stat.st_mtime as u64, + accessed: stat.st_atime as u64, + unstable: io::UnstableFileStat { + device: stat.st_dev as u64, + inode: stat.st_ino as u64, + rdev: stat.st_rdev as u64, + nlink: stat.st_nlink as u64, + uid: stat.st_uid as u64, + gid: stat.st_gid as u64, + blksize: 0, + blocks: 0, + flags: 0, + gen: 0, + } + } +} + +#[cfg(unix)] +fn mkstat(stat: &libc::stat, path: &CString) -> io::FileStat { + let path = unsafe { CString::new(path.with_ref(|p| p), false) }; + + // FileStat times are in milliseconds + fn mktime(secs: u64, nsecs: u64) -> u64 { secs * 1000 + nsecs / 1000000 } + + let kind = match (stat.st_mode as c_int) & libc::S_IFMT { + libc::S_IFREG => io::TypeFile, + libc::S_IFDIR => io::TypeDirectory, + libc::S_IFIFO => io::TypeNamedPipe, + libc::S_IFBLK => io::TypeBlockSpecial, + libc::S_IFLNK => io::TypeSymlink, + _ => io::TypeUnknown, + }; + + #[cfg(not(target_os = "linux"), not(target_os = "android"))] + fn flags(stat: &libc::stat) -> u64 { stat.st_flags as u64 } + #[cfg(target_os = "linux")] #[cfg(target_os = "android")] + fn flags(_stat: &libc::stat) -> u64 { 0 } + + #[cfg(not(target_os = "linux"), not(target_os = "android"))] + fn gen(stat: &libc::stat) -> u64 { stat.st_gen as u64 } + #[cfg(target_os = "linux")] #[cfg(target_os = "android")] + fn gen(_stat: &libc::stat) -> u64 { 0 } + + io::FileStat { + path: Path::new(path), + size: stat.st_size as u64, + kind: kind, + perm: (stat.st_mode) as io::FilePermission & io::AllPermissions, + created: mktime(stat.st_ctime as u64, stat.st_ctime_nsec as u64), + modified: mktime(stat.st_mtime as u64, stat.st_mtime_nsec as u64), + accessed: mktime(stat.st_atime as u64, stat.st_atime_nsec as u64), + unstable: io::UnstableFileStat { + device: stat.st_dev as u64, + inode: stat.st_ino as u64, + rdev: stat.st_rdev as u64, + nlink: stat.st_nlink as u64, + uid: stat.st_uid as u64, + gid: stat.st_gid as u64, + blksize: stat.st_blksize as u64, + blocks: stat.st_blocks as u64, + flags: flags(stat), + gen: gen(stat), + } + } +} + +pub fn stat(p: &CString) -> IoResult { + return os_stat(p); + + #[cfg(windows)] + fn os_stat(p: &CString) -> IoResult { + let mut stat: libc::stat = unsafe { intrinsics::uninit() }; + as_utf16_p(p.as_str().unwrap(), |up| { + match unsafe { libc::wstat(up, &mut stat) } { + 0 => Ok(mkstat(&stat, p)), + _ => Err(super::last_error()), + } + }) + } + + #[cfg(unix)] + fn os_stat(p: &CString) -> IoResult { + let mut stat: libc::stat = unsafe { intrinsics::uninit() }; + match unsafe { libc::stat(p.with_ref(|p| p), &mut stat) } { + 0 => Ok(mkstat(&stat, p)), + _ => Err(super::last_error()), + } + } +} + +pub fn lstat(p: &CString) -> IoResult { + return os_lstat(p); + + // XXX: windows implementation is missing + #[cfg(windows)] + fn os_lstat(_p: &CString) -> IoResult { + Err(super::unimpl()) + } + + #[cfg(unix)] + fn os_lstat(p: &CString) -> IoResult { + let mut stat: libc::stat = unsafe { intrinsics::uninit() }; + match unsafe { libc::lstat(p.with_ref(|p| p), &mut stat) } { + 0 => Ok(mkstat(&stat, p)), + _ => Err(super::last_error()), + } + } +} + +pub fn utime(p: &CString, atime: u64, mtime: u64) -> IoResult<()> { + return super::mkerr_libc(os_utime(p, atime, mtime)); + + #[cfg(windows)] + fn os_utime(p: &CString, atime: u64, mtime: u64) -> c_int { + let buf = libc::utimbuf { + actime: (atime / 1000) as libc::time64_t, + modtime: (mtime / 1000) as libc::time64_t, + }; + unsafe { + as_utf16_p(p.as_str().unwrap(), |p| libc::wutime(p, &buf)) + } + } + + #[cfg(unix)] + fn os_utime(p: &CString, atime: u64, mtime: u64) -> c_int { + let buf = libc::utimbuf { + actime: (atime / 1000) as libc::time_t, + modtime: (mtime / 1000) as libc::time_t, + }; + unsafe { libc::utime(p.with_ref(|p| p), &buf) } + } +} + +#[cfg(test)] +mod tests { + use std::io::native::file::{CFile, FileDesc}; + use std::io::fs; + use std::io; + use std::libc; + use std::os; + use std::rt::rtio::RtioFileStream; + + #[ignore(cfg(target_os = "freebsd"))] // hmm, maybe pipes have a tiny buffer + #[test] + fn test_file_desc() { + // Run this test with some pipes so we don't have to mess around with + // opening or closing files. + unsafe { + let os::Pipe { input, out } = os::pipe(); + let mut reader = FileDesc::new(input, true); + let mut writer = FileDesc::new(out, true); + + writer.inner_write(bytes!("test")); + let mut buf = [0u8, ..4]; + match reader.inner_read(buf) { + Ok(4) => { + assert_eq!(buf[0], 't' as u8); + assert_eq!(buf[1], 'e' as u8); + assert_eq!(buf[2], 's' as u8); + assert_eq!(buf[3], 't' as u8); + } + r => fail!("invalid read: {:?}", r) + } + + assert!(writer.inner_read(buf).is_err()); + assert!(reader.inner_write(buf).is_err()); + } + } + + #[ignore(cfg(windows))] // apparently windows doesn't like tmpfile + #[test] + fn test_cfile() { + unsafe { + let f = libc::tmpfile(); + assert!(!f.is_null()); + let mut file = CFile::new(f); + + file.write(bytes!("test")); + let mut buf = [0u8, ..4]; + file.seek(0, io::SeekSet); + match file.read(buf) { + Ok(4) => { + assert_eq!(buf[0], 't' as u8); + assert_eq!(buf[1], 'e' as u8); + assert_eq!(buf[2], 's' as u8); + assert_eq!(buf[3], 't' as u8); + } + r => fail!("invalid read: {:?}", r) + } + } + } +} diff --git a/src/libnative/io/mod.rs b/src/libnative/io/mod.rs new file mode 100644 index 00000000000..36e3f8af190 --- /dev/null +++ b/src/libnative/io/mod.rs @@ -0,0 +1,225 @@ +// 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. + +//! Native thread-blocking I/O implementation +//! +//! This module contains the implementation of native thread-blocking +//! implementations of I/O on all platforms. This module is not intended to be +//! used directly, but rather the rust runtime will fall back to using it if +//! necessary. +//! +//! Rust code normally runs inside of green tasks with a local scheduler using +//! asynchronous I/O to cooperate among tasks. This model is not always +//! available, however, and that's where these native implementations come into +//! play. The only dependencies of these modules are the normal system libraries +//! that you would find on the respective platform. + +use std::c_str::CString; +use std::comm::SharedChan; +use std::libc::c_int; +use std::libc; +use std::os; +use std::rt::rtio; +use std::rt::rtio::{RtioTcpStream, RtioTcpListener, RtioUdpSocket, + RtioUnixListener, RtioPipe, RtioFileStream, RtioProcess, + RtioSignal, RtioTTY, CloseBehavior, RtioTimer}; +use std::io; +use std::io::IoError; +use std::io::net::ip::SocketAddr; +use std::io::process::ProcessConfig; +use std::io::signal::Signum; +use ai = std::io::net::addrinfo; + +// Local re-exports +pub use self::file::FileDesc; +pub use self::process::Process; + +// Native I/O implementations +pub mod file; +pub mod process; + +type IoResult = Result; + +fn unimpl() -> IoError { + IoError { + kind: io::IoUnavailable, + desc: "unimplemented I/O interface", + detail: None, + } +} + +fn last_error() -> IoError { + #[cfg(windows)] + fn get_err(errno: i32) -> (io::IoErrorKind, &'static str) { + match errno { + libc::EOF => (io::EndOfFile, "end of file"), + _ => (io::OtherIoError, "unknown error"), + } + } + + #[cfg(not(windows))] + fn get_err(errno: i32) -> (io::IoErrorKind, &'static str) { + // XXX: this should probably be a bit more descriptive... + match errno { + libc::EOF => (io::EndOfFile, "end of file"), + + // These two constants can have the same value on some systems, but + // different values on others, so we can't use a match clause + x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => + (io::ResourceUnavailable, "resource temporarily unavailable"), + + _ => (io::OtherIoError, "unknown error"), + } + } + + let (kind, desc) = get_err(os::errno() as i32); + IoError { + kind: kind, + desc: desc, + detail: Some(os::last_os_error()) + } +} + +// unix has nonzero values as errors +fn mkerr_libc(ret: libc::c_int) -> IoResult<()> { + if ret != 0 { + Err(last_error()) + } else { + Ok(()) + } +} + +// windows has zero values as errors +#[cfg(windows)] +fn mkerr_winbool(ret: libc::c_int) -> IoResult<()> { + if ret == 0 { + Err(last_error()) + } else { + Ok(()) + } +} + +/// Implementation of rt::rtio's IoFactory trait to generate handles to the +/// native I/O functionality. +pub struct IoFactory; + +impl rtio::IoFactory for IoFactory { + // all native io factories are the same + fn id(&self) -> uint { 0 } + + // networking + fn tcp_connect(&mut self, _addr: SocketAddr) -> IoResult<~RtioTcpStream> { + Err(unimpl()) + } + fn tcp_bind(&mut self, _addr: SocketAddr) -> IoResult<~RtioTcpListener> { + Err(unimpl()) + } + fn udp_bind(&mut self, _addr: SocketAddr) -> IoResult<~RtioUdpSocket> { + Err(unimpl()) + } + fn unix_bind(&mut self, _path: &CString) -> IoResult<~RtioUnixListener> { + Err(unimpl()) + } + fn unix_connect(&mut self, _path: &CString) -> IoResult<~RtioPipe> { + Err(unimpl()) + } + fn get_host_addresses(&mut self, _host: Option<&str>, _servname: Option<&str>, + _hint: Option) -> IoResult<~[ai::Info]> { + Err(unimpl()) + } + + // filesystem operations + fn fs_from_raw_fd(&mut self, fd: c_int, + close: CloseBehavior) -> ~RtioFileStream { + let close = match close { + rtio::CloseSynchronously | rtio::CloseAsynchronously => true, + rtio::DontClose => false + }; + ~file::FileDesc::new(fd, close) as ~RtioFileStream + } + fn fs_open(&mut self, path: &CString, fm: io::FileMode, fa: io::FileAccess) + -> IoResult<~RtioFileStream> { + file::open(path, fm, fa).map(|fd| ~fd as ~RtioFileStream) + } + fn fs_unlink(&mut self, path: &CString) -> IoResult<()> { + file::unlink(path) + } + fn fs_stat(&mut self, path: &CString) -> IoResult { + file::stat(path) + } + fn fs_mkdir(&mut self, path: &CString, + mode: io::FilePermission) -> IoResult<()> { + file::mkdir(path, mode) + } + fn fs_chmod(&mut self, path: &CString, + mode: io::FilePermission) -> IoResult<()> { + file::chmod(path, mode) + } + fn fs_rmdir(&mut self, path: &CString) -> IoResult<()> { + file::rmdir(path) + } + fn fs_rename(&mut self, path: &CString, to: &CString) -> IoResult<()> { + file::rename(path, to) + } + fn fs_readdir(&mut self, path: &CString, _flags: c_int) -> IoResult<~[Path]> { + file::readdir(path) + } + fn fs_lstat(&mut self, path: &CString) -> IoResult { + file::lstat(path) + } + fn fs_chown(&mut self, path: &CString, uid: int, gid: int) -> IoResult<()> { + file::chown(path, uid, gid) + } + fn fs_readlink(&mut self, path: &CString) -> IoResult { + file::readlink(path) + } + fn fs_symlink(&mut self, src: &CString, dst: &CString) -> IoResult<()> { + file::symlink(src, dst) + } + fn fs_link(&mut self, src: &CString, dst: &CString) -> IoResult<()> { + file::link(src, dst) + } + fn fs_utime(&mut self, src: &CString, atime: u64, + mtime: u64) -> IoResult<()> { + file::utime(src, atime, mtime) + } + + // misc + fn timer_init(&mut self) -> IoResult<~RtioTimer> { + Err(unimpl()) + } + fn spawn(&mut self, config: ProcessConfig) + -> IoResult<(~RtioProcess, ~[Option<~RtioPipe>])> { + process::Process::spawn(config).map(|(p, io)| { + (~p as ~RtioProcess, + io.move_iter().map(|p| p.map(|p| ~p as ~RtioPipe)).collect()) + }) + } + fn pipe_open(&mut self, fd: c_int) -> IoResult<~RtioPipe> { + Ok(~file::FileDesc::new(fd, true) as ~RtioPipe) + } + fn tty_open(&mut self, fd: c_int, _readable: bool) -> IoResult<~RtioTTY> { + if unsafe { libc::isatty(fd) } != 0 { + // Don't ever close the stdio file descriptors, nothing good really + // comes of that. + Ok(~file::FileDesc::new(fd, fd > libc::STDERR_FILENO) as ~RtioTTY) + } else { + Err(IoError { + kind: io::MismatchedFileTypeForOperation, + desc: "file descriptor is not a TTY", + detail: None, + }) + } + } + fn signal(&mut self, _signal: Signum, _channel: SharedChan) + -> IoResult<~RtioSignal> { + Err(unimpl()) + } +} diff --git a/src/libnative/io/process.rs b/src/libnative/io/process.rs new file mode 100644 index 00000000000..2277d408ee4 --- /dev/null +++ b/src/libnative/io/process.rs @@ -0,0 +1,652 @@ +// Copyright 2012-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 std::cast; +use std::io; +use std::libc::{pid_t, c_void, c_int}; +use std::libc; +use std::os; +use std::ptr; +use std::rt::rtio; +use p = std::io::process; + +use super::file; + +/** + * A value representing a child process. + * + * The lifetime of this value is linked to the lifetime of the actual + * process - the Process destructor calls self.finish() which waits + * for the process to terminate. + */ +pub struct Process { + /// The unique id of the process (this should never be negative). + priv pid: pid_t, + + /// A handle to the process - on unix this will always be NULL, but on + /// windows it will be a HANDLE to the process, which will prevent the + /// pid being re-used until the handle is closed. + priv handle: *(), + + /// None until finish() is called. + priv exit_code: Option, +} + +impl Process { + /// Creates a new process using native process-spawning abilities provided + /// by the OS. Operations on this process will be blocking instead of using + /// the runtime for sleeping just this current task. + /// + /// # Arguments + /// + /// * prog - the program to run + /// * args - the arguments to pass to the program, not including the program + /// itself + /// * env - an optional environment to specify for the child process. If + /// this value is `None`, then the child will inherit the parent's + /// environment + /// * cwd - an optionally specified current working directory of the child, + /// defaulting to the parent's current working directory + /// * stdin, stdout, stderr - These optionally specified file descriptors + /// dictate where the stdin/out/err of the child process will go. If + /// these are `None`, then this module will bind the input/output to an + /// os pipe instead. This process takes ownership of these file + /// descriptors, closing them upon destruction of the process. + pub fn spawn(config: p::ProcessConfig) + -> Result<(Process, ~[Option]), io::IoError> + { + // right now we only handle stdin/stdout/stderr. + if config.io.len() > 3 { + return Err(super::unimpl()); + } + + fn get_io(io: &[p::StdioContainer], + ret: &mut ~[Option], + idx: uint) -> (Option, c_int) { + if idx >= io.len() { return (None, -1); } + ret.push(None); + match io[idx] { + p::Ignored => (None, -1), + p::InheritFd(fd) => (None, fd), + p::CreatePipe(readable, _writable) => { + let pipe = os::pipe(); + let (theirs, ours) = if readable { + (pipe.input, pipe.out) + } else { + (pipe.out, pipe.input) + }; + ret[idx] = Some(file::FileDesc::new(ours, true)); + (Some(pipe), theirs) + } + } + } + + let mut ret_io = ~[]; + let (in_pipe, in_fd) = get_io(config.io, &mut ret_io, 0); + let (out_pipe, out_fd) = get_io(config.io, &mut ret_io, 1); + let (err_pipe, err_fd) = get_io(config.io, &mut ret_io, 2); + + let env = config.env.map(|a| a.to_owned()); + let cwd = config.cwd.map(|a| Path::new(a)); + let res = spawn_process_os(config.program, config.args, env, + cwd.as_ref(), in_fd, out_fd, err_fd); + + unsafe { + for pipe in in_pipe.iter() { libc::close(pipe.input); } + for pipe in out_pipe.iter() { libc::close(pipe.out); } + for pipe in err_pipe.iter() { libc::close(pipe.out); } + } + + Ok((Process { pid: res.pid, handle: res.handle, exit_code: None }, ret_io)) + } +} + +impl rtio::RtioProcess for Process { + fn id(&self) -> pid_t { self.pid } + + fn wait(&mut self) -> p::ProcessExit { + let code = match self.exit_code { + Some(code) => code, + None => { + let code = waitpid(self.pid); + self.exit_code = Some(code); + code + } + }; + return p::ExitStatus(code); // XXX: this is wrong + } + + fn kill(&mut self, signum: int) -> Result<(), io::IoError> { + // if the process has finished, and therefore had waitpid called, + // and we kill it, then on unix we might ending up killing a + // newer process that happens to have the same (re-used) id + match self.exit_code { + Some(..) => return Err(io::IoError { + kind: io::OtherIoError, + desc: "can't kill an exited process", + detail: None, + }), + None => {} + } + return unsafe { killpid(self.pid, signum) }; + + #[cfg(windows)] + unsafe fn killpid(pid: pid_t, signal: int) -> Result<(), io::IoError> { + match signal { + io::process::PleaseExitSignal | io::process::MustDieSignal => { + libc::funcs::extra::kernel32::TerminateProcess( + cast::transmute(pid), 1); + Ok(()) + } + _ => Err(io::IoError { + kind: io::OtherIoError, + desc: "unsupported signal on windows", + detail: None, + }) + } + } + + #[cfg(not(windows))] + unsafe fn killpid(pid: pid_t, signal: int) -> Result<(), io::IoError> { + libc::funcs::posix88::signal::kill(pid, signal as c_int); + Ok(()) + } + } +} + +impl Drop for Process { + fn drop(&mut self) { + free_handle(self.handle); + } +} + +struct SpawnProcessResult { + pid: pid_t, + handle: *(), +} + +#[cfg(windows)] +fn spawn_process_os(prog: &str, args: &[~str], + env: Option<~[(~str, ~str)]>, + dir: Option<&Path>, + in_fd: c_int, out_fd: c_int, err_fd: c_int) -> SpawnProcessResult { + use std::libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO}; + use std::libc::consts::os::extra::{ + TRUE, FALSE, + STARTF_USESTDHANDLES, + INVALID_HANDLE_VALUE, + DUPLICATE_SAME_ACCESS + }; + use std::libc::funcs::extra::kernel32::{ + GetCurrentProcess, + DuplicateHandle, + CloseHandle, + CreateProcessA + }; + use std::libc::funcs::extra::msvcrt::get_osfhandle; + + use std::mem; + + unsafe { + + let mut si = zeroed_startupinfo(); + si.cb = mem::size_of::() as DWORD; + si.dwFlags = STARTF_USESTDHANDLES; + + let cur_proc = GetCurrentProcess(); + + let orig_std_in = get_osfhandle(in_fd) as HANDLE; + if orig_std_in == INVALID_HANDLE_VALUE as HANDLE { + fail!("failure in get_osfhandle: {}", os::last_os_error()); + } + if DuplicateHandle(cur_proc, orig_std_in, cur_proc, &mut si.hStdInput, + 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE { + fail!("failure in DuplicateHandle: {}", os::last_os_error()); + } + + let orig_std_out = get_osfhandle(out_fd) as HANDLE; + if orig_std_out == INVALID_HANDLE_VALUE as HANDLE { + fail!("failure in get_osfhandle: {}", os::last_os_error()); + } + if DuplicateHandle(cur_proc, orig_std_out, cur_proc, &mut si.hStdOutput, + 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE { + fail!("failure in DuplicateHandle: {}", os::last_os_error()); + } + + let orig_std_err = get_osfhandle(err_fd) as HANDLE; + if orig_std_err == INVALID_HANDLE_VALUE as HANDLE { + fail!("failure in get_osfhandle: {}", os::last_os_error()); + } + if DuplicateHandle(cur_proc, orig_std_err, cur_proc, &mut si.hStdError, + 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE { + fail!("failure in DuplicateHandle: {}", os::last_os_error()); + } + + let cmd = make_command_line(prog, args); + let mut pi = zeroed_process_information(); + let mut create_err = None; + + with_envp(env, |envp| { + with_dirp(dir, |dirp| { + cmd.with_c_str(|cmdp| { + let created = CreateProcessA(ptr::null(), cast::transmute(cmdp), + ptr::mut_null(), ptr::mut_null(), TRUE, + 0, envp, dirp, &mut si, &mut pi); + if created == FALSE { + create_err = Some(os::last_os_error()); + } + }) + }) + }); + + CloseHandle(si.hStdInput); + CloseHandle(si.hStdOutput); + CloseHandle(si.hStdError); + + for msg in create_err.iter() { + fail!("failure in CreateProcess: {}", *msg); + } + + // We close the thread handle because std::we don't care about keeping the + // thread id valid, and we aren't keeping the thread handle around to be + // able to close it later. We don't close the process handle however + // because std::we want the process id to stay valid at least until the + // calling code closes the process handle. + CloseHandle(pi.hThread); + + SpawnProcessResult { + pid: pi.dwProcessId as pid_t, + handle: pi.hProcess as *() + } + } +} + +#[cfg(windows)] +fn zeroed_startupinfo() -> libc::types::os::arch::extra::STARTUPINFO { + libc::types::os::arch::extra::STARTUPINFO { + cb: 0, + lpReserved: ptr::mut_null(), + lpDesktop: ptr::mut_null(), + lpTitle: ptr::mut_null(), + dwX: 0, + dwY: 0, + dwXSize: 0, + dwYSize: 0, + dwXCountChars: 0, + dwYCountCharts: 0, + dwFillAttribute: 0, + dwFlags: 0, + wShowWindow: 0, + cbReserved2: 0, + lpReserved2: ptr::mut_null(), + hStdInput: ptr::mut_null(), + hStdOutput: ptr::mut_null(), + hStdError: ptr::mut_null() + } +} + +#[cfg(windows)] +fn zeroed_process_information() -> libc::types::os::arch::extra::PROCESS_INFORMATION { + libc::types::os::arch::extra::PROCESS_INFORMATION { + hProcess: ptr::mut_null(), + hThread: ptr::mut_null(), + dwProcessId: 0, + dwThreadId: 0 + } +} + +// FIXME: this is only pub so it can be tested (see issue #4536) +#[cfg(windows)] +pub fn make_command_line(prog: &str, args: &[~str]) -> ~str { + let mut cmd = ~""; + append_arg(&mut cmd, prog); + for arg in args.iter() { + cmd.push_char(' '); + append_arg(&mut cmd, *arg); + } + return cmd; + + fn append_arg(cmd: &mut ~str, arg: &str) { + let quote = arg.chars().any(|c| c == ' ' || c == '\t'); + if quote { + cmd.push_char('"'); + } + for i in range(0u, arg.len()) { + append_char_at(cmd, arg, i); + } + if quote { + cmd.push_char('"'); + } + } + + fn append_char_at(cmd: &mut ~str, arg: &str, i: uint) { + match arg[i] as char { + '"' => { + // Escape quotes. + cmd.push_str("\\\""); + } + '\\' => { + if backslash_run_ends_in_quote(arg, i) { + // Double all backslashes that are in runs before quotes. + cmd.push_str("\\\\"); + } else { + // Pass other backslashes through unescaped. + cmd.push_char('\\'); + } + } + c => { + cmd.push_char(c); + } + } + } + + fn backslash_run_ends_in_quote(s: &str, mut i: uint) -> bool { + while i < s.len() && s[i] as char == '\\' { + i += 1; + } + return i < s.len() && s[i] as char == '"'; + } +} + +#[cfg(unix)] +fn spawn_process_os(prog: &str, args: &[~str], + env: Option<~[(~str, ~str)]>, + dir: Option<&Path>, + in_fd: c_int, out_fd: c_int, err_fd: c_int) -> SpawnProcessResult { + use std::libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp}; + use std::libc::funcs::bsd44::getdtablesize; + + mod rustrt { + extern { + pub fn rust_unset_sigprocmask(); + } + } + + #[cfg(windows)] + unsafe fn set_environ(_envp: *c_void) {} + #[cfg(target_os = "macos")] + unsafe fn set_environ(envp: *c_void) { + extern { fn _NSGetEnviron() -> *mut *c_void; } + + *_NSGetEnviron() = envp; + } + #[cfg(not(target_os = "macos"), not(windows))] + unsafe fn set_environ(envp: *c_void) { + extern { + static mut environ: *c_void; + } + environ = envp; + } + + unsafe { + + let pid = fork(); + if pid < 0 { + fail!("failure in fork: {}", os::last_os_error()); + } else if pid > 0 { + return SpawnProcessResult {pid: pid, handle: ptr::null()}; + } + + rustrt::rust_unset_sigprocmask(); + + if dup2(in_fd, 0) == -1 { + fail!("failure in dup2(in_fd, 0): {}", os::last_os_error()); + } + if dup2(out_fd, 1) == -1 { + fail!("failure in dup2(out_fd, 1): {}", os::last_os_error()); + } + if dup2(err_fd, 2) == -1 { + fail!("failure in dup3(err_fd, 2): {}", os::last_os_error()); + } + // close all other fds + for fd in range(3, getdtablesize()).invert() { + close(fd as c_int); + } + + with_dirp(dir, |dirp| { + if !dirp.is_null() && chdir(dirp) == -1 { + fail!("failure in chdir: {}", os::last_os_error()); + } + }); + + with_envp(env, |envp| { + if !envp.is_null() { + set_environ(envp); + } + with_argv(prog, args, |argv| { + execvp(*argv, argv); + // execvp only returns if an error occurred + fail!("failure in execvp: {}", os::last_os_error()); + }) + }) + } +} + +#[cfg(unix)] +fn with_argv(prog: &str, args: &[~str], cb: |**libc::c_char| -> T) -> T { + use std::vec; + + // We can't directly convert `str`s into `*char`s, as someone needs to hold + // a reference to the intermediary byte buffers. So first build an array to + // hold all the ~[u8] byte strings. + let mut tmps = vec::with_capacity(args.len() + 1); + + tmps.push(prog.to_c_str()); + + for arg in args.iter() { + tmps.push(arg.to_c_str()); + } + + // Next, convert each of the byte strings into a pointer. This is + // technically unsafe as the caller could leak these pointers out of our + // scope. + let mut ptrs = tmps.map(|tmp| tmp.with_ref(|buf| buf)); + + // Finally, make sure we add a null pointer. + ptrs.push(ptr::null()); + + cb(ptrs.as_ptr()) +} + +#[cfg(unix)] +fn with_envp(env: Option<~[(~str, ~str)]>, cb: |*c_void| -> T) -> T { + use std::vec; + + // On posixy systems we can pass a char** for envp, which is a + // null-terminated array of "k=v\n" strings. Like `with_argv`, we have to + // have a temporary buffer to hold the intermediary `~[u8]` byte strings. + match env { + Some(env) => { + let mut tmps = vec::with_capacity(env.len()); + + for pair in env.iter() { + let kv = format!("{}={}", pair.first(), pair.second()); + tmps.push(kv.to_c_str()); + } + + // Once again, this is unsafe. + let mut ptrs = tmps.map(|tmp| tmp.with_ref(|buf| buf)); + ptrs.push(ptr::null()); + + cb(ptrs.as_ptr() as *c_void) + } + _ => cb(ptr::null()) + } +} + +#[cfg(windows)] +fn with_envp(env: Option<~[(~str, ~str)]>, cb: |*mut c_void| -> T) -> T { + // On win32 we pass an "environment block" which is not a char**, but + // rather a concatenation of null-terminated k=v\0 sequences, with a final + // \0 to terminate. + match env { + Some(env) => { + let mut blk = ~[]; + + for pair in env.iter() { + let kv = format!("{}={}", pair.first(), pair.second()); + blk.push_all(kv.as_bytes()); + blk.push(0); + } + + blk.push(0); + + cb(blk.as_mut_ptr() as *mut c_void) + } + _ => cb(ptr::mut_null()) + } +} + +fn with_dirp(d: Option<&Path>, cb: |*libc::c_char| -> T) -> T { + match d { + Some(dir) => dir.with_c_str(|buf| cb(buf)), + None => cb(ptr::null()) + } +} + +#[cfg(windows)] +fn free_handle(handle: *()) { + unsafe { + libc::funcs::extra::kernel32::CloseHandle(cast::transmute(handle)); + } +} + +#[cfg(unix)] +fn free_handle(_handle: *()) { + // unix has no process handle object, just a pid +} + +/** + * Waits for a process to exit and returns the exit code, failing + * if there is no process with the specified id. + * + * Note that this is private to avoid race conditions on unix where if + * a user calls waitpid(some_process.get_id()) then some_process.finish() + * and some_process.destroy() and some_process.finalize() will then either + * operate on a none-existent process or, even worse, on a newer process + * with the same id. + */ +fn waitpid(pid: pid_t) -> int { + return waitpid_os(pid); + + #[cfg(windows)] + fn waitpid_os(pid: pid_t) -> int { + use std::libc::types::os::arch::extra::DWORD; + use std::libc::consts::os::extra::{ + SYNCHRONIZE, + PROCESS_QUERY_INFORMATION, + FALSE, + STILL_ACTIVE, + INFINITE, + WAIT_FAILED + }; + use std::libc::funcs::extra::kernel32::{ + OpenProcess, + GetExitCodeProcess, + CloseHandle, + WaitForSingleObject + }; + + unsafe { + + let process = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, + FALSE, + pid as DWORD); + if process.is_null() { + fail!("failure in OpenProcess: {}", os::last_os_error()); + } + + loop { + let mut status = 0; + if GetExitCodeProcess(process, &mut status) == FALSE { + CloseHandle(process); + fail!("failure in GetExitCodeProcess: {}", os::last_os_error()); + } + if status != STILL_ACTIVE { + CloseHandle(process); + return status as int; + } + if WaitForSingleObject(process, INFINITE) == WAIT_FAILED { + CloseHandle(process); + fail!("failure in WaitForSingleObject: {}", os::last_os_error()); + } + } + } + } + + #[cfg(unix)] + fn waitpid_os(pid: pid_t) -> int { + use std::libc::funcs::posix01::wait; + + #[cfg(target_os = "linux")] + #[cfg(target_os = "android")] + fn WIFEXITED(status: i32) -> bool { + (status & 0xffi32) == 0i32 + } + + #[cfg(target_os = "macos")] + #[cfg(target_os = "freebsd")] + fn WIFEXITED(status: i32) -> bool { + (status & 0x7fi32) == 0i32 + } + + #[cfg(target_os = "linux")] + #[cfg(target_os = "android")] + fn WEXITSTATUS(status: i32) -> i32 { + (status >> 8i32) & 0xffi32 + } + + #[cfg(target_os = "macos")] + #[cfg(target_os = "freebsd")] + fn WEXITSTATUS(status: i32) -> i32 { + status >> 8i32 + } + + let mut status = 0 as c_int; + if unsafe { wait::waitpid(pid, &mut status, 0) } == -1 { + fail!("failure in waitpid: {}", os::last_os_error()); + } + + return if WIFEXITED(status) { + WEXITSTATUS(status) as int + } else { + 1 + }; + } +} + +#[cfg(test)] +mod tests { + + #[test] #[cfg(windows)] + fn test_make_command_line() { + use super::make_command_line; + assert_eq!( + make_command_line("prog", [~"aaa", ~"bbb", ~"ccc"]), + ~"prog aaa bbb ccc" + ); + assert_eq!( + make_command_line("C:\\Program Files\\blah\\blah.exe", [~"aaa"]), + ~"\"C:\\Program Files\\blah\\blah.exe\" aaa" + ); + assert_eq!( + make_command_line("C:\\Program Files\\test", [~"aa\"bb"]), + ~"\"C:\\Program Files\\test\" aa\\\"bb" + ); + assert_eq!( + make_command_line("echo", [~"a b c"]), + ~"echo \"a b c\"" + ); + } + + // Currently most of the tests of this functionality live inside std::run, + // but they may move here eventually as a non-blocking backend is added to + // std::run +} diff --git a/src/libnative/lib.rs b/src/libnative/lib.rs new file mode 100644 index 00000000000..4b32511dc42 --- /dev/null +++ b/src/libnative/lib.rs @@ -0,0 +1,61 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The native runtime crate +//! +//! This crate contains an implementation of 1:1 scheduling for a "native" +//! runtime. In addition, all I/O provided by this crate is the thread blocking +//! version of I/O. + +#[link(name = "native", + package_id = "native", + vers = "0.9-pre", + uuid = "535344a7-890f-5a23-e1f3-e0d118805141", + url = "https://github.com/mozilla/rust/tree/master/src/native")]; + +#[license = "MIT/ASL2"]; +#[crate_type = "rlib"]; +#[crate_type = "dylib"]; + +// NB this crate explicitly does *not* allow glob imports, please seriously +// consider whether they're needed before adding that feature here. + +use std::cast; +use std::os; +use std::rt; +use std::task::try; + +pub mod io; +pub mod task; + +// XXX: this should not exist here +#[cfg(stage0)] +#[lang = "start"] +pub fn start(main: *u8, argc: int, argv: **u8) -> int { + rt::init(argc, argv); + + // Bootstrap ourselves by installing a local Task and then immediately + // spawning a thread to run 'main'. Always spawn a new thread for main so + // the stack size of 'main' is known (and the bounds can be set + // appropriately). + // + // Once the main task has completed, then we wait for everyone else to exit. + task::run(task::new(), proc() { + let main: extern "Rust" fn() = unsafe { cast::transmute(main) }; + match do try { main() } { + Ok(()) => { os::set_exit_status(0); } + Err(..) => { os::set_exit_status(rt::DEFAULT_ERROR_CODE); } + } + }); + task::wait_for_completion(); + + unsafe { rt::cleanup(); } + os::get_exit_status() +} diff --git a/src/libnative/task.rs b/src/libnative/task.rs new file mode 100644 index 00000000000..fa7500ca85e --- /dev/null +++ b/src/libnative/task.rs @@ -0,0 +1,257 @@ +// 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. + +//! Tasks implemented on top of OS threads +//! +//! This module contains the implementation of the 1:1 threading module required +//! by rust tasks. This implements the necessary API traits laid out by std::rt +//! in order to spawn new tasks and deschedule the current task. + +use std::cast; +use std::rt::env; +use std::rt::local::Local; +use std::rt::rtio; +use std::rt::task::{Task, BlockedTask}; +use std::rt::thread::Thread; +use std::rt; +use std::sync::atomics::{AtomicUint, SeqCst, INIT_ATOMIC_UINT}; +use std::task::TaskOpts; +use std::unstable::mutex::{Mutex, MUTEX_INIT}; +use std::unstable::stack; + +use io; +use task; + +static mut THREAD_CNT: AtomicUint = INIT_ATOMIC_UINT; +static mut LOCK: Mutex = MUTEX_INIT; + +/// Waits for all spawned threads to finish completion. This should only be used +/// by the main task in order to wait for all other tasks to terminate. +/// +/// This mirrors the same semantics as the green scheduling model. +pub fn wait_for_completion() { + static mut someone_waited: bool = false; + + unsafe { + LOCK.lock(); + assert!(!someone_waited); + someone_waited = true; + while THREAD_CNT.load(SeqCst) > 0 { + LOCK.wait(); + } + LOCK.unlock(); + LOCK.destroy(); + } + +} + +// Signal that a thread has finished execution, possibly waking up a blocker +// waiting for all threads to have finished. +fn signal_done() { + unsafe { + LOCK.lock(); + if THREAD_CNT.fetch_sub(1, SeqCst) == 1 { + LOCK.signal(); + } + LOCK.unlock(); + } +} + +/// Creates a new Task which is ready to execute as a 1:1 task. +pub fn new() -> ~Task { + let mut task = ~Task::new(); + task.put_runtime(~Ops { + lock: unsafe { Mutex::new() }, + } as ~rt::Runtime); + return task; +} + +/// Spawns a new task given the configuration options and a procedure to run +/// inside the task. +pub fn spawn(opts: TaskOpts, f: proc()) { + // must happen before the spawn, no need to synchronize with a lock. + unsafe { THREAD_CNT.fetch_add(1, SeqCst); } + + let TaskOpts { + watched: _watched, + notify_chan, name, stack_size + } = opts; + + let mut task = new(); + task.name = name; + match notify_chan { + Some(chan) => { + let on_exit = proc(task_result) { chan.send(task_result) }; + task.death.on_exit = Some(on_exit); + } + None => {} + } + + let stack = stack_size.unwrap_or(env::min_stack()); + let task = task; + + // Spawning a new OS thread guarantees that __morestack will never get + // triggered, but we must manually set up the actual stack bounds once this + // function starts executing. This raises the lower limit by a bit because + // by the time that this function is executing we've already consumed at + // least a little bit of stack (we don't know the exact byte address at + // which our stack started). + Thread::spawn_stack(stack, proc() { + let something_around_the_top_of_the_stack = 1; + let addr = &something_around_the_top_of_the_stack as *int; + unsafe { + let my_stack = addr as uint; + stack::record_stack_bounds(my_stack - stack + 1024, my_stack); + } + + run(task, f); + signal_done(); + }) +} + +/// Runs a task once, consuming the task. The given procedure is run inside of +/// the task. +pub fn run(t: ~Task, f: proc()) { + let mut f = Some(f); + t.run(|| { f.take_unwrap()(); }); +} + +// This structure is the glue between channels and the 1:1 scheduling mode. This +// structure is allocated once per task. +struct Ops { + lock: Mutex, // native synchronization +} + +impl rt::Runtime for Ops { + fn yield_now(~self, mut cur_task: ~Task) { + // put the task back in TLS and then invoke the OS thread yield + cur_task.put_runtime(self as ~rt::Runtime); + Local::put(cur_task); + Thread::yield_now(); + } + + fn maybe_yield(~self, mut cur_task: ~Task) { + // just put the task back in TLS, on OS threads we never need to + // opportunistically yield b/c the OS will do that for us (preemption) + cur_task.put_runtime(self as ~rt::Runtime); + Local::put(cur_task); + } + + fn wrap(~self) -> ~Any { + self as ~Any + } + + // This function gets a little interesting. There are a few safety and + // ownership violations going on here, but this is all done in the name of + // shared state. Additionally, all of the violations are protected with a + // mutex, so in theory there are no races. + // + // The first thing we need to do is to get a pointer to the task's internal + // mutex. This address will not be changing (because the task is allocated + // on the heap). We must have this handle separately because the task will + // have its ownership transferred to the given closure. We're guaranteed, + // however, that this memory will remain valid because *this* is the current + // task's execution thread. + // + // The next weird part is where ownership of the task actually goes. We + // relinquish it to the `f` blocking function, but upon returning this + // function needs to replace the task back in TLS. There is no communication + // from the wakeup thread back to this thread about the task pointer, and + // there's really no need to. In order to get around this, we cast the task + // to a `uint` which is then used at the end of this function to cast back + // to a `~Task` object. Naturally, this looks like it violates ownership + // semantics in that there may be two `~Task` objects. + // + // The fun part is that the wakeup half of this implementation knows to + // "forget" the task on the other end. This means that the awakening half of + // things silently relinquishes ownership back to this thread, but not in a + // way that the compiler can understand. The task's memory is always valid + // for both tasks because these operations are all done inside of a mutex. + // + // You'll also find that if blocking fails (the `f` function hands the + // BlockedTask back to us), we will `cast::forget` the handles. The + // reasoning for this is the same logic as above in that the task silently + // transfers ownership via the `uint`, not through normal compiler + // semantics. + fn deschedule(mut ~self, times: uint, mut cur_task: ~Task, + f: |BlockedTask| -> Result<(), BlockedTask>) { + let my_lock: *mut Mutex = &mut self.lock as *mut Mutex; + cur_task.put_runtime(self as ~rt::Runtime); + + unsafe { + let cur_task_dupe = *cast::transmute::<&~Task, &uint>(&cur_task); + let task = BlockedTask::block(cur_task); + + if times == 1 { + (*my_lock).lock(); + match f(task) { + Ok(()) => (*my_lock).wait(), + Err(task) => { cast::forget(task.wake()); } + } + (*my_lock).unlock(); + } else { + let mut iter = task.make_selectable(times); + (*my_lock).lock(); + let success = iter.all(|task| { + match f(task) { + Ok(()) => true, + Err(task) => { + cast::forget(task.wake()); + false + } + } + }); + if success { + (*my_lock).wait(); + } + (*my_lock).unlock(); + } + // re-acquire ownership of the task + cur_task = cast::transmute::(cur_task_dupe); + } + + // put the task back in TLS, and everything is as it once was. + Local::put(cur_task); + } + + // See the comments on `deschedule` for why the task is forgotten here, and + // why it's valid to do so. + fn reawaken(mut ~self, mut to_wake: ~Task, _can_resched: bool) { + unsafe { + let lock: *mut Mutex = &mut self.lock as *mut Mutex; + to_wake.put_runtime(self as ~rt::Runtime); + cast::forget(to_wake); + (*lock).lock(); + (*lock).signal(); + (*lock).unlock(); + } + } + + fn spawn_sibling(~self, mut cur_task: ~Task, opts: TaskOpts, f: proc()) { + cur_task.put_runtime(self as ~rt::Runtime); + Local::put(cur_task); + + task::spawn(opts, f); + } + + fn local_io<'a>(&'a mut self) -> Option> { + static mut io: io::IoFactory = io::IoFactory; + // Unsafety is from accessing `io`, which is guaranteed to be safe + // because you can't do anything usable with this statically initialized + // unit struct. + Some(unsafe { rtio::LocalIo::new(&mut io as &mut rtio::IoFactory) }) + } +} + +impl Drop for Ops { + fn drop(&mut self) { + unsafe { self.lock.destroy() } + } +} -- cgit 1.4.1-3-g733a5 From 018d60509c04cdebdf8b0d9e2b58f2604538e516 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 12 Dec 2013 21:38:57 -0800 Subject: std: Get stdtest all passing again This commit brings the library up-to-date in order to get all tests passing again --- mk/host.mk | 2 +- mk/tests.mk | 4 +- src/driver/driver.rs | 2 + src/libgreen/lib.rs | 2 + src/libnative/task.rs | 11 +- src/libstd/any.rs | 15 +- src/libstd/comm/mod.rs | 412 +++++++++++++++------------------- src/libstd/comm/select.rs | 94 ++------ src/libstd/io/fs.rs | 87 +++---- src/libstd/io/mod.rs | 4 + src/libstd/io/net/tcp.rs | 62 +++-- src/libstd/io/net/udp.rs | 11 +- src/libstd/io/net/unix.rs | 5 +- src/libstd/io/stdio.rs | 17 +- src/libstd/io/test.rs | 44 +++- src/libstd/lib.rs | 8 +- src/libstd/local_data.rs | 1 + src/libstd/rt/local.rs | 19 +- src/libstd/rt/task.rs | 94 ++++---- src/libstd/run.rs | 4 +- src/libstd/sync/arc.rs | 1 - src/libstd/sync/mpmc_bounded_queue.rs | 12 +- src/libstd/sync/mpsc_queue.rs | 10 +- src/libstd/sync/spsc_queue.rs | 7 +- src/libstd/task.rs | 98 +++----- src/libstd/unstable/mutex.rs | 2 +- src/libstd/unstable/stack.rs | 9 +- src/libstd/unstable/sync.rs | 3 +- src/libstd/vec.rs | 1 - 29 files changed, 451 insertions(+), 590 deletions(-) (limited to 'src/libnative') diff --git a/mk/host.mk b/mk/host.mk index 7aabff52bc4..f94afe587f3 100644 --- a/mk/host.mk +++ b/mk/host.mk @@ -24,7 +24,7 @@ define CP_HOST_STAGE_N # Note: $(3) and $(4) are both the same! $$(HBIN$(2)_H_$(4))/rustc$$(X_$(4)): \ - $$(TBIN$(1)_T_$(4)_H_$(3))/rustc$$(X_$(4)) + $$(TBIN$(1)_T_$(4)_H_$(3))/rustc$$(X_$(4)) \ $$(HLIBRUSTC_DEFAULT$(2)_H_$(4)) \ | $$(HBIN$(2)_H_$(4))/ @$$(call E, cp: $$@) diff --git a/mk/tests.mk b/mk/tests.mk index 179e41ad330..9fd9d9617c7 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -348,13 +348,13 @@ STDTESTDEP_$(1)_$(2)_$(3) = endif $(3)/stage$(1)/test/stdtest-$(2)$$(X_$(2)): \ - $$(STDLIB_CRATE) $$(STDLIB_INPUTS) \ + $$(STDLIB_CRATE) $$(STDLIB_INPUTS) \ $$(STDTESTDEP_$(1)_$(2)_$(3)) @$$(call E, compile_and_link: $$@) $$(STAGE$(1)_T_$(2)_H_$(3)) -o $$@ $$< --test $(3)/stage$(1)/test/extratest-$(2)$$(X_$(2)): \ - $$(EXTRALIB_CRATE) $$(EXTRALIB_INPUTS) \ + $$(EXTRALIB_CRATE) $$(EXTRALIB_INPUTS) \ $$(STDTESTDEP_$(1)_$(2)_$(3)) @$$(call E, compile_and_link: $$@) $$(STAGE$(1)_T_$(2)_H_$(3)) -o $$@ $$< --test diff --git a/src/driver/driver.rs b/src/driver/driver.rs index 9402578d552..8e5b6356a0b 100644 --- a/src/driver/driver.rs +++ b/src/driver/driver.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[cfg(stage0)] extern mod green; + #[cfg(rustpkg)] extern mod this = "rustpkg"; diff --git a/src/libgreen/lib.rs b/src/libgreen/lib.rs index 193b64ff7e5..6530316a627 100644 --- a/src/libgreen/lib.rs +++ b/src/libgreen/lib.rs @@ -57,6 +57,8 @@ pub mod sleeper_list; pub mod stack; pub mod task; +#[cfg(test)] mod tests; + #[cfg(stage0)] #[lang = "start"] pub fn lang_start(main: *u8, argc: int, argv: **u8) -> int { diff --git a/src/libnative/task.rs b/src/libnative/task.rs index fa7500ca85e..782bef10c92 100644 --- a/src/libnative/task.rs +++ b/src/libnative/task.rs @@ -22,7 +22,7 @@ use std::rt::task::{Task, BlockedTask}; use std::rt::thread::Thread; use std::rt; use std::sync::atomics::{AtomicUint, SeqCst, INIT_ATOMIC_UINT}; -use std::task::TaskOpts; +use std::task::{TaskOpts, default_task_opts}; use std::unstable::mutex::{Mutex, MUTEX_INIT}; use std::unstable::stack; @@ -73,9 +73,14 @@ pub fn new() -> ~Task { return task; } +/// Spawns a function with the default configuration +pub fn spawn(f: proc()) { + spawn_opts(default_task_opts(), f) +} + /// Spawns a new task given the configuration options and a procedure to run /// inside the task. -pub fn spawn(opts: TaskOpts, f: proc()) { +pub fn spawn_opts(opts: TaskOpts, f: proc()) { // must happen before the spawn, no need to synchronize with a lock. unsafe { THREAD_CNT.fetch_add(1, SeqCst); } @@ -238,7 +243,7 @@ impl rt::Runtime for Ops { cur_task.put_runtime(self as ~rt::Runtime); Local::put(cur_task); - task::spawn(opts, f); + task::spawn_opts(opts, f); } fn local_io<'a>(&'a mut self) -> Option> { diff --git a/src/libstd/any.rs b/src/libstd/any.rs index 49bae30a461..45a91d01b7a 100644 --- a/src/libstd/any.rs +++ b/src/libstd/any.rs @@ -119,7 +119,7 @@ impl<'a> AnyMutRefExt<'a> for &'a mut Any { /// Extension methods for a owning `Any` trait object pub trait AnyOwnExt { /// Returns the boxed value if it is of type `T`, or - /// `None` if it isn't. + /// `Err(Self)` if it isn't. fn move(self) -> Result<~T, Self>; } @@ -156,9 +156,8 @@ impl<'a> ToStr for &'a Any { #[cfg(test)] mod tests { + use prelude::*; use super::*; - use super::AnyRefExt; - use option::{Some, None}; #[deriving(Eq)] struct Test; @@ -385,8 +384,14 @@ mod tests { let a = ~8u as ~Any; let b = ~Test as ~Any; - assert_eq!(a.move(), Ok(~8u)); - assert_eq!(b.move(), Ok(~Test)); + match a.move::() { + Ok(a) => { assert_eq!(a, ~8u); } + Err(..) => fail!() + } + match b.move::() { + Ok(a) => { assert_eq!(a, ~Test); } + Err(..) => fail!() + } let a = ~8u as ~Any; let b = ~Test as ~Any; diff --git a/src/libstd/comm/mod.rs b/src/libstd/comm/mod.rs index f5048ec62a4..76a9e5d17e1 100644 --- a/src/libstd/comm/mod.rs +++ b/src/libstd/comm/mod.rs @@ -251,18 +251,21 @@ macro_rules! test ( mod $name { #[allow(unused_imports)]; - use util; - use super::super::*; + use native; use prelude::*; + use super::*; + use super::super::*; + use task; + use util; fn f() $b $($a)* #[test] fn uv() { f() } - $($a)* #[test] - #[ignore(cfg(windows))] // FIXME(#11003) - fn native() { - use unstable::run_in_bare_thread; - run_in_bare_thread(f); + $($a)* #[test] fn native() { + use native; + let (p, c) = Chan::new(); + do native::task::spawn { c.send(f()) } + p.recv(); } } ) @@ -889,10 +892,16 @@ impl Drop for Port { mod test { use prelude::*; - use task; - use rt::thread::Thread; + use native; + use os; use super::*; - use rt::test::*; + + pub fn stress_factor() -> uint { + match os::getenv("RUST_TEST_STRESS") { + Some(val) => from_str::(val).unwrap(), + None => 1, + } + } test!(fn smoke() { let (p, c) = Chan::new(); @@ -919,99 +928,88 @@ mod test { assert_eq!(p.recv(), 1); }) - #[test] - fn smoke_threads() { + test!(fn smoke_threads() { let (p, c) = Chan::new(); - do task::spawn_sched(task::SingleThreaded) { + do spawn { c.send(1); } assert_eq!(p.recv(), 1); - } + }) - #[test] #[should_fail] - fn smoke_port_gone() { + test!(fn smoke_port_gone() { let (p, c) = Chan::new(); drop(p); c.send(1); - } + } #[should_fail]) - #[test] #[should_fail] - fn smoke_shared_port_gone() { + test!(fn smoke_shared_port_gone() { let (p, c) = SharedChan::new(); drop(p); c.send(1); - } + } #[should_fail]) - #[test] #[should_fail] - fn smoke_shared_port_gone2() { + test!(fn smoke_shared_port_gone2() { let (p, c) = SharedChan::new(); drop(p); let c2 = c.clone(); drop(c); c2.send(1); - } + } #[should_fail]) - #[test] #[should_fail] - fn port_gone_concurrent() { + test!(fn port_gone_concurrent() { let (p, c) = Chan::new(); - do task::spawn_sched(task::SingleThreaded) { + do spawn { p.recv(); } loop { c.send(1) } - } + } #[should_fail]) - #[test] #[should_fail] - fn port_gone_concurrent_shared() { + test!(fn port_gone_concurrent_shared() { let (p, c) = SharedChan::new(); let c1 = c.clone(); - do task::spawn_sched(task::SingleThreaded) { + do spawn { p.recv(); } loop { c.send(1); c1.send(1); } - } + } #[should_fail]) - #[test] #[should_fail] - fn smoke_chan_gone() { + test!(fn smoke_chan_gone() { let (p, c) = Chan::::new(); drop(c); p.recv(); - } + } #[should_fail]) - #[test] #[should_fail] - fn smoke_chan_gone_shared() { + test!(fn smoke_chan_gone_shared() { let (p, c) = SharedChan::<()>::new(); let c2 = c.clone(); drop(c); drop(c2); p.recv(); - } + } #[should_fail]) - #[test] #[should_fail] - fn chan_gone_concurrent() { + test!(fn chan_gone_concurrent() { let (p, c) = Chan::new(); - do task::spawn_sched(task::SingleThreaded) { + do spawn { c.send(1); c.send(1); } loop { p.recv(); } - } + } #[should_fail]) - #[test] - fn stress() { + test!(fn stress() { let (p, c) = Chan::new(); - do task::spawn_sched(task::SingleThreaded) { + do spawn { for _ in range(0, 10000) { c.send(1); } } for _ in range(0, 10000) { assert_eq!(p.recv(), 1); } - } + }) - #[test] - fn stress_shared() { + test!(fn stress_shared() { static AMT: uint = 10000; static NTHREADS: uint = 8; let (p, c) = SharedChan::::new(); @@ -1027,47 +1025,53 @@ mod test { for _ in range(0, NTHREADS) { let c = c.clone(); - do task::spawn_sched(task::SingleThreaded) { + do spawn { for _ in range(0, AMT) { c.send(1); } } } p1.recv(); - - } + }) #[test] #[ignore(cfg(windows))] // FIXME(#11003) fn send_from_outside_runtime() { let (p, c) = Chan::::new(); let (p1, c1) = Chan::new(); + let (port, chan) = SharedChan::new(); + let chan2 = chan.clone(); do spawn { c1.send(()); for _ in range(0, 40) { assert_eq!(p.recv(), 1); } + chan2.send(()); } p1.recv(); - let t = do Thread::start { + do native::task::spawn { for _ in range(0, 40) { c.send(1); } - }; - t.join(); + chan.send(()); + } + port.recv(); + port.recv(); } #[test] #[ignore(cfg(windows))] // FIXME(#11003) fn recv_from_outside_runtime() { let (p, c) = Chan::::new(); - let t = do Thread::start { + let (dp, dc) = Chan::new(); + do native::task::spawn { for _ in range(0, 40) { assert_eq!(p.recv(), 1); } + dc.send(()); }; for _ in range(0, 40) { c.send(1); } - t.join(); + dp.recv(); } #[test] @@ -1075,173 +1079,132 @@ mod test { fn no_runtime() { let (p1, c1) = Chan::::new(); let (p2, c2) = Chan::::new(); - let t1 = do Thread::start { + let (port, chan) = SharedChan::new(); + let chan2 = chan.clone(); + do native::task::spawn { assert_eq!(p1.recv(), 1); c2.send(2); - }; - let t2 = do Thread::start { + chan2.send(()); + } + do native::task::spawn { c1.send(1); assert_eq!(p2.recv(), 2); - }; - t1.join(); - t2.join(); + chan.send(()); + } + port.recv(); + port.recv(); } - #[test] - fn oneshot_single_thread_close_port_first() { + test!(fn oneshot_single_thread_close_port_first() { // Simple test of closing without sending - do run_in_newsched_task { - let (port, _chan) = Chan::::new(); - { let _p = port; } - } - } + let (port, _chan) = Chan::::new(); + { let _p = port; } + }) - #[test] - fn oneshot_single_thread_close_chan_first() { + test!(fn oneshot_single_thread_close_chan_first() { // Simple test of closing without sending - do run_in_newsched_task { - let (_port, chan) = Chan::::new(); - { let _c = chan; } - } - } + let (_port, chan) = Chan::::new(); + { let _c = chan; } + }) - #[test] #[should_fail] - fn oneshot_single_thread_send_port_close() { + test!(fn oneshot_single_thread_send_port_close() { // Testing that the sender cleans up the payload if receiver is closed let (port, chan) = Chan::<~int>::new(); { let _p = port; } chan.send(~0); - } + } #[should_fail]) - #[test] - fn oneshot_single_thread_recv_chan_close() { + test!(fn oneshot_single_thread_recv_chan_close() { // Receiving on a closed chan will fail - do run_in_newsched_task { - let res = do spawntask_try { - let (port, chan) = Chan::<~int>::new(); - { let _c = chan; } - port.recv(); - }; - // What is our res? - assert!(res.is_err()); - } - } - - #[test] - fn oneshot_single_thread_send_then_recv() { - do run_in_newsched_task { + let res = do task::try { let (port, chan) = Chan::<~int>::new(); - chan.send(~10); - assert!(port.recv() == ~10); - } - } + { let _c = chan; } + port.recv(); + }; + // What is our res? + assert!(res.is_err()); + }) - #[test] - fn oneshot_single_thread_try_send_open() { - do run_in_newsched_task { - let (port, chan) = Chan::::new(); - assert!(chan.try_send(10)); - assert!(port.recv() == 10); - } - } + test!(fn oneshot_single_thread_send_then_recv() { + let (port, chan) = Chan::<~int>::new(); + chan.send(~10); + assert!(port.recv() == ~10); + }) - #[test] - fn oneshot_single_thread_try_send_closed() { - do run_in_newsched_task { - let (port, chan) = Chan::::new(); - { let _p = port; } - assert!(!chan.try_send(10)); - } - } + test!(fn oneshot_single_thread_try_send_open() { + let (port, chan) = Chan::::new(); + assert!(chan.try_send(10)); + assert!(port.recv() == 10); + }) - #[test] - fn oneshot_single_thread_try_recv_open() { - do run_in_newsched_task { - let (port, chan) = Chan::::new(); - chan.send(10); - assert!(port.try_recv() == Some(10)); - } - } + test!(fn oneshot_single_thread_try_send_closed() { + let (port, chan) = Chan::::new(); + { let _p = port; } + assert!(!chan.try_send(10)); + }) - #[test] - fn oneshot_single_thread_try_recv_closed() { - do run_in_newsched_task { - let (port, chan) = Chan::::new(); - { let _c = chan; } - assert!(port.recv_opt() == None); - } - } + test!(fn oneshot_single_thread_try_recv_open() { + let (port, chan) = Chan::::new(); + chan.send(10); + assert!(port.try_recv() == Some(10)); + }) - #[test] - fn oneshot_single_thread_peek_data() { - do run_in_newsched_task { - let (port, chan) = Chan::::new(); - assert!(port.try_recv().is_none()); - chan.send(10); - assert!(port.try_recv().is_some()); - } - } + test!(fn oneshot_single_thread_try_recv_closed() { + let (port, chan) = Chan::::new(); + { let _c = chan; } + assert!(port.recv_opt() == None); + }) - #[test] - fn oneshot_single_thread_peek_close() { - do run_in_newsched_task { - let (port, chan) = Chan::::new(); - { let _c = chan; } - assert!(port.try_recv().is_none()); - assert!(port.try_recv().is_none()); - } - } + test!(fn oneshot_single_thread_peek_data() { + let (port, chan) = Chan::::new(); + assert!(port.try_recv().is_none()); + chan.send(10); + assert!(port.try_recv().is_some()); + }) - #[test] - fn oneshot_single_thread_peek_open() { - do run_in_newsched_task { - let (port, _) = Chan::::new(); - assert!(port.try_recv().is_none()); - } - } + test!(fn oneshot_single_thread_peek_close() { + let (port, chan) = Chan::::new(); + { let _c = chan; } + assert!(port.try_recv().is_none()); + assert!(port.try_recv().is_none()); + }) - #[test] - fn oneshot_multi_task_recv_then_send() { - do run_in_newsched_task { - let (port, chan) = Chan::<~int>::new(); - do spawntask { - assert!(port.recv() == ~10); - } + test!(fn oneshot_single_thread_peek_open() { + let (port, _) = Chan::::new(); + assert!(port.try_recv().is_none()); + }) - chan.send(~10); + test!(fn oneshot_multi_task_recv_then_send() { + let (port, chan) = Chan::<~int>::new(); + do spawn { + assert!(port.recv() == ~10); } - } - #[test] - fn oneshot_multi_task_recv_then_close() { - do run_in_newsched_task { - let (port, chan) = Chan::<~int>::new(); - do spawntask_later { - let _chan = chan; - } - let res = do spawntask_try { - assert!(port.recv() == ~10); - }; - assert!(res.is_err()); + chan.send(~10); + }) + + test!(fn oneshot_multi_task_recv_then_close() { + let (port, chan) = Chan::<~int>::new(); + do spawn { + let _chan = chan; } - } + let res = do task::try { + assert!(port.recv() == ~10); + }; + assert!(res.is_err()); + }) - #[test] - fn oneshot_multi_thread_close_stress() { + test!(fn oneshot_multi_thread_close_stress() { stress_factor().times(|| { - do run_in_newsched_task { - let (port, chan) = Chan::::new(); - let thread = do spawntask_thread { - let _p = port; - }; - let _chan = chan; - thread.join(); + let (port, chan) = Chan::::new(); + do spawn { + let _p = port; } + let _chan = chan; }) - } + }) - #[test] - fn oneshot_multi_thread_send_close_stress() { + test!(fn oneshot_multi_thread_send_close_stress() { stress_factor().times(|| { let (port, chan) = Chan::::new(); do spawn { @@ -1251,10 +1214,9 @@ mod test { chan.send(1); }; }) - } + }) - #[test] - fn oneshot_multi_thread_recv_close_stress() { + test!(fn oneshot_multi_thread_recv_close_stress() { stress_factor().times(|| { let (port, chan) = Chan::::new(); do spawn { @@ -1271,10 +1233,9 @@ mod test { } }; }) - } + }) - #[test] - fn oneshot_multi_thread_send_recv_stress() { + test!(fn oneshot_multi_thread_send_recv_stress() { stress_factor().times(|| { let (port, chan) = Chan::<~int>::new(); do spawn { @@ -1284,10 +1245,9 @@ mod test { assert!(port.recv() == ~10); } }) - } + }) - #[test] - fn stream_send_recv_stress() { + test!(fn stream_send_recv_stress() { stress_factor().times(|| { let (port, chan) = Chan::<~int>::new(); @@ -1297,7 +1257,7 @@ mod test { fn send(chan: Chan<~int>, i: int) { if i == 10 { return } - do spawntask_random { + do spawn { chan.send(~i); send(chan, i + 1); } @@ -1306,44 +1266,37 @@ mod test { fn recv(port: Port<~int>, i: int) { if i == 10 { return } - do spawntask_random { + do spawn { assert!(port.recv() == ~i); recv(port, i + 1); }; } }) - } + }) - #[test] - fn recv_a_lot() { + test!(fn recv_a_lot() { // Regression test that we don't run out of stack in scheduler context - do run_in_newsched_task { - let (port, chan) = Chan::new(); - 10000.times(|| { chan.send(()) }); - 10000.times(|| { port.recv() }); - } - } + let (port, chan) = Chan::new(); + 10000.times(|| { chan.send(()) }); + 10000.times(|| { port.recv() }); + }) - #[test] - fn shared_chan_stress() { - do run_in_mt_newsched_task { - let (port, chan) = SharedChan::new(); - let total = stress_factor() + 100; - total.times(|| { - let chan_clone = chan.clone(); - do spawntask_random { - chan_clone.send(()); - } - }); + test!(fn shared_chan_stress() { + let (port, chan) = SharedChan::new(); + let total = stress_factor() + 100; + total.times(|| { + let chan_clone = chan.clone(); + do spawn { + chan_clone.send(()); + } + }); - total.times(|| { - port.recv(); - }); - } - } + total.times(|| { + port.recv(); + }); + }) - #[test] - fn test_nested_recv_iter() { + test!(fn test_nested_recv_iter() { let (port, chan) = Chan::::new(); let (total_port, total_chan) = Chan::::new(); @@ -1360,10 +1313,9 @@ mod test { chan.send(2); drop(chan); assert_eq!(total_port.recv(), 6); - } + }) - #[test] - fn test_recv_iter_break() { + test!(fn test_recv_iter_break() { let (port, chan) = Chan::::new(); let (count_port, count_chan) = Chan::::new(); @@ -1385,5 +1337,5 @@ mod test { chan.try_send(2); drop(chan); assert_eq!(count_port.recv(), 4); - } + }) } diff --git a/src/libstd/comm/select.rs b/src/libstd/comm/select.rs index 68e1a05a653..302c9d9ea46 100644 --- a/src/libstd/comm/select.rs +++ b/src/libstd/comm/select.rs @@ -51,11 +51,11 @@ use ops::Drop; use option::{Some, None, Option}; use ptr::RawPtr; use result::{Ok, Err}; -use rt::thread::Thread; use rt::local::Local; use rt::task::Task; use super::{Packet, Port}; use sync::atomics::{Relaxed, SeqCst}; +use task; use uint; macro_rules! select { @@ -310,6 +310,7 @@ impl Iterator<*mut Packet> for PacketIterator { } #[cfg(test)] +#[allow(unused_imports)] mod test { use super::super::*; use prelude::*; @@ -365,19 +366,16 @@ mod test { ) }) - #[test] - fn unblocks() { - use std::io::timer; - + test!(fn unblocks() { let (mut p1, c1) = Chan::::new(); let (mut p2, _c2) = Chan::::new(); let (p3, c3) = Chan::::new(); do spawn { - timer::sleep(3); + 20.times(task::deschedule); c1.send(1); p3.recv(); - timer::sleep(3); + 20.times(task::deschedule); } select! ( @@ -389,18 +387,15 @@ mod test { a = p1.recv_opt() => { assert_eq!(a, None); }, _b = p2.recv() => { fail!() } ) - } - - #[test] - fn both_ready() { - use std::io::timer; + }) + test!(fn both_ready() { let (mut p1, c1) = Chan::::new(); let (mut p2, c2) = Chan::::new(); let (p3, c3) = Chan::<()>::new(); do spawn { - timer::sleep(3); + 20.times(task::deschedule); c1.send(1); c2.send(2); p3.recv(); @@ -414,11 +409,12 @@ mod test { a = p1.recv() => { assert_eq!(a, 1); }, a = p2.recv() => { assert_eq!(a, 2); } ) + assert_eq!(p1.try_recv(), None); + assert_eq!(p2.try_recv(), None); c3.send(()); - } + }) - #[test] - fn stress() { + test!(fn stress() { static AMT: int = 10000; let (mut p1, c1) = Chan::::new(); let (mut p2, c2) = Chan::::new(); @@ -442,69 +438,5 @@ mod test { ) c3.send(()); } - } - - #[test] - #[ignore(cfg(windows))] // FIXME(#11003) - fn stress_native() { - use std::rt::thread::Thread; - use std::unstable::run_in_bare_thread; - static AMT: int = 10000; - - do run_in_bare_thread { - let (mut p1, c1) = Chan::::new(); - let (mut p2, c2) = Chan::::new(); - let (p3, c3) = Chan::<()>::new(); - - let t = do Thread::start { - for i in range(0, AMT) { - if i % 2 == 0 { - c1.send(i); - } else { - c2.send(i); - } - p3.recv(); - } - }; - - for i in range(0, AMT) { - select! ( - i1 = p1.recv() => { assert!(i % 2 == 0 && i == i1); }, - i2 = p2.recv() => { assert!(i % 2 == 1 && i == i2); } - ) - c3.send(()); - } - t.join(); - } - } - - #[test] - #[ignore(cfg(windows))] // FIXME(#11003) - fn native_both_ready() { - use std::rt::thread::Thread; - use std::unstable::run_in_bare_thread; - - do run_in_bare_thread { - let (mut p1, c1) = Chan::::new(); - let (mut p2, c2) = Chan::::new(); - let (p3, c3) = Chan::<()>::new(); - - let t = do Thread::start { - c1.send(1); - c2.send(2); - p3.recv(); - }; - - select! ( - a = p1.recv() => { assert_eq!(a, 1); }, - b = p2.recv() => { assert_eq!(b, 2); } - ) - select! ( - a = p1.recv() => { assert_eq!(a, 1); }, - b = p2.recv() => { assert_eq!(b, 2); } - ) - c3.send(()); - t.join(); - } - } + }) } diff --git a/src/libstd/io/fs.rs b/src/libstd/io/fs.rs index ded1d254f3f..b4838d534dc 100644 --- a/src/libstd/io/fs.rs +++ b/src/libstd/io/fs.rs @@ -714,7 +714,7 @@ mod test { } } - fn tmpdir() -> TempDir { + pub fn tmpdir() -> TempDir { use os; use rand; let ret = os::tmpdir().join(format!("rust-{}", rand::random::())); @@ -722,32 +722,7 @@ mod test { TempDir(ret) } - macro_rules! test ( - { fn $name:ident() $b:block } => ( - mod $name { - use prelude::*; - use io::{SeekSet, SeekCur, SeekEnd, io_error, Read, Open, - ReadWrite}; - use io; - use str; - use io::fs::{File, rmdir, mkdir, readdir, rmdir_recursive, - mkdir_recursive, copy, unlink, stat, symlink, link, - readlink, chmod, lstat, change_file_times}; - use io::fs::test::tmpdir; - use util; - - fn f() $b - - #[test] fn uv() { f() } - #[test] fn native() { - use rt::test::run_in_newsched_task; - run_in_newsched_task(f); - } - } - ) - ) - - test!(fn file_test_io_smoke_test() { + iotest!(fn file_test_io_smoke_test() { let message = "it's alright. have a good time"; let tmpdir = tmpdir(); let filename = &tmpdir.join("file_rt_io_file_test.txt"); @@ -767,7 +742,7 @@ mod test { unlink(filename); }) - test!(fn invalid_path_raises() { + iotest!(fn invalid_path_raises() { let tmpdir = tmpdir(); let filename = &tmpdir.join("file_that_does_not_exist.txt"); let mut called = false; @@ -780,7 +755,7 @@ mod test { assert!(called); }) - test!(fn file_test_iounlinking_invalid_path_should_raise_condition() { + iotest!(fn file_test_iounlinking_invalid_path_should_raise_condition() { let tmpdir = tmpdir(); let filename = &tmpdir.join("file_another_file_that_does_not_exist.txt"); let mut called = false; @@ -790,7 +765,7 @@ mod test { assert!(called); }) - test!(fn file_test_io_non_positional_read() { + iotest!(fn file_test_io_non_positional_read() { let message: &str = "ten-four"; let mut read_mem = [0, .. 8]; let tmpdir = tmpdir(); @@ -815,7 +790,7 @@ mod test { assert_eq!(read_str, message); }) - test!(fn file_test_io_seek_and_tell_smoke_test() { + iotest!(fn file_test_io_seek_and_tell_smoke_test() { let message = "ten-four"; let mut read_mem = [0, .. 4]; let set_cursor = 4 as u64; @@ -841,7 +816,7 @@ mod test { assert_eq!(tell_pos_post_read, message.len() as u64); }) - test!(fn file_test_io_seek_and_write() { + iotest!(fn file_test_io_seek_and_write() { let initial_msg = "food-is-yummy"; let overwrite_msg = "-the-bar!!"; let final_msg = "foo-the-bar!!"; @@ -864,7 +839,7 @@ mod test { assert!(read_str == final_msg.to_owned()); }) - test!(fn file_test_io_seek_shakedown() { + iotest!(fn file_test_io_seek_shakedown() { use std::str; // 01234567890123 let initial_msg = "qwer-asdf-zxcv"; let chunk_one: &str = "qwer"; @@ -895,7 +870,7 @@ mod test { unlink(filename); }) - test!(fn file_test_stat_is_correct_on_is_file() { + iotest!(fn file_test_stat_is_correct_on_is_file() { let tmpdir = tmpdir(); let filename = &tmpdir.join("file_stat_correct_on_is_file.txt"); { @@ -908,7 +883,7 @@ mod test { unlink(filename); }) - test!(fn file_test_stat_is_correct_on_is_dir() { + iotest!(fn file_test_stat_is_correct_on_is_dir() { let tmpdir = tmpdir(); let filename = &tmpdir.join("file_stat_correct_on_is_dir"); mkdir(filename, io::UserRWX); @@ -917,7 +892,7 @@ mod test { rmdir(filename); }) - test!(fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() { + iotest!(fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() { let tmpdir = tmpdir(); let dir = &tmpdir.join("fileinfo_false_on_dir"); mkdir(dir, io::UserRWX); @@ -925,7 +900,7 @@ mod test { rmdir(dir); }) - test!(fn file_test_fileinfo_check_exists_before_and_after_file_creation() { + iotest!(fn file_test_fileinfo_check_exists_before_and_after_file_creation() { let tmpdir = tmpdir(); let file = &tmpdir.join("fileinfo_check_exists_b_and_a.txt"); File::create(file).write(bytes!("foo")); @@ -934,7 +909,7 @@ mod test { assert!(!file.exists()); }) - test!(fn file_test_directoryinfo_check_exists_before_and_after_mkdir() { + iotest!(fn file_test_directoryinfo_check_exists_before_and_after_mkdir() { let tmpdir = tmpdir(); let dir = &tmpdir.join("before_and_after_dir"); assert!(!dir.exists()); @@ -945,7 +920,7 @@ mod test { assert!(!dir.exists()); }) - test!(fn file_test_directoryinfo_readdir() { + iotest!(fn file_test_directoryinfo_readdir() { use std::str; let tmpdir = tmpdir(); let dir = &tmpdir.join("di_readdir"); @@ -976,11 +951,11 @@ mod test { rmdir(dir); }) - test!(fn recursive_mkdir_slash() { + iotest!(fn recursive_mkdir_slash() { mkdir_recursive(&Path::new("/"), io::UserRWX); }) - test!(fn unicode_path_is_dir() { + iotest!(fn unicode_path_is_dir() { assert!(Path::new(".").is_dir()); assert!(!Path::new("test/stdtest/fs.rs").is_dir()); @@ -998,7 +973,7 @@ mod test { assert!(filepath.exists()); }) - test!(fn unicode_path_exists() { + iotest!(fn unicode_path_exists() { assert!(Path::new(".").exists()); assert!(!Path::new("test/nonexistent-bogus-path").exists()); @@ -1010,7 +985,7 @@ mod test { assert!(!Path::new("test/unicode-bogus-path-각丁ー再见").exists()); }) - test!(fn copy_file_does_not_exist() { + iotest!(fn copy_file_does_not_exist() { let from = Path::new("test/nonexistent-bogus-path"); let to = Path::new("test/other-bogus-path"); match io::result(|| copy(&from, &to)) { @@ -1022,7 +997,7 @@ mod test { } }) - test!(fn copy_file_ok() { + iotest!(fn copy_file_ok() { let tmpdir = tmpdir(); let input = tmpdir.join("in.txt"); let out = tmpdir.join("out.txt"); @@ -1035,7 +1010,7 @@ mod test { assert_eq!(input.stat().perm, out.stat().perm); }) - test!(fn copy_file_dst_dir() { + iotest!(fn copy_file_dst_dir() { let tmpdir = tmpdir(); let out = tmpdir.join("out"); @@ -1045,7 +1020,7 @@ mod test { } }) - test!(fn copy_file_dst_exists() { + iotest!(fn copy_file_dst_exists() { let tmpdir = tmpdir(); let input = tmpdir.join("in"); let output = tmpdir.join("out"); @@ -1058,7 +1033,7 @@ mod test { (bytes!("foo")).to_owned()); }) - test!(fn copy_file_src_dir() { + iotest!(fn copy_file_src_dir() { let tmpdir = tmpdir(); let out = tmpdir.join("out"); @@ -1068,7 +1043,7 @@ mod test { assert!(!out.exists()); }) - test!(fn copy_file_preserves_perm_bits() { + iotest!(fn copy_file_preserves_perm_bits() { let tmpdir = tmpdir(); let input = tmpdir.join("in.txt"); let out = tmpdir.join("out.txt"); @@ -1083,7 +1058,7 @@ mod test { }) #[cfg(not(windows))] // FIXME(#10264) operation not permitted? - test!(fn symlinks_work() { + iotest!(fn symlinks_work() { let tmpdir = tmpdir(); let input = tmpdir.join("in.txt"); let out = tmpdir.join("out.txt"); @@ -1098,14 +1073,14 @@ mod test { }) #[cfg(not(windows))] // apparently windows doesn't like symlinks - test!(fn symlink_noexist() { + iotest!(fn symlink_noexist() { let tmpdir = tmpdir(); // symlinks can point to things that don't exist symlink(&tmpdir.join("foo"), &tmpdir.join("bar")); assert!(readlink(&tmpdir.join("bar")).unwrap() == tmpdir.join("foo")); }) - test!(fn readlink_not_symlink() { + iotest!(fn readlink_not_symlink() { let tmpdir = tmpdir(); match io::result(|| readlink(&*tmpdir)) { Ok(..) => fail!("wanted a failure"), @@ -1113,7 +1088,7 @@ mod test { } }) - test!(fn links_work() { + iotest!(fn links_work() { let tmpdir = tmpdir(); let input = tmpdir.join("in.txt"); let out = tmpdir.join("out.txt"); @@ -1139,7 +1114,7 @@ mod test { } }) - test!(fn chmod_works() { + iotest!(fn chmod_works() { let tmpdir = tmpdir(); let file = tmpdir.join("in.txt"); @@ -1156,7 +1131,7 @@ mod test { chmod(&file, io::UserFile); }) - test!(fn sync_doesnt_kill_anything() { + iotest!(fn sync_doesnt_kill_anything() { let tmpdir = tmpdir(); let path = tmpdir.join("in.txt"); @@ -1169,7 +1144,7 @@ mod test { drop(file); }) - test!(fn truncate_works() { + iotest!(fn truncate_works() { let tmpdir = tmpdir(); let path = tmpdir.join("in.txt"); @@ -1200,7 +1175,7 @@ mod test { drop(file); }) - test!(fn open_flavors() { + iotest!(fn open_flavors() { let tmpdir = tmpdir(); match io::result(|| File::open_mode(&tmpdir.join("a"), io::Open, diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 0852c4cadb6..8481de73c7f 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -313,6 +313,10 @@ pub use self::net::udp::UdpStream; pub use self::pipe::PipeStream; pub use self::process::Process; +/// Testing helpers +#[cfg(test)] +mod test; + /// Synchronous, non-blocking filesystem operations. pub mod fs; diff --git a/src/libstd/io/net/tcp.rs b/src/libstd/io/net/tcp.rs index bd7d8bacb38..e7787692dd2 100644 --- a/src/libstd/io/net/tcp.rs +++ b/src/libstd/io/net/tcp.rs @@ -176,7 +176,7 @@ mod test { #[test] fn smoke_test_ip4() { let addr = next_test_ip4(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { port.recv(); @@ -195,7 +195,7 @@ mod test { #[test] fn smoke_test_ip6() { let addr = next_test_ip6(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { port.recv(); @@ -214,7 +214,7 @@ mod test { #[test] fn read_eof_ip4() { let addr = next_test_ip4(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { port.recv(); @@ -233,7 +233,7 @@ mod test { #[test] fn read_eof_ip6() { let addr = next_test_ip6(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { port.recv(); @@ -252,10 +252,10 @@ mod test { #[test] fn read_eof_twice_ip4() { let addr = next_test_ip4(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { - port.take().recv(); + port.recv(); let _stream = TcpStream::connect(addr); // Close } @@ -281,7 +281,7 @@ mod test { #[test] fn read_eof_twice_ip6() { let addr = next_test_ip6(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { port.recv(); @@ -310,7 +310,7 @@ mod test { #[test] fn write_close_ip4() { let addr = next_test_ip4(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { port.recv(); @@ -342,7 +342,7 @@ mod test { #[test] fn write_close_ip6() { let addr = next_test_ip6(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { port.recv(); @@ -375,7 +375,7 @@ mod test { fn multiple_connect_serial_ip4() { let addr = next_test_ip4(); let max = 10; - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { port.recv(); @@ -398,7 +398,7 @@ mod test { fn multiple_connect_serial_ip6() { let addr = next_test_ip6(); let max = 10; - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { port.recv(); @@ -421,16 +421,15 @@ mod test { fn multiple_connect_interleaved_greedy_schedule_ip4() { let addr = next_test_ip4(); static MAX: int = 10; - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { let mut acceptor = TcpListener::bind(addr).listen(); chan.send(()); for (i, stream) in acceptor.incoming().enumerate().take(MAX as uint) { - let stream = Cell::new(stream); // Start another task to handle the connection do spawn { - let mut stream = stream.take(); + let mut stream = stream; let mut buf = [0]; stream.read(buf); assert!(buf[0] == i as u8); @@ -460,15 +459,15 @@ mod test { fn multiple_connect_interleaved_greedy_schedule_ip6() { let addr = next_test_ip6(); static MAX: int = 10; - let (port, chan) = oneshot(); + let (port, chan) = Chan::<()>::new(); do spawn { let mut acceptor = TcpListener::bind(addr).listen(); + chan.send(()); for (i, stream) in acceptor.incoming().enumerate().take(MAX as uint) { - let stream = Cell::new(stream); // Start another task to handle the connection do spawn { - let mut stream = stream.take(); + let mut stream = stream; let mut buf = [0]; stream.read(buf); assert!(buf[0] == i as u8); @@ -498,16 +497,15 @@ mod test { fn multiple_connect_interleaved_lazy_schedule_ip4() { let addr = next_test_ip4(); static MAX: int = 10; - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { let mut acceptor = TcpListener::bind(addr).listen(); chan.send(()); for stream in acceptor.incoming().take(MAX as uint) { - let stream = Cell::new(stream); // Start another task to handle the connection do spawn { - let mut stream = stream.take(); + let mut stream = stream; let mut buf = [0]; stream.read(buf); assert!(buf[0] == 99); @@ -536,16 +534,15 @@ mod test { fn multiple_connect_interleaved_lazy_schedule_ip6() { let addr = next_test_ip6(); static MAX: int = 10; - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { let mut acceptor = TcpListener::bind(addr).listen(); chan.send(()); for stream in acceptor.incoming().take(MAX as uint) { - let stream = Cell::new(stream); // Start another task to handle the connection do spawn { - let mut stream = stream.take(); + let mut stream = stream; let mut buf = [0]; stream.read(buf); assert!(buf[0] == 99); @@ -573,23 +570,18 @@ mod test { #[cfg(test)] fn socket_name(addr: SocketAddr) { - do run_in_mt_newsched_task { - do spawntask { - let mut listener = TcpListener::bind(addr).unwrap(); - - // Make sure socket_name gives - // us the socket we binded to. - let so_name = listener.socket_name(); - assert!(so_name.is_some()); - assert_eq!(addr, so_name.unwrap()); + let mut listener = TcpListener::bind(addr).unwrap(); - } - } + // Make sure socket_name gives + // us the socket we binded to. + let so_name = listener.socket_name(); + assert!(so_name.is_some()); + assert_eq!(addr, so_name.unwrap()); } #[cfg(test)] fn peer_name(addr: SocketAddr) { - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { let mut acceptor = TcpListener::bind(addr).listen(); diff --git a/src/libstd/io/net/udp.rs b/src/libstd/io/net/udp.rs index 159823ba2b5..7cb8f741cf3 100644 --- a/src/libstd/io/net/udp.rs +++ b/src/libstd/io/net/udp.rs @@ -101,6 +101,7 @@ mod test { use super::*; use io::net::ip::{Ipv4Addr, SocketAddr}; use io::*; + use io::test::*; use prelude::*; #[test] #[ignore] @@ -121,7 +122,7 @@ mod test { fn socket_smoke_test_ip4() { let server_ip = next_test_ip4(); let client_ip = next_test_ip4(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { match UdpSocket::bind(client_ip) { @@ -154,7 +155,7 @@ mod test { fn socket_smoke_test_ip6() { let server_ip = next_test_ip6(); let client_ip = next_test_ip6(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::<()>::new(); do spawn { match UdpSocket::bind(client_ip) { @@ -168,7 +169,7 @@ mod test { match UdpSocket::bind(server_ip) { Some(ref mut server) => { - chan.take().send(()); + chan.send(()); let mut buf = [0]; match server.recvfrom(buf) { Some((nread, src)) => { @@ -187,7 +188,7 @@ mod test { fn stream_smoke_test_ip4() { let server_ip = next_test_ip4(); let client_ip = next_test_ip4(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { match UdpSocket::bind(client_ip) { @@ -223,7 +224,7 @@ mod test { fn stream_smoke_test_ip6() { let server_ip = next_test_ip6(); let client_ip = next_test_ip6(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { match UdpSocket::bind(client_ip) { diff --git a/src/libstd/io/net/unix.rs b/src/libstd/io/net/unix.rs index 8fd256a22f9..59a6903adbf 100644 --- a/src/libstd/io/net/unix.rs +++ b/src/libstd/io/net/unix.rs @@ -141,11 +141,12 @@ mod tests { use prelude::*; use super::*; use io::*; + use io::test::*; fn smalltest(server: proc(UnixStream), client: proc(UnixStream)) { let path1 = next_test_unix(); let path2 = path1.clone(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { port.recv(); @@ -229,7 +230,7 @@ mod tests { let times = 10; let path1 = next_test_unix(); let path2 = path1.clone(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { port.recv(); diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 88047aecda2..5249d331f72 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -308,23 +308,10 @@ impl Writer for StdWriter { #[cfg(test)] mod tests { - use super::*; - use rt::test::run_in_newsched_task; - - #[test] - fn smoke_uv() { + iotest!(fn smoke() { // Just make sure we can acquire handles stdin(); stdout(); stderr(); - } - - #[test] - fn smoke_native() { - do run_in_newsched_task { - stdin(); - stdout(); - stderr(); - } - } + }) } diff --git a/src/libstd/io/test.rs b/src/libstd/io/test.rs index 212e4ebffa8..dd24150e03e 100644 --- a/src/libstd/io/test.rs +++ b/src/libstd/io/test.rs @@ -8,9 +8,48 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[macro_escape]; + +use os; +use prelude::*; +use rand; +use rand::Rng; +use std::io::net::ip::*; +use sync::atomics::{AtomicUint, INIT_ATOMIC_UINT, Relaxed}; + +macro_rules! iotest ( + { fn $name:ident() $b:block } => ( + mod $name { + #[allow(unused_imports)]; + + use super::super::*; + use super::*; + use io; + use prelude::*; + use io::*; + use io::fs::*; + use io::net::tcp::*; + use io::net::ip::*; + use io::net::udp::*; + use io::net::unix::*; + use str; + use util; + + fn f() $b + + #[test] fn green() { f() } + #[test] fn native() { + use native; + let (p, c) = Chan::new(); + do native::task::spawn { c.send(f()) } + p.recv(); + } + } + ) +) + /// Get a port number, starting at 9600, for use in tests pub fn next_test_port() -> u16 { - use unstable::atomics::{AtomicUint, INIT_ATOMIC_UINT, Relaxed}; static mut next_offset: AtomicUint = INIT_ATOMIC_UINT; unsafe { base_port() + next_offset.fetch_add(1, Relaxed) as u16 @@ -44,9 +83,6 @@ all want to use ports. This function figures out which workspace it is running in and assigns a port range based on it. */ fn base_port() -> u16 { - use os; - use str::StrSlice; - use vec::ImmutableVector; let base = 9600u16; let range = 1000u16; diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 200e4e63261..4f633a63bab 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -65,13 +65,15 @@ // When testing libstd, bring in libuv as the I/O backend so tests can print // things and all of the std::io tests have an I/O interface to run on top // of -#[cfg(test)] extern mod rustuv = "rustuv#0.9-pre"; +#[cfg(test)] extern mod rustuv = "rustuv"; +#[cfg(test)] extern mod native = "native"; +#[cfg(test)] extern mod green = "green"; // Make extra accessible for benchmarking -#[cfg(test)] extern mod extra = "extra#0.9-pre"; +#[cfg(test)] extern mod extra = "extra"; // Make std testable by not duplicating lang items. See #2912 -#[cfg(test)] extern mod realstd = "std#0.9-pre"; +#[cfg(test)] extern mod realstd = "std"; #[cfg(test)] pub use kinds = realstd::kinds; #[cfg(test)] pub use ops = realstd::ops; #[cfg(test)] pub use cmp = realstd::cmp; diff --git a/src/libstd/local_data.rs b/src/libstd/local_data.rs index 652aa4d8198..d7e11d2f3a7 100644 --- a/src/libstd/local_data.rs +++ b/src/libstd/local_data.rs @@ -432,6 +432,7 @@ mod tests { } #[test] + #[allow(dead_code)] fn test_tls_overwrite_multiple_types() { static str_key: Key<~str> = &Key; static box_key: Key<@()> = &Key; diff --git a/src/libstd/rt/local.rs b/src/libstd/rt/local.rs index ea27956ad90..1c04b6b43ce 100644 --- a/src/libstd/rt/local.rs +++ b/src/libstd/rt/local.rs @@ -49,7 +49,6 @@ impl Local> for Task { mod test { use option::None; use unstable::run_in_bare_thread; - use rt::test::*; use super::*; use rt::task::Task; use rt::local_ptr; @@ -58,8 +57,7 @@ mod test { fn thread_local_task_smoke_test() { do run_in_bare_thread { local_ptr::init(); - let mut sched = ~new_test_uv_sched(); - let task = ~Task::new_root(&mut sched.stack_pool, None, proc(){}); + let task = ~Task::new(); Local::put(task); let task: ~Task = Local::take(); cleanup_task(task); @@ -70,12 +68,11 @@ mod test { fn thread_local_task_two_instances() { do run_in_bare_thread { local_ptr::init(); - let mut sched = ~new_test_uv_sched(); - let task = ~Task::new_root(&mut sched.stack_pool, None, proc(){}); + let task = ~Task::new(); Local::put(task); let task: ~Task = Local::take(); cleanup_task(task); - let task = ~Task::new_root(&mut sched.stack_pool, None, proc(){}); + let task = ~Task::new(); Local::put(task); let task: ~Task = Local::take(); cleanup_task(task); @@ -87,8 +84,7 @@ mod test { fn borrow_smoke_test() { do run_in_bare_thread { local_ptr::init(); - let mut sched = ~new_test_uv_sched(); - let task = ~Task::new_root(&mut sched.stack_pool, None, proc(){}); + let task = ~Task::new(); Local::put(task); unsafe { @@ -103,8 +99,7 @@ mod test { fn borrow_with_return() { do run_in_bare_thread { local_ptr::init(); - let mut sched = ~new_test_uv_sched(); - let task = ~Task::new_root(&mut sched.stack_pool, None, proc(){}); + let task = ~Task::new(); Local::put(task); { @@ -116,5 +111,9 @@ mod test { } } + fn cleanup_task(mut t: ~Task) { + t.destroyed = true; + } + } diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs index 7602d7b0564..c0164891cd4 100644 --- a/src/libstd/rt/task.rs +++ b/src/libstd/rt/task.rs @@ -176,8 +176,12 @@ impl Task { // Cleanup the dynamic borrowck debugging info borrowck::clear_task_borrow_list(); + // TODO: dox + unsafe { + let me: *mut Task = Local::unsafe_borrow(); + (*me).death.collect_failure((*me).unwinder.result()); + } let mut me: ~Task = Local::take(); - me.death.collect_failure(me.unwinder.result()); me.destroyed = true; return me; } @@ -375,92 +379,76 @@ impl Drop for Death { #[cfg(test)] mod test { use super::*; - use rt::test::*; use prelude::*; + use task; #[test] fn local_heap() { - do run_in_newsched_task() { - let a = @5; - let b = a; - assert!(*a == 5); - assert!(*b == 5); - } + let a = @5; + let b = a; + assert!(*a == 5); + assert!(*b == 5); } #[test] fn tls() { use local_data; - do run_in_newsched_task() { - local_data_key!(key: @~str) - local_data::set(key, @~"data"); - assert!(*local_data::get(key, |k| k.map(|k| *k)).unwrap() == ~"data"); - local_data_key!(key2: @~str) - local_data::set(key2, @~"data"); - assert!(*local_data::get(key2, |k| k.map(|k| *k)).unwrap() == ~"data"); - } + local_data_key!(key: @~str) + local_data::set(key, @~"data"); + assert!(*local_data::get(key, |k| k.map(|k| *k)).unwrap() == ~"data"); + local_data_key!(key2: @~str) + local_data::set(key2, @~"data"); + assert!(*local_data::get(key2, |k| k.map(|k| *k)).unwrap() == ~"data"); } #[test] fn unwind() { - do run_in_newsched_task() { - let result = spawntask_try(proc()()); - rtdebug!("trying first assert"); - assert!(result.is_ok()); - let result = spawntask_try(proc() fail!()); - rtdebug!("trying second assert"); - assert!(result.is_err()); - } + let result = task::try(proc()()); + rtdebug!("trying first assert"); + assert!(result.is_ok()); + let result = task::try::<()>(proc() fail!()); + rtdebug!("trying second assert"); + assert!(result.is_err()); } #[test] fn rng() { - do run_in_uv_task() { - use rand::{rng, Rng}; - let mut r = rng(); - let _ = r.next_u32(); - } + use rand::{rng, Rng}; + let mut r = rng(); + let _ = r.next_u32(); } #[test] fn logging() { - do run_in_uv_task() { - info!("here i am. logging in a newsched task"); - } + info!("here i am. logging in a newsched task"); } #[test] fn comm_stream() { - do run_in_newsched_task() { - let (port, chan) = Chan::new(); - chan.send(10); - assert!(port.recv() == 10); - } + let (port, chan) = Chan::new(); + chan.send(10); + assert!(port.recv() == 10); } #[test] fn comm_shared_chan() { - do run_in_newsched_task() { - let (port, chan) = SharedChan::new(); - chan.send(10); - assert!(port.recv() == 10); - } + let (port, chan) = SharedChan::new(); + chan.send(10); + assert!(port.recv() == 10); } #[test] fn heap_cycles() { use option::{Option, Some, None}; - do run_in_newsched_task { - struct List { - next: Option<@mut List>, - } + struct List { + next: Option<@mut List>, + } - let a = @mut List { next: None }; - let b = @mut List { next: Some(a) }; + let a = @mut List { next: None }; + let b = @mut List { next: Some(a) }; - a.next = Some(b); - } + a.next = Some(b); } #[test] @@ -471,8 +459,8 @@ mod test { #[test] fn block_and_wake() { - do with_test_task |task| { - BlockedTask::block(task).wake().unwrap() - } + let task = ~Task::new(); + let mut task = BlockedTask::block(task).wake().unwrap(); + task.destroyed = true; } } diff --git a/src/libstd/run.rs b/src/libstd/run.rs index 15c0986f899..69704c855ee 100644 --- a/src/libstd/run.rs +++ b/src/libstd/run.rs @@ -426,13 +426,13 @@ mod tests { } fn writeclose(fd: c_int, s: &str) { - let mut writer = PipeStream::open(fd as int); + let mut writer = PipeStream::open(fd); writer.write(s.as_bytes()); } fn readclose(fd: c_int) -> ~str { let mut res = ~[]; - let mut reader = PipeStream::open(fd as int); + let mut reader = PipeStream::open(fd); let mut buf = [0, ..1024]; loop { match reader.read(buf) { diff --git a/src/libstd/sync/arc.rs b/src/libstd/sync/arc.rs index 7632ec6cf29..7b94a3acc2b 100644 --- a/src/libstd/sync/arc.rs +++ b/src/libstd/sync/arc.rs @@ -131,7 +131,6 @@ impl Drop for UnsafeArc{ mod tests { use prelude::*; use super::UnsafeArc; - use task; use mem::size_of; #[test] diff --git a/src/libstd/sync/mpmc_bounded_queue.rs b/src/libstd/sync/mpmc_bounded_queue.rs index b623976306d..fe51de4e42d 100644 --- a/src/libstd/sync/mpmc_bounded_queue.rs +++ b/src/libstd/sync/mpmc_bounded_queue.rs @@ -163,8 +163,8 @@ impl Clone for Queue { mod tests { use prelude::*; use option::*; - use task; use super::Queue; + use native; #[test] fn test() { @@ -172,14 +172,17 @@ mod tests { let nmsgs = 1000u; let mut q = Queue::with_capacity(nthreads*nmsgs); assert_eq!(None, q.pop()); + let (port, chan) = SharedChan::new(); for _ in range(0, nthreads) { let q = q.clone(); - do task::spawn_sched(task::SingleThreaded) { + let chan = chan.clone(); + do native::task::spawn { let mut q = q; for i in range(0, nmsgs) { assert!(q.push(i)); } + chan.send(()); } } @@ -188,7 +191,7 @@ mod tests { let (completion_port, completion_chan) = Chan::new(); completion_ports.push(completion_port); let q = q.clone(); - do task::spawn_sched(task::SingleThreaded) { + do native::task::spawn { let mut q = q; let mut i = 0u; loop { @@ -207,5 +210,8 @@ mod tests { for completion_port in completion_ports.mut_iter() { assert_eq!(nmsgs, completion_port.recv()); } + for _ in range(0, nthreads) { + port.recv(); + } } } diff --git a/src/libstd/sync/mpsc_queue.rs b/src/libstd/sync/mpsc_queue.rs index 89e56e3fa67..a249d6ed2e8 100644 --- a/src/libstd/sync/mpsc_queue.rs +++ b/src/libstd/sync/mpsc_queue.rs @@ -203,8 +203,8 @@ impl Consumer { mod tests { use prelude::*; - use task; use super::{queue, Data, Empty, Inconsistent}; + use native; #[test] fn test_full() { @@ -222,14 +222,17 @@ mod tests { Empty => {} Inconsistent | Data(..) => fail!() } + let (port, chan) = SharedChan::new(); for _ in range(0, nthreads) { let q = p.clone(); - do task::spawn_sched(task::SingleThreaded) { + let chan = chan.clone(); + do native::task::spawn { let mut q = q; for i in range(0, nmsgs) { q.push(i); } + chan.send(()); } } @@ -240,6 +243,9 @@ mod tests { Data(_) => { i += 1 } } } + for _ in range(0, nthreads) { + port.recv(); + } } } diff --git a/src/libstd/sync/spsc_queue.rs b/src/libstd/sync/spsc_queue.rs index c4abba04659..6f1b887c271 100644 --- a/src/libstd/sync/spsc_queue.rs +++ b/src/libstd/sync/spsc_queue.rs @@ -268,7 +268,7 @@ impl Drop for State { mod test { use prelude::*; use super::queue; - use task; + use native; #[test] fn smoke() { @@ -314,7 +314,8 @@ mod test { fn stress_bound(bound: uint) { let (c, mut p) = queue(bound, ()); - do task::spawn_sched(task::SingleThreaded) { + let (port, chan) = Chan::new(); + do native::task::spawn { let mut c = c; for _ in range(0, 100000) { loop { @@ -325,10 +326,12 @@ mod test { } } } + chan.send(()); } for _ in range(0, 100000) { p.push(1); } + port.recv(); } } } diff --git a/src/libstd/task.rs b/src/libstd/task.rs index 4632a3cf6e0..3b9cde5f44d 100644 --- a/src/libstd/task.rs +++ b/src/libstd/task.rs @@ -64,6 +64,7 @@ use send_str::{SendStr, IntoSendStr}; use str::Str; use util; +#[cfg(test)] use any::{AnyOwnExt, AnyRefExt}; #[cfg(test)] use comm::SharedChan; #[cfg(test)] use ptr; #[cfg(test)] use result; @@ -385,59 +386,43 @@ pub fn failing() -> bool { #[test] fn test_unnamed_task() { - use rt::test::run_in_uv_task; - - do run_in_uv_task { - do spawn { - with_task_name(|name| { - assert!(name.is_none()); - }) - } + do spawn { + with_task_name(|name| { + assert!(name.is_none()); + }) } } #[test] fn test_owned_named_task() { - use rt::test::run_in_uv_task; - - do run_in_uv_task { - let mut t = task(); - t.name(~"ada lovelace"); - do t.spawn { - with_task_name(|name| { - assert!(name.unwrap() == "ada lovelace"); - }) - } + let mut t = task(); + t.name(~"ada lovelace"); + do t.spawn { + with_task_name(|name| { + assert!(name.unwrap() == "ada lovelace"); + }) } } #[test] fn test_static_named_task() { - use rt::test::run_in_uv_task; - - do run_in_uv_task { - let mut t = task(); - t.name("ada lovelace"); - do t.spawn { - with_task_name(|name| { - assert!(name.unwrap() == "ada lovelace"); - }) - } + let mut t = task(); + t.name("ada lovelace"); + do t.spawn { + with_task_name(|name| { + assert!(name.unwrap() == "ada lovelace"); + }) } } #[test] fn test_send_named_task() { - use rt::test::run_in_uv_task; - - do run_in_uv_task { - let mut t = task(); - t.name("ada lovelace".into_send_str()); - do t.spawn { - with_task_name(|name| { - assert!(name.unwrap() == "ada lovelace"); - }) - } + let mut t = task(); + t.name("ada lovelace".into_send_str()); + do t.spawn { + with_task_name(|name| { + assert!(name.unwrap() == "ada lovelace"); + }) } } @@ -508,28 +493,19 @@ fn test_try_fail() { } } -#[cfg(test)] -fn get_sched_id() -> int { - use rt::sched::Scheduler; - let mut sched = Local::borrow(None::); - sched.get().sched_id() as int -} - #[test] fn test_spawn_sched() { + use clone::Clone; + let (po, ch) = SharedChan::new(); fn f(i: int, ch: SharedChan<()>) { - let parent_sched_id = get_sched_id(); - - do spawn_sched(SingleThreaded) { - let child_sched_id = get_sched_id(); - assert!(parent_sched_id != child_sched_id); - + let ch = ch.clone(); + do spawn { if (i == 0) { ch.send(()); } else { - f(i - 1, ch.clone()); + f(i - 1, ch); } }; @@ -542,16 +518,9 @@ fn test_spawn_sched() { fn test_spawn_sched_childs_on_default_sched() { let (po, ch) = Chan::new(); - // Assuming tests run on the default scheduler - let default_id = get_sched_id(); - - do spawn_sched(SingleThreaded) { + do spawn { let ch = ch; - let parent_sched_id = get_sched_id(); do spawn { - let child_sched_id = get_sched_id(); - assert!(parent_sched_id != child_sched_id); - assert_eq!(child_sched_id, default_id); ch.send(()); }; }; @@ -562,6 +531,7 @@ fn test_spawn_sched_childs_on_default_sched() { #[test] fn test_spawn_sched_blocking() { use unstable::mutex::Mutex; + use num::Times; unsafe { @@ -574,7 +544,7 @@ fn test_spawn_sched_blocking() { let mut lock = Mutex::new(); let lock2 = lock.clone(); - do spawn_sched(SingleThreaded) { + do spawn { let mut lock = lock2; lock.lock(); @@ -681,11 +651,7 @@ fn test_child_doesnt_ref_parent() { #[test] fn test_simple_newsched_spawn() { - use rt::test::run_in_uv_task; - - do run_in_uv_task { - spawn(proc()()) - } + spawn(proc()()) } #[test] diff --git a/src/libstd/unstable/mutex.rs b/src/libstd/unstable/mutex.rs index eaf716f2726..5b2fac8e74e 100644 --- a/src/libstd/unstable/mutex.rs +++ b/src/libstd/unstable/mutex.rs @@ -333,12 +333,12 @@ mod test { fn somke_cond() { static mut lock: Mutex = MUTEX_INIT; unsafe { + lock.lock(); let t = do Thread::start { lock.lock(); lock.signal(); lock.unlock(); }; - lock.lock(); lock.wait(); lock.unlock(); t.join(); diff --git a/src/libstd/unstable/stack.rs b/src/libstd/unstable/stack.rs index 46a3a80be25..b8788b8c55c 100644 --- a/src/libstd/unstable/stack.rs +++ b/src/libstd/unstable/stack.rs @@ -24,11 +24,6 @@ //! detection is not guaranteed to continue in the future. Usage of this module //! is discouraged unless absolutely necessary. -use rt::task::Task; -use option::None; -use rt::local::Local; -use unstable::intrinsics; - static RED_ZONE: uint = 20 * 1024; /// This function is invoked from rust's current __morestack function. Segmented @@ -41,6 +36,10 @@ static RED_ZONE: uint = 20 * 1024; // irrelevant for documentation purposes. #[cfg(not(test))] // in testing, use the original libstd's version pub extern "C" fn rust_stack_exhausted() { + use rt::task::Task; + use option::None; + use rt::local::Local; + use unstable::intrinsics; unsafe { // We're calling this function because the stack just ran out. We need diff --git a/src/libstd/unstable/sync.rs b/src/libstd/unstable/sync.rs index ad36f71cdea..687efea939b 100644 --- a/src/libstd/unstable/sync.rs +++ b/src/libstd/unstable/sync.rs @@ -161,9 +161,8 @@ impl Exclusive { mod tests { use option::*; use prelude::*; - use super::{Exclusive, UnsafeArc, atomic}; + use super::Exclusive; use task; - use mem::size_of; #[test] fn exclusive_new_arc() { diff --git a/src/libstd/vec.rs b/src/libstd/vec.rs index 97d4c2f6d1b..86f28c28f69 100644 --- a/src/libstd/vec.rs +++ b/src/libstd/vec.rs @@ -2874,7 +2874,6 @@ impl Extendable for ~[A] { #[cfg(test)] mod tests { - use option::{None, Some}; use mem; use vec::*; use cmp::*; -- cgit 1.4.1-3-g733a5 From f5d9b2ca6d9a360112f06b3044897c22736c52b8 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 13 Dec 2013 00:02:54 -0800 Subject: native: Add tests and cleanup entry points This adds a few smoke tests associated with libnative tasks (not much code to test here anyway), and cleans up the entry points a little bit to be a little more like libgreen. The I/O code doesn't need much testing because that's all tested in libstd (with the iotest! macro). --- src/libnative/io/file.rs | 3 +- src/libnative/lib.rs | 56 ++++++++++++++++++++-------- src/libnative/task.rs | 97 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+), 17 deletions(-) (limited to 'src/libnative') diff --git a/src/libnative/io/file.rs b/src/libnative/io/file.rs index eaa4403f7bf..c1a378c7e3c 100644 --- a/src/libnative/io/file.rs +++ b/src/libnative/io/file.rs @@ -899,8 +899,7 @@ pub fn utime(p: &CString, atime: u64, mtime: u64) -> IoResult<()> { #[cfg(test)] mod tests { - use std::io::native::file::{CFile, FileDesc}; - use std::io::fs; + use super::{CFile, FileDesc}; use std::io; use std::libc; use std::os; diff --git a/src/libnative/lib.rs b/src/libnative/lib.rs index 4b32511dc42..40d8f6f0b46 100644 --- a/src/libnative/lib.rs +++ b/src/libnative/lib.rs @@ -24,38 +24,64 @@ #[crate_type = "rlib"]; #[crate_type = "dylib"]; +#[cfg(stage0, test)] extern mod green; + // NB this crate explicitly does *not* allow glob imports, please seriously -// consider whether they're needed before adding that feature here. +// consider whether they're needed before adding that feature here (the +// answer is that you don't need them) -use std::cast; use std::os; use std::rt; -use std::task::try; pub mod io; pub mod task; + // XXX: this should not exist here -#[cfg(stage0)] +#[cfg(stage0, notready)] #[lang = "start"] -pub fn start(main: *u8, argc: int, argv: **u8) -> int { - rt::init(argc, argv); +pub fn lang_start(main: *u8, argc: int, argv: **u8) -> int { + use std::cast; + use std::task::try; - // Bootstrap ourselves by installing a local Task and then immediately - // spawning a thread to run 'main'. Always spawn a new thread for main so - // the stack size of 'main' is known (and the bounds can be set - // appropriately). - // - // Once the main task has completed, then we wait for everyone else to exit. - task::run(task::new(), proc() { + do start(argc, argv) { + // Instead of invoking main directly on this thread, invoke it on + // another spawned thread that we are guaranteed to know the size of the + // stack of. Currently, we do not have a method of figuring out the size + // of the main thread's stack, so for stack overflow detection to work + // we must spawn the task in a subtask which we know the stack size of. let main: extern "Rust" fn() = unsafe { cast::transmute(main) }; match do try { main() } { Ok(()) => { os::set_exit_status(0); } Err(..) => { os::set_exit_status(rt::DEFAULT_ERROR_CODE); } } - }); - task::wait_for_completion(); + } +} +/// Executes the given procedure after initializing the runtime with the given +/// argc/argv. +/// +/// This procedure is guaranteed to run on the thread calling this function, but +/// the stack bounds for this rust task will *not* be set. Care must be taken +/// for this function to not overflow its stack. +/// +/// This function will only return once *all* native threads in the system have +/// exited. +pub fn start(argc: int, argv: **u8, main: proc()) -> int { + rt::init(argc, argv); + let exit_code = run(main); unsafe { rt::cleanup(); } + return exit_code; +} + +/// Executes a procedure on the current thread in a Rust task context. +/// +/// This function has all of the same details as `start` except for a different +/// number of arguments. +pub fn run(main: proc()) -> int { + // Create a task, run the procedure in it, and then wait for everything. + task::run(task::new(), main); + task::wait_for_completion(); + os::get_exit_status() } diff --git a/src/libnative/task.rs b/src/libnative/task.rs index 782bef10c92..f0502a43990 100644 --- a/src/libnative/task.rs +++ b/src/libnative/task.rs @@ -260,3 +260,100 @@ impl Drop for Ops { unsafe { self.lock.destroy() } } } + +#[cfg(test)] +mod tests { + use std::rt::Runtime; + use std::rt::local::Local; + use std::rt::task::Task; + use std::task; + use super::{spawn, spawn_opts, Ops}; + + #[test] + fn smoke() { + let (p, c) = Chan::new(); + do spawn { + c.send(()); + } + p.recv(); + } + + #[test] + fn smoke_fail() { + let (p, c) = Chan::<()>::new(); + do spawn { + let _c = c; + fail!() + } + assert_eq!(p.recv_opt(), None); + } + + #[test] + fn smoke_opts() { + let mut opts = task::default_task_opts(); + opts.name = Some(SendStrStatic("test")); + opts.stack_size = Some(20 * 4096); + let (p, c) = Chan::new(); + opts.notify_chan = Some(c); + spawn_opts(opts, proc() {}); + assert!(p.recv().is_ok()); + } + + #[test] + fn smoke_opts_fail() { + let mut opts = task::default_task_opts(); + let (p, c) = Chan::new(); + opts.notify_chan = Some(c); + spawn_opts(opts, proc() { fail!() }); + assert!(p.recv().is_err()); + } + + #[test] + fn yield_test() { + let (p, c) = Chan::new(); + do spawn { + 10.times(task::deschedule); + c.send(()); + } + p.recv(); + } + + #[test] + fn spawn_children() { + let (p, c) = Chan::new(); + do spawn { + let (p, c2) = Chan::new(); + do spawn { + let (p, c3) = Chan::new(); + do spawn { + c3.send(()); + } + p.recv(); + c2.send(()); + } + p.recv(); + c.send(()); + } + p.recv(); + } + + #[test] + fn spawn_inherits() { + let (p, c) = Chan::new(); + do spawn { + let c = c; + do spawn { + let mut task: ~Task = Local::take(); + match task.maybe_take_runtime::() { + Some(ops) => { + task.put_runtime(ops as ~Runtime); + } + None => fail!(), + } + Local::put(task); + c.send(()); + } + } + p.recv(); + } +} -- cgit 1.4.1-3-g733a5 From 3893716390f2c4857b7e8b1705a6344f96b85bb6 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 13 Dec 2013 16:26:02 -0800 Subject: Finalize the green::Pool type The scheduler pool now has a much more simplified interface. There is now a clear distinction between creating the pool and then interacting the pool. When a pool is created, all schedulers are not active, and only later if a spawn is done does activity occur. There are four operations that you can do on a pool: 1. Create a new pool. The only argument to this function is the configuration for the scheduler pool. Currently the only configuration parameter is the number of threads to initially spawn. 2. Spawn a task into this pool. This takes a procedure and task configuration options and spawns a new task into the pool of schedulers. 3. Spawn a new scheduler into the pool. This will return a handle on which to communicate with the scheduler in order to do something like a pinned task. 4. Shut down the scheduler pool. This will consume the scheduler pool, request all of the schedulers to shut down, and then wait on all the scheduler threads. Currently this will block the invoking OS thread, but I plan on making 'Thread::join' not a thread-blocking call. These operations can be used to encode all current usage of M:N schedulers, as well as providing a simple interface through which a pool can be modified. There is currently no way to remove a scheduler from a pool of scheduler, as there's no way to guarantee that a scheduler has exited. This may be added in the future, however (as necessary). --- src/libgreen/lib.rs | 276 +++++++++++++++++++------------------------------- src/libgreen/sched.rs | 5 + src/libgreen/task.rs | 48 +++++---- src/libnative/lib.rs | 5 +- src/libnative/task.rs | 50 +-------- src/libstd/rt/task.rs | 20 +++- src/libstd/task.rs | 31 +++--- 7 files changed, 185 insertions(+), 250 deletions(-) (limited to 'src/libnative') diff --git a/src/libgreen/lib.rs b/src/libgreen/lib.rs index 6530316a627..f4903ea38d2 100644 --- a/src/libgreen/lib.rs +++ b/src/libgreen/lib.rs @@ -39,13 +39,15 @@ use std::rt::task::Task; use std::rt::rtio; use std::sync::deque; use std::sync::atomics::{SeqCst, AtomicUint, INIT_ATOMIC_UINT}; -use std::task::TaskResult; +use std::task::TaskOpts; use std::vec; use std::util; +use stdtask = std::rt::task; -use sched::{Shutdown, Scheduler, SchedHandle}; +use sched::{Shutdown, Scheduler, SchedHandle, TaskFromFriend, NewNeighbor}; use sleeper_list::SleeperList; -use task::{GreenTask, HomeSched}; +use stack::StackPool; +use task::GreenTask; mod macros; @@ -103,37 +105,17 @@ pub fn start(argc: int, argv: **u8, main: proc()) -> int { /// This function will not return until all schedulers in the associated pool /// have returned. pub fn run(main: proc()) -> int { - let config = Config { - shutdown_after_main_exits: true, - ..Config::new() - }; - Pool::spawn(config, main).wait(); + let mut pool = Pool::new(Config::new()); + pool.spawn(TaskOpts::new(), main); + unsafe { stdtask::wait_for_completion(); } + pool.shutdown(); os::get_exit_status() } /// Configuration of how an M:N pool of schedulers is spawned. pub struct Config { - /// If this flag is set, then when schedulers are spawned via the `start` - /// and `run` functions the thread invoking `start` and `run` will have a - /// scheduler spawned on it. This scheduler will be "special" in that the - /// main task will be pinned to the scheduler and it will not participate in - /// work stealing. - /// - /// If the `spawn` function is used to create a pool of schedulers, then - /// this option has no effect. - use_main_thread: bool, - /// The number of schedulers (OS threads) to spawn into this M:N pool. threads: uint, - - /// When the main function exits, this flag will dictate whether a shutdown - /// is requested of all schedulers. If this flag is `true`, this means that - /// schedulers will shut down as soon as possible after the main task exits - /// (but some may stay alive longer for things like I/O or other tasks). - /// - /// If this flag is `false`, then no action is taken when the `main` task - /// exits. The scheduler pool is then shut down via the `wait()` function. - shutdown_after_main_exits: bool, } impl Config { @@ -141,9 +123,7 @@ impl Config { /// variables of this process. pub fn new() -> Config { Config { - use_main_thread: false, threads: rt::default_sched_threads(), - shutdown_after_main_exits: false, } } } @@ -151,8 +131,14 @@ impl Config { /// A structure representing a handle to a pool of schedulers. This handle is /// used to keep the pool alive and also reap the status from the pool. pub struct Pool { + priv id: uint, priv threads: ~[Thread<()>], - priv handles: Option<~[SchedHandle]>, + priv handles: ~[SchedHandle], + priv stealers: ~[deque::Stealer<~task::GreenTask>], + priv next_friend: uint, + priv stack_pool: StackPool, + priv deque_pool: deque::BufferPool<~task::GreenTask>, + priv sleepers: SleeperList, } impl Pool { @@ -160,177 +146,125 @@ impl Pool { /// /// This will configure the pool according to the `config` parameter, and /// initially run `main` inside the pool of schedulers. - pub fn spawn(config: Config, main: proc()) -> Pool { + pub fn new(config: Config) -> Pool { static mut POOL_ID: AtomicUint = INIT_ATOMIC_UINT; - let Config { - threads: nscheds, - use_main_thread: use_main_sched, - shutdown_after_main_exits - } = config; - - let mut main = Some(main); - let pool_id = unsafe { POOL_ID.fetch_add(1, SeqCst) }; + let Config { threads: nscheds } = config; + assert!(nscheds > 0); - // The shared list of sleeping schedulers. - let sleepers = SleeperList::new(); + // The pool of schedulers that will be returned from this function + let mut pool = Pool { + threads: ~[], + handles: ~[], + stealers: ~[], + id: unsafe { POOL_ID.fetch_add(1, SeqCst) }, + sleepers: SleeperList::new(), + stack_pool: StackPool::new(), + deque_pool: deque::BufferPool::new(), + next_friend: 0, + }; // Create a work queue for each scheduler, ntimes. Create an extra // for the main thread if that flag is set. We won't steal from it. - let mut pool = deque::BufferPool::new(); - let arr = vec::from_fn(nscheds, |_| pool.deque()); + let arr = vec::from_fn(nscheds, |_| pool.deque_pool.deque()); let (workers, stealers) = vec::unzip(arr.move_iter()); + pool.stealers = stealers; - // The schedulers. - let mut scheds = ~[]; - // Handles to the schedulers. When the main task ends these will be - // sent the Shutdown message to terminate the schedulers. - let mut handles = ~[]; - + // Now that we've got all our work queues, create one scheduler per + // queue, spawn the scheduler into a thread, and be sure to keep a + // handle to the scheduler and the thread to keep them alive. for worker in workers.move_iter() { rtdebug!("inserting a regular scheduler"); - // Every scheduler is driven by an I/O event loop. - let loop_ = new_event_loop(); - let mut sched = ~Scheduler::new(pool_id, - loop_, + let mut sched = ~Scheduler::new(pool.id, + new_event_loop(), worker, - stealers.clone(), - sleepers.clone()); - let handle = sched.make_handle(); - - scheds.push(sched); - handles.push(handle); - } - - // If we need a main-thread task then create a main thread scheduler - // that will reject any task that isn't pinned to it - let main_sched = if use_main_sched { - - // Create a friend handle. - let mut friend_sched = scheds.pop(); - let friend_handle = friend_sched.make_handle(); - scheds.push(friend_sched); - - // This scheduler needs a queue that isn't part of the stealee - // set. - let (worker, _) = pool.deque(); - - let main_loop = new_event_loop(); - let mut main_sched = ~Scheduler::new_special(pool_id, - main_loop, - worker, - stealers.clone(), - sleepers.clone(), - false, - Some(friend_handle)); - let mut main_handle = main_sched.make_handle(); - // Allow the scheduler to exit when the main task exits. - // Note: sending the shutdown message also prevents the scheduler - // from pushing itself to the sleeper list, which is used for - // waking up schedulers for work stealing; since this is a - // non-work-stealing scheduler it should not be adding itself - // to the list. - main_handle.send(Shutdown); - Some(main_sched) - } else { - None - }; - - // The pool of schedulers that will be returned from this function - let mut pool = Pool { threads: ~[], handles: None }; - - // When the main task exits, after all the tasks in the main - // task tree, shut down the schedulers and set the exit code. - let mut on_exit = if shutdown_after_main_exits { - let handles = handles; - Some(proc(exit_success: TaskResult) { - let mut handles = handles; - for handle in handles.mut_iter() { - handle.send(Shutdown); - } - if exit_success.is_err() { - os::set_exit_status(rt::DEFAULT_ERROR_CODE); - } - }) - } else { - pool.handles = Some(handles); - None - }; - - if !use_main_sched { - - // In the case where we do not use a main_thread scheduler we - // run the main task in one of our threads. - - let mut main = GreenTask::new(&mut scheds[0].stack_pool, None, - main.take_unwrap()); - let mut main_task = ~Task::new(); - main_task.name = Some(SendStrStatic("
")); - main_task.death.on_exit = on_exit.take(); - main.put_task(main_task); - - let sched = scheds.pop(); - let main = main; - let thread = do Thread::start { - sched.bootstrap(main); - }; - pool.threads.push(thread); - } - - // Run each remaining scheduler in a thread. - for sched in scheds.move_rev_iter() { - rtdebug!("creating regular schedulers"); - let thread = do Thread::start { + pool.stealers.clone(), + pool.sleepers.clone()); + pool.handles.push(sched.make_handle()); + let sched = sched; + pool.threads.push(do Thread::start { let mut sched = sched; let mut task = do GreenTask::new(&mut sched.stack_pool, None) { rtdebug!("boostraping a non-primary scheduler"); }; task.put_task(~Task::new()); sched.bootstrap(task); - }; - pool.threads.push(thread); + }); } - // If we do have a main thread scheduler, run it now. + return pool; + } - if use_main_sched { - rtdebug!("about to create the main scheduler task"); + pub fn shutdown(mut self) { + self.stealers = ~[]; - let mut main_sched = main_sched.unwrap(); + for mut handle in util::replace(&mut self.handles, ~[]).move_iter() { + handle.send(Shutdown); + } + for thread in util::replace(&mut self.threads, ~[]).move_iter() { + thread.join(); + } + } - let home = HomeSched(main_sched.make_handle()); - let mut main = GreenTask::new_homed(&mut main_sched.stack_pool, None, - home, main.take_unwrap()); - let mut main_task = ~Task::new(); - main_task.name = Some(SendStrStatic("
")); - main_task.death.on_exit = on_exit.take(); - main.put_task(main_task); - rtdebug!("bootstrapping main_task"); + pub fn spawn(&mut self, opts: TaskOpts, f: proc()) { + let task = GreenTask::configure(&mut self.stack_pool, opts, f); - main_sched.bootstrap(main); + // Figure out someone to send this task to + let idx = self.next_friend; + self.next_friend += 1; + if self.next_friend >= self.handles.len() { + self.next_friend = 0; } - return pool; + // Jettison the task away! + self.handles[idx].send(TaskFromFriend(task)); } - /// Waits for the pool of schedulers to exit. If the pool was spawned to - /// shutdown after the main task exits, this will simply wait for all the - /// scheudlers to exit. If the pool was not spawned like that, this function - /// will trigger shutdown of all the active schedulers. The schedulers will - /// exit once all tasks in this pool of schedulers has exited. - pub fn wait(&mut self) { - match self.handles.take() { - Some(mut handles) => { - for handle in handles.mut_iter() { - handle.send(Shutdown); - } - } - None => {} + /// Spawns a new scheduler into this M:N pool. A handle is returned to the + /// scheduler for use. The scheduler will not exit as long as this handle is + /// active. + /// + /// The scheduler spawned will participate in work stealing with all of the + /// other schedulers currently in the scheduler pool. + pub fn spawn_sched(&mut self) -> SchedHandle { + let (worker, stealer) = self.deque_pool.deque(); + self.stealers.push(stealer.clone()); + + // Tell all existing schedulers about this new scheduler so they can all + // steal work from it + for handle in self.handles.mut_iter() { + handle.send(NewNeighbor(stealer.clone())); } - for thread in util::replace(&mut self.threads, ~[]).move_iter() { - thread.join(); + // Create the new scheduler, using the same sleeper list as all the + // other schedulers as well as having a stealer handle to all other + // schedulers. + let mut sched = ~Scheduler::new(self.id, + new_event_loop(), + worker, + self.stealers.clone(), + self.sleepers.clone()); + let ret = sched.make_handle(); + self.handles.push(sched.make_handle()); + let sched = sched; + self.threads.push(do Thread::start { + let mut sched = sched; + let mut task = do GreenTask::new(&mut sched.stack_pool, None) { + rtdebug!("boostraping a non-primary scheduler"); + }; + task.put_task(~Task::new()); + sched.bootstrap(task); + }); + + return ret; + } +} + +impl Drop for Pool { + fn drop(&mut self) { + if self.threads.len() > 0 { + fail!("dropping a M:N scheduler pool that wasn't shut down"); } } } diff --git a/src/libgreen/sched.rs b/src/libgreen/sched.rs index b0a49f2450a..e349ae1e601 100644 --- a/src/libgreen/sched.rs +++ b/src/libgreen/sched.rs @@ -393,6 +393,10 @@ impl Scheduler { stask.put_with_sched(self); return None; } + Some(NewNeighbor(neighbor)) => { + self.work_queues.push(neighbor); + return Some((self, stask)); + } None => { return Some((self, stask)); } @@ -831,6 +835,7 @@ type SchedulingFn = extern "Rust" fn (~Scheduler, ~GreenTask, ~GreenTask); pub enum SchedMessage { Wake, Shutdown, + NewNeighbor(deque::Stealer<~GreenTask>), PinnedTask(~GreenTask), TaskFromFriend(~GreenTask), RunOnce(~GreenTask), diff --git a/src/libgreen/task.rs b/src/libgreen/task.rs index 72e72f2cd99..e07d7f2413f 100644 --- a/src/libgreen/task.rs +++ b/src/libgreen/task.rs @@ -55,12 +55,15 @@ pub enum Home { } impl GreenTask { + /// Creates a new green task which is not homed to any particular scheduler + /// and will not have any contained Task structure. pub fn new(stack_pool: &mut StackPool, stack_size: Option, start: proc()) -> ~GreenTask { GreenTask::new_homed(stack_pool, stack_size, AnySched, start) } + /// Creates a new task (like `new`), but specifies the home for new task. pub fn new_homed(stack_pool: &mut StackPool, stack_size: Option, home: Home, @@ -71,6 +74,8 @@ impl GreenTask { return ops; } + /// Creates a new green task with the specified coroutine and type, this is + /// useful when creating scheduler tasks. pub fn new_typed(coroutine: Option, task_type: TaskType) -> ~GreenTask { ~GreenTask { @@ -84,6 +89,31 @@ impl GreenTask { } } + /// Creates a new green task with the given configuration options for the + /// contained Task object. The given stack pool is also used to allocate a + /// new stack for this task. + pub fn configure(pool: &mut StackPool, + opts: TaskOpts, + f: proc()) -> ~GreenTask { + let TaskOpts { + watched: _watched, + notify_chan, name, stack_size + } = opts; + + let mut green = GreenTask::new(pool, stack_size, f); + let mut task = ~Task::new(); + task.name = name; + match notify_chan { + Some(chan) => { + let on_exit = proc(task_result) { chan.send(task_result) }; + task.death.on_exit = Some(on_exit); + } + None => {} + } + green.put_task(task); + return green; + } + /// Just like the `maybe_take_runtime` function, this function should *not* /// exist. Usage of this function is _strongly_ discouraged. This is an /// absolute last resort necessary for converting a libstd task to a green @@ -367,11 +397,6 @@ impl Runtime for GreenTask { fn spawn_sibling(mut ~self, cur_task: ~Task, opts: TaskOpts, f: proc()) { self.put_task(cur_task); - let TaskOpts { - watched: _watched, - notify_chan, name, stack_size - } = opts; - // Spawns a task into the current scheduler. We allocate the new task's // stack from the scheduler's stack pool, and then configure it // accordingly to `opts`. Afterwards we bootstrap it immediately by @@ -379,18 +404,7 @@ impl Runtime for GreenTask { // // Upon returning, our task is back in TLS and we're good to return. let mut sched = self.sched.take_unwrap(); - let mut sibling = GreenTask::new(&mut sched.stack_pool, stack_size, f); - let mut sibling_task = ~Task::new(); - sibling_task.name = name; - match notify_chan { - Some(chan) => { - let on_exit = proc(task_result) { chan.send(task_result) }; - sibling_task.death.on_exit = Some(on_exit); - } - None => {} - } - - sibling.task = Some(sibling_task); + let sibling = GreenTask::configure(&mut sched.stack_pool, opts, f); sched.run_task(self, sibling) } diff --git a/src/libnative/lib.rs b/src/libnative/lib.rs index 40d8f6f0b46..b97d9127277 100644 --- a/src/libnative/lib.rs +++ b/src/libnative/lib.rs @@ -32,6 +32,7 @@ use std::os; use std::rt; +use stdtask = std::rt::task; pub mod io; pub mod task; @@ -81,7 +82,9 @@ pub fn start(argc: int, argv: **u8, main: proc()) -> int { pub fn run(main: proc()) -> int { // Create a task, run the procedure in it, and then wait for everything. task::run(task::new(), main); - task::wait_for_completion(); + + // Block this OS task waiting for everything to finish. + unsafe { stdtask::wait_for_completion() } os::get_exit_status() } diff --git a/src/libnative/task.rs b/src/libnative/task.rs index f0502a43990..1aa32bc8a26 100644 --- a/src/libnative/task.rs +++ b/src/libnative/task.rs @@ -21,49 +21,13 @@ use std::rt::rtio; use std::rt::task::{Task, BlockedTask}; use std::rt::thread::Thread; use std::rt; -use std::sync::atomics::{AtomicUint, SeqCst, INIT_ATOMIC_UINT}; -use std::task::{TaskOpts, default_task_opts}; -use std::unstable::mutex::{Mutex, MUTEX_INIT}; +use std::task::TaskOpts; +use std::unstable::mutex::Mutex; use std::unstable::stack; use io; use task; -static mut THREAD_CNT: AtomicUint = INIT_ATOMIC_UINT; -static mut LOCK: Mutex = MUTEX_INIT; - -/// Waits for all spawned threads to finish completion. This should only be used -/// by the main task in order to wait for all other tasks to terminate. -/// -/// This mirrors the same semantics as the green scheduling model. -pub fn wait_for_completion() { - static mut someone_waited: bool = false; - - unsafe { - LOCK.lock(); - assert!(!someone_waited); - someone_waited = true; - while THREAD_CNT.load(SeqCst) > 0 { - LOCK.wait(); - } - LOCK.unlock(); - LOCK.destroy(); - } - -} - -// Signal that a thread has finished execution, possibly waking up a blocker -// waiting for all threads to have finished. -fn signal_done() { - unsafe { - LOCK.lock(); - if THREAD_CNT.fetch_sub(1, SeqCst) == 1 { - LOCK.signal(); - } - LOCK.unlock(); - } -} - /// Creates a new Task which is ready to execute as a 1:1 task. pub fn new() -> ~Task { let mut task = ~Task::new(); @@ -75,15 +39,12 @@ pub fn new() -> ~Task { /// Spawns a function with the default configuration pub fn spawn(f: proc()) { - spawn_opts(default_task_opts(), f) + spawn_opts(TaskOpts::new(), f) } /// Spawns a new task given the configuration options and a procedure to run /// inside the task. pub fn spawn_opts(opts: TaskOpts, f: proc()) { - // must happen before the spawn, no need to synchronize with a lock. - unsafe { THREAD_CNT.fetch_add(1, SeqCst); } - let TaskOpts { watched: _watched, notify_chan, name, stack_size @@ -117,7 +78,6 @@ pub fn spawn_opts(opts: TaskOpts, f: proc()) { } run(task, f); - signal_done(); }) } @@ -290,7 +250,7 @@ mod tests { #[test] fn smoke_opts() { - let mut opts = task::default_task_opts(); + let mut opts = TaskOpts::new(); opts.name = Some(SendStrStatic("test")); opts.stack_size = Some(20 * 4096); let (p, c) = Chan::new(); @@ -301,7 +261,7 @@ mod tests { #[test] fn smoke_opts_fail() { - let mut opts = task::default_task_opts(); + let mut opts = TaskOpts::new(); let (p, c) = Chan::new(); opts.notify_chan = Some(c); spawn_opts(opts, proc() { fail!() }); diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs index c0164891cd4..91e285b1061 100644 --- a/src/libstd/rt/task.rs +++ b/src/libstd/rt/task.rs @@ -33,12 +33,16 @@ use rt::rtio::LocalIo; use rt::unwind::Unwinder; use send_str::SendStr; use sync::arc::UnsafeArc; -use sync::atomics::{AtomicUint, SeqCst}; +use sync::atomics::{AtomicUint, SeqCst, INIT_ATOMIC_UINT}; use task::{TaskResult, TaskOpts}; use unstable::finally::Finally; +use unstable::mutex::{Mutex, MUTEX_INIT}; #[cfg(stage0)] pub use rt::unwind::begin_unwind; +static mut TASK_COUNT: AtomicUint = INIT_ATOMIC_UINT; +static mut TASK_LOCK: Mutex = MUTEX_INIT; + // The Task struct represents all state associated with a rust // task. There are at this point two primary "subtypes" of task, // however instead of using a subtype we just have a "task_type" field @@ -117,6 +121,7 @@ impl Task { *cast::transmute::<&~Task, &*mut Task>(&self) }; Local::put(self); + unsafe { TASK_COUNT.fetch_add(1, SeqCst); } // The only try/catch block in the world. Attempt to run the task's // client-specified code and catch any failures. @@ -180,6 +185,11 @@ impl Task { unsafe { let me: *mut Task = Local::unsafe_borrow(); (*me).death.collect_failure((*me).unwinder.result()); + if TASK_COUNT.fetch_sub(1, SeqCst) == 1 { + TASK_LOCK.lock(); + TASK_LOCK.signal(); + TASK_LOCK.unlock(); + } } let mut me: ~Task = Local::take(); me.destroyed = true; @@ -376,6 +386,14 @@ impl Drop for Death { } } +pub unsafe fn wait_for_completion() { + TASK_LOCK.lock(); + while TASK_COUNT.load(SeqCst) > 0 { + TASK_LOCK.wait(); + } + TASK_LOCK.unlock(); +} + #[cfg(test)] mod test { use super::*; diff --git a/src/libstd/task.rs b/src/libstd/task.rs index 3b9cde5f44d..836390fb416 100644 --- a/src/libstd/task.rs +++ b/src/libstd/task.rs @@ -131,7 +131,7 @@ pub struct TaskBuilder { */ pub fn task() -> TaskBuilder { TaskBuilder { - opts: default_task_opts(), + opts: TaskOpts::new(), gen_body: None, can_not_copy: None, } @@ -301,22 +301,23 @@ impl TaskBuilder { } } - /* Task construction */ -pub fn default_task_opts() -> TaskOpts { - /*! - * The default task options - * - * By default all tasks are supervised by their parent, are spawned - * into the same scheduler, and do not post lifecycle notifications. - */ - - TaskOpts { - watched: true, - notify_chan: None, - name: None, - stack_size: None +impl TaskOpts { + pub fn new() -> TaskOpts { + /*! + * The default task options + * + * By default all tasks are supervised by their parent, are spawned + * into the same scheduler, and do not post lifecycle notifications. + */ + + TaskOpts { + watched: true, + notify_chan: None, + name: None, + stack_size: None + } } } -- cgit 1.4.1-3-g733a5 From aad9fbf6b65137f278d74cc84e0028a8f8aeed03 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 13 Dec 2013 19:33:28 -0800 Subject: green: Fixing all tests from previous refactorings --- src/libgreen/basic.rs | 58 +++++++ src/libgreen/lib.rs | 2 - src/libgreen/sched.rs | 405 +++++++++++++++++++++++++------------------------ src/libgreen/task.rs | 161 +++++++++++--------- src/libnative/task.rs | 1 + src/librustuv/timer.rs | 6 +- 6 files changed, 351 insertions(+), 282 deletions(-) (limited to 'src/libnative') diff --git a/src/libgreen/basic.rs b/src/libgreen/basic.rs index 6140da08b68..e1e489a2a2b 100644 --- a/src/libgreen/basic.rs +++ b/src/libgreen/basic.rs @@ -223,3 +223,61 @@ impl Drop for BasicPausable { } } } + +#[cfg(test)] +mod test { + use std::task::TaskOpts; + + use basic; + use PoolConfig; + use SchedPool; + + fn pool() -> SchedPool { + SchedPool::new(PoolConfig { + threads: 1, + event_loop_factory: Some(basic::event_loop), + }) + } + + fn run(f: proc()) { + let mut pool = pool(); + pool.spawn(TaskOpts::new(), f); + pool.shutdown(); + } + + #[test] + fn smoke() { + do run {} + } + + #[test] + fn some_channels() { + do run { + let (p, c) = Chan::new(); + do spawn { + c.send(()); + } + p.recv(); + } + } + + #[test] + fn multi_thread() { + let mut pool = SchedPool::new(PoolConfig { + threads: 2, + event_loop_factory: Some(basic::event_loop), + }); + + for _ in range(0, 20) { + do pool.spawn(TaskOpts::new()) { + let (p, c) = Chan::new(); + do spawn { + c.send(()); + } + p.recv(); + } + } + + pool.shutdown(); + } +} diff --git a/src/libgreen/lib.rs b/src/libgreen/lib.rs index 82d5bc83e2e..bb219936ae0 100644 --- a/src/libgreen/lib.rs +++ b/src/libgreen/lib.rs @@ -58,8 +58,6 @@ pub mod sleeper_list; pub mod stack; pub mod task; -#[cfg(test)] mod tests; - #[cfg(stage0)] #[lang = "start"] pub fn lang_start(main: *u8, argc: int, argv: **u8) -> int { diff --git a/src/libgreen/sched.rs b/src/libgreen/sched.rs index 70fd6768c8b..9e26cd41cdd 100644 --- a/src/libgreen/sched.rs +++ b/src/libgreen/sched.rs @@ -951,21 +951,47 @@ fn new_sched_rng() -> XorShiftRng { #[cfg(test)] mod test { - use borrow::to_uint; - use rt::deque::BufferPool; - use rt::basic; - use rt::sched::{Scheduler}; - use rt::task::{GreenTask, Sched}; - use rt::thread::Thread; - use rt::util; - use task::TaskResult; - use unstable::run_in_bare_thread; + use std::task::TaskOpts; + use std::rt::Runtime; + use std::rt::task::Task; + use std::rt::local::Local; + + use basic; + use sched::TaskFromFriend; + use task::{GreenTask, HomeSched}; + use PoolConfig; + use SchedPool; + + fn pool() -> SchedPool { + SchedPool::new(PoolConfig { + threads: 1, + event_loop_factory: Some(basic::event_loop), + }) + } + + fn run(f: proc()) { + let mut pool = pool(); + pool.spawn(TaskOpts::new(), f); + pool.shutdown(); + } + + fn sched_id() -> uint { + let mut task = Local::borrow(None::); + match task.get().maybe_take_runtime::() { + Some(green) => { + let ret = green.sched.get_ref().sched_id(); + task.get().put_runtime(green as ~Runtime); + return ret; + } + None => fail!() + } + } #[test] fn trivial_run_in_newsched_task_test() { let mut task_ran = false; let task_ran_ptr: *mut bool = &mut task_ran; - do run_in_newsched_task || { + do run { unsafe { *task_ran_ptr = true }; rtdebug!("executed from the new scheduler") } @@ -977,9 +1003,11 @@ mod test { let total = 10; let mut task_run_count = 0; let task_run_count_ptr: *mut uint = &mut task_run_count; - do run_in_newsched_task || { + // with only one thread this is safe to run in without worries of + // contention. + do run { for _ in range(0u, total) { - do spawntask || { + do spawn || { unsafe { *task_run_count_ptr = *task_run_count_ptr + 1}; } } @@ -991,12 +1019,12 @@ mod test { fn multiple_task_nested_test() { let mut task_run_count = 0; let task_run_count_ptr: *mut uint = &mut task_run_count; - do run_in_newsched_task || { - do spawntask || { + do run { + do spawn { unsafe { *task_run_count_ptr = *task_run_count_ptr + 1 }; - do spawntask || { + do spawn { unsafe { *task_run_count_ptr = *task_run_count_ptr + 1 }; - do spawntask || { + do spawn { unsafe { *task_run_count_ptr = *task_run_count_ptr + 1 }; } } @@ -1005,52 +1033,33 @@ mod test { assert!(task_run_count == 3); } - // Confirm that a sched_id actually is the uint form of the - // pointer to the scheduler struct. - #[test] - fn simple_sched_id_test() { - do run_in_bare_thread { - let sched = ~new_test_uv_sched(); - assert!(to_uint(sched) == sched.sched_id()); - } - } - - // Compare two scheduler ids that are different, this should never - // fail but may catch a mistake someday. - #[test] - fn compare_sched_id_test() { - do run_in_bare_thread { - let sched_one = ~new_test_uv_sched(); - let sched_two = ~new_test_uv_sched(); - assert!(sched_one.sched_id() != sched_two.sched_id()); - } - } - - // A very simple test that confirms that a task executing on the // home scheduler notices that it is home. #[test] fn test_home_sched() { - do run_in_bare_thread { - let mut task_ran = false; - let task_ran_ptr: *mut bool = &mut task_ran; + let mut pool = pool(); - let mut sched = ~new_test_uv_sched(); - let sched_handle = sched.make_handle(); + let (dport, dchan) = Chan::new(); + { + let (port, chan) = Chan::new(); + let mut handle1 = pool.spawn_sched(); + let mut handle2 = pool.spawn_sched(); - let mut task = ~do GreenTask::new_root_homed(&mut sched.stack_pool, None, - Sched(sched_handle)) { - unsafe { *task_ran_ptr = true }; - assert!(GreenTask::on_appropriate_sched()); - }; + handle1.send(TaskFromFriend(do pool.task(TaskOpts::new()) { + chan.send(sched_id()); + })); + let sched1_id = port.recv(); - let on_exit: proc(TaskResult) = proc(exit_status) { - rtassert!(exit_status.is_ok()) + let mut task = do pool.task(TaskOpts::new()) { + assert_eq!(sched_id(), sched1_id); + dchan.send(()); }; - task.death.on_exit = Some(on_exit); - - sched.bootstrap(task); + task.give_home(HomeSched(handle1)); + handle2.send(TaskFromFriend(task)); } + dport.recv(); + + pool.shutdown(); } // An advanced test that checks all four possible states that a @@ -1058,12 +1067,13 @@ mod test { #[test] fn test_schedule_home_states() { - use rt::sleeper_list::SleeperList; - use rt::sched::Shutdown; - use borrow; + use sleeper_list::SleeperList; + use super::{Shutdown, Scheduler, SchedHandle}; + use std::unstable::run_in_bare_thread; + use std::rt::thread::Thread; + use std::sync::deque::BufferPool; do run_in_bare_thread { - let sleepers = SleeperList::new(); let mut pool = BufferPool::new(); let (normal_worker, normal_stealer) = pool.deque(); @@ -1072,15 +1082,18 @@ mod test { // Our normal scheduler let mut normal_sched = ~Scheduler::new( + 1, basic::event_loop(), normal_worker, queues.clone(), sleepers.clone()); let normal_handle = normal_sched.make_handle(); + let friend_handle = normal_sched.make_handle(); // Our special scheduler let mut special_sched = ~Scheduler::new_special( + 1, basic::event_loop(), special_worker, queues.clone(), @@ -1099,35 +1112,61 @@ mod test { // 3) task not homed, sched requeues // 4) task not home, send home - let task1 = ~do GreenTask::new_root_homed(&mut special_sched.stack_pool, None, - Sched(t1_handle)) || { - rtassert!(GreenTask::on_appropriate_sched()); + // Grab both the scheduler and the task from TLS and check if the + // task is executing on an appropriate scheduler. + fn on_appropriate_sched() -> bool { + use task::{TypeGreen, TypeSched, HomeSched}; + let task = GreenTask::convert(Local::take()); + let sched_id = task.sched.get_ref().sched_id(); + let run_any = task.sched.get_ref().run_anything; + let ret = match task.task_type { + TypeGreen(Some(AnySched)) => { + run_any + } + TypeGreen(Some(HomeSched(SchedHandle { + sched_id: ref id, + .. + }))) => { + *id == sched_id + } + TypeGreen(None) => { fail!("task without home"); } + TypeSched => { fail!("expected green task"); } + }; + task.put(); + ret + } + + let task1 = do GreenTask::new_homed(&mut special_sched.stack_pool, + None, HomeSched(t1_handle)) { + rtassert!(on_appropriate_sched()); }; - rtdebug!("task1 id: **{}**", borrow::to_uint(task1)); - let task2 = ~do GreenTask::new_root(&mut normal_sched.stack_pool, None) { - rtassert!(GreenTask::on_appropriate_sched()); + let task2 = do GreenTask::new(&mut normal_sched.stack_pool, None) { + rtassert!(on_appropriate_sched()); }; - let task3 = ~do GreenTask::new_root(&mut normal_sched.stack_pool, None) { - rtassert!(GreenTask::on_appropriate_sched()); + let task3 = do GreenTask::new(&mut normal_sched.stack_pool, None) { + rtassert!(on_appropriate_sched()); }; - let task4 = ~do GreenTask::new_root_homed(&mut special_sched.stack_pool, None, - Sched(t4_handle)) { - rtassert!(GreenTask::on_appropriate_sched()); + let task4 = do GreenTask::new_homed(&mut special_sched.stack_pool, + None, HomeSched(t4_handle)) { + rtassert!(on_appropriate_sched()); }; - rtdebug!("task4 id: **{}**", borrow::to_uint(task4)); // Signal from the special task that we are done. let (port, chan) = Chan::<()>::new(); - let normal_task = ~do GreenTask::new_root(&mut normal_sched.stack_pool, None) { - rtdebug!("*about to submit task2*"); - Scheduler::run_task(task2); - rtdebug!("*about to submit task4*"); - Scheduler::run_task(task4); - rtdebug!("*normal_task done*"); + fn run(next: ~GreenTask) { + let mut task = GreenTask::convert(Local::take()); + let sched = task.sched.take_unwrap(); + sched.run_task(task, next) + } + + let normal_task = do GreenTask::new(&mut normal_sched.stack_pool, + None) { + run(task2); + run(task4); port.recv(); let mut nh = normal_handle; nh.send(Shutdown); @@ -1135,29 +1174,23 @@ mod test { sh.send(Shutdown); }; - rtdebug!("normal task: {}", borrow::to_uint(normal_task)); - let special_task = ~do GreenTask::new_root(&mut special_sched.stack_pool, None) { - rtdebug!("*about to submit task1*"); - Scheduler::run_task(task1); - rtdebug!("*about to submit task3*"); - Scheduler::run_task(task3); - rtdebug!("*done with special_task*"); + let special_task = do GreenTask::new(&mut special_sched.stack_pool, + None) { + run(task1); + run(task3); chan.send(()); }; - rtdebug!("special task: {}", borrow::to_uint(special_task)); let normal_sched = normal_sched; let normal_thread = do Thread::start { normal_sched.bootstrap(normal_task); - rtdebug!("finished with normal_thread"); }; let special_sched = special_sched; let special_thread = do Thread::start { special_sched.bootstrap(special_task); - rtdebug!("finished with special_sched"); }; normal_thread.join(); @@ -1165,109 +1198,82 @@ mod test { } } - #[test] - fn test_stress_schedule_task_states() { - if util::limit_thread_creation_due_to_osx_and_valgrind() { return; } - let n = stress_factor() * 120; - for _ in range(0, n as int) { - test_schedule_home_states(); - } - } + //#[test] + //fn test_stress_schedule_task_states() { + // if util::limit_thread_creation_due_to_osx_and_valgrind() { return; } + // let n = stress_factor() * 120; + // for _ in range(0, n as int) { + // test_schedule_home_states(); + // } + //} #[test] fn test_io_callback() { - use io::timer; - - // This is a regression test that when there are no schedulable tasks - // in the work queue, but we are performing I/O, that once we do put - // something in the work queue again the scheduler picks it up and doesn't - // exit before emptying the work queue - do run_in_uv_task { - do spawntask { + use std::io::timer; + + let mut pool = SchedPool::new(PoolConfig { + threads: 2, + event_loop_factory: None, + }); + + // This is a regression test that when there are no schedulable tasks in + // the work queue, but we are performing I/O, that once we do put + // something in the work queue again the scheduler picks it up and + // doesn't exit before emptying the work queue + do pool.spawn(TaskOpts::new()) { + do spawn { timer::sleep(10); } } + + pool.shutdown(); } #[test] - fn handle() { - do run_in_bare_thread { - let (port, chan) = Chan::new(); - - let thread_one = do Thread::start { - let chan = chan; - do run_in_newsched_task_core { - chan.send(()); - } - }; - - let thread_two = do Thread::start { - let port = port; - do run_in_newsched_task_core { - port.recv(); - } - }; + fn wakeup_across_scheds() { + let (port1, chan1) = Chan::new(); + let (port2, chan2) = Chan::new(); + + let mut pool1 = pool(); + let mut pool2 = pool(); + + do pool1.spawn(TaskOpts::new()) { + let id = sched_id(); + chan1.send(()); + port2.recv(); + assert_eq!(id, sched_id()); + } - thread_two.join(); - thread_one.join(); + do pool2.spawn(TaskOpts::new()) { + let id = sched_id(); + port1.recv(); + assert_eq!(id, sched_id()); + chan2.send(()); } + + pool1.shutdown(); + pool2.shutdown(); } // A regression test that the final message is always handled. // Used to deadlock because Shutdown was never recvd. #[test] fn no_missed_messages() { - use rt::sleeper_list::SleeperList; - use rt::stack::StackPool; - use rt::sched::{Shutdown, TaskFromFriend}; + let mut pool = pool(); - do run_in_bare_thread { - stress_factor().times(|| { - let sleepers = SleeperList::new(); - let mut pool = BufferPool::new(); - let (worker, stealer) = pool.deque(); - - let mut sched = ~Scheduler::new( - basic::event_loop(), - worker, - ~[stealer], - sleepers.clone()); - - let mut handle = sched.make_handle(); - - let sched = sched; - let thread = do Thread::start { - let mut sched = sched; - let bootstrap_task = - ~GreenTask::new_root(&mut sched.stack_pool, - None, - proc()()); - sched.bootstrap(bootstrap_task); - }; + let task = pool.task(TaskOpts::new(), proc()()); + pool.spawn_sched().send(TaskFromFriend(task)); - let mut stack_pool = StackPool::new(); - let task = ~GreenTask::new_root(&mut stack_pool, None, proc()()); - handle.send(TaskFromFriend(task)); - - handle.send(Shutdown); - drop(handle); - - thread.join(); - }) - } + pool.shutdown(); } #[test] fn multithreading() { - use num::Times; - use vec::OwnedVector; - use container::Container; - - do run_in_mt_newsched_task { + do run { let mut ports = ~[]; 10.times(|| { let (port, chan) = Chan::new(); - do spawntask_later { + do spawn { chan.send(()); } ports.push(port); @@ -1281,7 +1287,7 @@ mod test { #[test] fn thread_ring() { - do run_in_mt_newsched_task { + do run { let (end_port, end_chan) = Chan::new(); let n_tasks = 10; @@ -1294,14 +1300,14 @@ mod test { let (next_p, ch) = Chan::new(); let imm_i = i; let imm_p = p; - do spawntask_random { + do spawn { roundtrip(imm_i, n_tasks, &imm_p, &ch); }; p = next_p; i += 1; } let p = p; - do spawntask_random { + do spawn { roundtrip(1, n_tasks, &p, &ch1); } @@ -1332,11 +1338,9 @@ mod test { #[test] fn start_closure_dtor() { - use ops::Drop; - // Regression test that the `start` task entrypoint can // contain dtors that use task resources - do run_in_newsched_task { + do run { struct S { field: () } impl Drop for S { @@ -1347,7 +1351,7 @@ mod test { let s = S { field: () }; - do spawntask { + do spawn { let _ss = &s; } } @@ -1357,52 +1361,49 @@ mod test { #[ignore] #[test] fn dont_starve_1() { - stress_factor().times(|| { - do run_in_mt_newsched_task { - let (port, chan) = Chan::new(); - - // This task should not be able to starve the sender; - // The sender should get stolen to another thread. - do spawntask { - while port.try_recv().is_none() { } - } + let mut pool = SchedPool::new(PoolConfig { + threads: 2, // this must be > 1 + event_loop_factory: Some(basic::event_loop), + }); + do pool.spawn(TaskOpts::new()) { + let (port, chan) = Chan::new(); - chan.send(()); + // This task should not be able to starve the sender; + // The sender should get stolen to another thread. + do spawn { + while port.try_recv().is_none() { } } - }) + + chan.send(()); + } + pool.shutdown(); } #[test] fn dont_starve_2() { - stress_factor().times(|| { - do run_in_newsched_task { - let (port, chan) = Chan::new(); - let (_port2, chan2) = Chan::new(); + do run { + let (port, chan) = Chan::new(); + let (_port2, chan2) = Chan::new(); - // This task should not be able to starve the other task. - // The sends should eventually yield. - do spawntask { - while port.try_recv().is_none() { - chan2.send(()); - } + // This task should not be able to starve the other task. + // The sends should eventually yield. + do spawn { + while port.try_recv().is_none() { + chan2.send(()); } - - chan.send(()); } - }) + + chan.send(()); + } } - // Regression test for a logic bug that would cause single-threaded schedulers - // to sleep forever after yielding and stealing another task. + // Regression test for a logic bug that would cause single-threaded + // schedulers to sleep forever after yielding and stealing another task. #[test] fn single_threaded_yield() { - use task::{spawn, spawn_sched, SingleThreaded, deschedule}; - use num::Times; - - do spawn_sched(SingleThreaded) { - 5.times(|| { deschedule(); }) + use std::task::deschedule; + do run { + 5.times(deschedule); } - do spawn { } - do spawn { } } } diff --git a/src/libgreen/task.rs b/src/libgreen/task.rs index d37ab8bba57..9da9af9f50b 100644 --- a/src/libgreen/task.rs +++ b/src/libgreen/task.rs @@ -346,7 +346,7 @@ impl Runtime for GreenTask { } } - fn reawaken(mut ~self, to_wake: ~Task, can_resched: bool) { + fn reawaken(mut ~self, to_wake: ~Task) { self.put_task(to_wake); assert!(self.sched.is_none()); @@ -371,21 +371,16 @@ impl Runtime for GreenTask { let mut running_task: ~Task = Local::take(); match running_task.maybe_take_runtime::() { Some(mut running_green_task) => { - let mut sched = running_green_task.sched.take_unwrap(); + running_green_task.put_task(running_task); + let sched = running_green_task.sched.take_unwrap(); + if sched.pool_id == self.pool_id { - running_green_task.put_task(running_task); - if can_resched { - sched.run_task(running_green_task, self); - } else { - sched.enqueue_task(self); - running_green_task.put_with_sched(sched); - } + sched.run_task(running_green_task, self); } else { self.reawaken_remotely(); // put that thing back where it came from! - running_task.put_runtime(running_green_task as ~Runtime); - Local::put(running_task); + running_green_task.put_with_sched(sched); } } None => { @@ -427,94 +422,110 @@ impl Drop for GreenTask { } #[cfg(test)] -mod test { +mod tests { + use std::rt::Runtime; + use std::rt::local::Local; + use std::rt::task::Task; + use std::task; + use std::task::TaskOpts; - #[test] - fn local_heap() { - do run_in_newsched_task() { - let a = @5; - let b = a; - assert!(*a == 5); - assert!(*b == 5); - } + use super::super::{PoolConfig, SchedPool}; + use super::GreenTask; + + fn spawn_opts(opts: TaskOpts, f: proc()) { + let mut pool = SchedPool::new(PoolConfig { + threads: 1, + event_loop_factory: None, + }); + pool.spawn(opts, f); + pool.shutdown(); } #[test] - fn tls() { - use std::local_data; - do run_in_newsched_task() { - local_data_key!(key: @~str) - local_data::set(key, @~"data"); - assert!(*local_data::get(key, |k| k.map(|k| *k)).unwrap() == ~"data"); - local_data_key!(key2: @~str) - local_data::set(key2, @~"data"); - assert!(*local_data::get(key2, |k| k.map(|k| *k)).unwrap() == ~"data"); + fn smoke() { + let (p, c) = Chan::new(); + do spawn_opts(TaskOpts::new()) { + c.send(()); } + p.recv(); } #[test] - fn unwind() { - do run_in_newsched_task() { - let result = spawntask_try(proc()()); - rtdebug!("trying first assert"); - assert!(result.is_ok()); - let result = spawntask_try(proc() fail!()); - rtdebug!("trying second assert"); - assert!(result.is_err()); + fn smoke_fail() { + let (p, c) = Chan::<()>::new(); + do spawn_opts(TaskOpts::new()) { + let _c = c; + fail!() } + assert_eq!(p.recv_opt(), None); } #[test] - fn rng() { - do run_in_uv_task() { - use std::rand::{rng, Rng}; - let mut r = rng(); - let _ = r.next_u32(); - } + fn smoke_opts() { + let mut opts = TaskOpts::new(); + opts.name = Some(SendStrStatic("test")); + opts.stack_size = Some(20 * 4096); + let (p, c) = Chan::new(); + opts.notify_chan = Some(c); + spawn_opts(opts, proc() {}); + assert!(p.recv().is_ok()); } #[test] - fn logging() { - do run_in_uv_task() { - info!("here i am. logging in a newsched task"); - } + fn smoke_opts_fail() { + let mut opts = TaskOpts::new(); + let (p, c) = Chan::new(); + opts.notify_chan = Some(c); + spawn_opts(opts, proc() { fail!() }); + assert!(p.recv().is_err()); } #[test] - fn comm_stream() { - do run_in_newsched_task() { - let (port, chan) = Chan::new(); - chan.send(10); - assert!(port.recv() == 10); + fn yield_test() { + let (p, c) = Chan::new(); + do spawn_opts(TaskOpts::new()) { + 10.times(task::deschedule); + c.send(()); } + p.recv(); } #[test] - fn comm_shared_chan() { - do run_in_newsched_task() { - let (port, chan) = SharedChan::new(); - chan.send(10); - assert!(port.recv() == 10); + fn spawn_children() { + let (p, c) = Chan::new(); + do spawn_opts(TaskOpts::new()) { + let (p, c2) = Chan::new(); + do spawn { + let (p, c3) = Chan::new(); + do spawn { + c3.send(()); + } + p.recv(); + c2.send(()); + } + p.recv(); + c.send(()); } + p.recv(); } - //#[test] - //fn heap_cycles() { - // use std::option::{Option, Some, None}; - - // do run_in_newsched_task { - // struct List { - // next: Option<@mut List>, - // } - - // let a = @mut List { next: None }; - // let b = @mut List { next: Some(a) }; - - // a.next = Some(b); - // } - //} - #[test] - #[should_fail] - fn test_begin_unwind() { begin_unwind("cause", file!(), line!()) } + fn spawn_inherits() { + let (p, c) = Chan::new(); + do spawn_opts(TaskOpts::new()) { + let c = c; + do spawn { + let mut task: ~Task = Local::take(); + match task.maybe_take_runtime::() { + Some(ops) => { + task.put_runtime(ops as ~Runtime); + } + None => fail!(), + } + Local::put(task); + c.send(()); + } + } + p.recv(); + } } diff --git a/src/libnative/task.rs b/src/libnative/task.rs index 1aa32bc8a26..48768def067 100644 --- a/src/libnative/task.rs +++ b/src/libnative/task.rs @@ -227,6 +227,7 @@ mod tests { use std::rt::local::Local; use std::rt::task::Task; use std::task; + use std::task::TaskOpts; use super::{spawn, spawn_opts, Ops}; #[test] diff --git a/src/librustuv/timer.rs b/src/librustuv/timer.rs index d3a190df8be..9c4473ead36 100644 --- a/src/librustuv/timer.rs +++ b/src/librustuv/timer.rs @@ -207,9 +207,9 @@ mod test { let port = timer.period(1); port.recv(); port.recv(); - let port = timer.period(1); - port.recv(); - port.recv(); + let port2 = timer.period(1); + port2.recv(); + port2.recv(); } #[test] -- cgit 1.4.1-3-g733a5 From 282f3d99a5ad85acbc58c03b5dfcdabf649c0c85 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 13 Dec 2013 21:14:08 -0800 Subject: Test fixes and rebase problems Note that this removes a number of run-pass tests which are exercising behavior of the old runtime. This functionality no longer exists and is thoroughly tested inside of libgreen and libnative. There isn't really the notion of "starting the runtime" any more. The major notion now is "bootstrapping the initial task". --- Makefile.in | 6 +++ mk/target.mk | 7 +-- src/compiletest/compiletest.rs | 4 +- src/etc/licenseck.py | 6 +-- src/libgreen/lib.rs | 22 ++++++--- src/libnative/lib.rs | 2 + src/librustuv/homing.rs | 2 + src/librustuv/queue.rs | 2 + src/libstd/io/test.rs | 79 +++++++++++++++++++++++++++++++ src/libstd/rt/task.rs | 18 ++++++- src/libstd/sync/arc.rs | 2 +- src/libsyntax/ext/build.rs | 1 - src/test/run-pass/core-rt-smoke.rs | 20 -------- src/test/run-pass/native-print-no-uv.rs | 17 ------- src/test/run-pass/rt-run-twice.rs | 26 ---------- src/test/run-pass/rt-start-main-thread.rs | 21 -------- src/test/run-pass/spawning-with-debug.rs | 1 - src/test/run-pass/use.rs | 5 +- 18 files changed, 134 insertions(+), 107 deletions(-) delete mode 100644 src/test/run-pass/core-rt-smoke.rs delete mode 100644 src/test/run-pass/native-print-no-uv.rs delete mode 100644 src/test/run-pass/rt-run-twice.rs delete mode 100644 src/test/run-pass/rt-start-main-thread.rs (limited to 'src/libnative') diff --git a/Makefile.in b/Makefile.in index d5a62f11e05..f1b18e8f64b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -280,9 +280,15 @@ define CHECK_FOR_OLD_GLOB_MATCHES_EXCEPT endef # Same interface as above, but deletes rather than just listing the files. +ifdef VERBOSE define REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT $(Q)MATCHES="$(filter-out %$(3),$(wildcard $(1)/$(2)))"; if [ -n "$$MATCHES" ] ; then echo "warning: removing previous" \'$(2)\' "libraries:" $$MATCHES; rm $$MATCHES ; fi endef +else +define REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT + $(Q)MATCHES="$(filter-out %$(3),$(wildcard $(1)/$(2)))"; if [ -n "$$MATCHES" ] ; then rm $$MATCHES ; fi +endef +endif # We use a different strategy for LIST_ALL_OLD_GLOB_MATCHES_EXCEPT # than in the macros above because it needs the result of running the diff --git a/mk/target.mk b/mk/target.mk index 3746a4eafc0..db8488f792f 100644 --- a/mk/target.mk +++ b/mk/target.mk @@ -161,16 +161,13 @@ $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTC_$(3)): \ $$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(LIBRUSTC_GLOB_$(2)),$$(notdir $$@)) $$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(LIBRUSTC_RGLOB_$(2)),$$(notdir $$@)) -# NOTE: after the next snapshot remove these '-L' flags $$(TBIN$(1)_T_$(2)_H_$(3))/rustc$$(X_$(3)): \ $$(DRIVER_CRATE) \ - $$(TSREQ$(1)_T_$(2)_H_$(3)) \ + $$(SREQ$(1)_T_$(2)_H_$(3)) \ $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTC_$(3)) \ | $$(TBIN$(1)_T_$(2)_H_$(3))/ @$$(call E, compile_and_link: $$@) - $$(STAGE$(1)_T_$(2)_H_$(3)) --cfg rustc -o $$@ $$< \ - -L $$(UV_SUPPORT_DIR_$(2)) \ - -L $$(dir $$(LIBUV_LIB_$(2))) + $$(STAGE$(1)_T_$(2)_H_$(3)) --cfg rustc -o $$@ $$< ifdef CFG_ENABLE_PAX_FLAGS @$$(call E, apply PaX flags: $$@) @"$(CFG_PAXCTL)" -cm "$$@" diff --git a/src/compiletest/compiletest.rs b/src/compiletest/compiletest.rs index 0fb75b7c8e0..89b6f06abfc 100644 --- a/src/compiletest/compiletest.rs +++ b/src/compiletest/compiletest.rs @@ -16,7 +16,7 @@ extern mod extra; use std::os; -use std::rt; +use std::io; use std::io::fs; use extra::getopts; @@ -234,7 +234,7 @@ pub fn run_tests(config: &config) { // sadly osx needs some file descriptor limits raised for running tests in // parallel (especially when we have lots and lots of child processes). // For context, see #8904 - rt::test::prepare_for_lots_of_tests(); + io::test::raise_fd_limit(); let res = test::run_tests_console(&opts, tests); if !res { fail!("Some tests failed"); } } diff --git a/src/etc/licenseck.py b/src/etc/licenseck.py index 78d0973fdfe..073322b0815 100644 --- a/src/etc/licenseck.py +++ b/src/etc/licenseck.py @@ -76,9 +76,9 @@ exceptions = [ "rt/isaac/randport.cpp", # public domain "rt/isaac/rand.h", # public domain "rt/isaac/standard.h", # public domain - "libstd/rt/mpsc_queue.rs", # BSD - "libstd/rt/spsc_queue.rs", # BSD - "libstd/rt/mpmc_bounded_queue.rs", # BSD + "libstd/sync/mpsc_queue.rs", # BSD + "libstd/sync/spsc_queue.rs", # BSD + "libstd/sync/mpmc_bounded_queue.rs", # BSD ] def check_license(name, contents): diff --git a/src/libgreen/lib.rs b/src/libgreen/lib.rs index bb219936ae0..57e2a0bfe16 100644 --- a/src/libgreen/lib.rs +++ b/src/libgreen/lib.rs @@ -17,6 +17,7 @@ //! This can be optionally linked in to rust programs in order to provide M:N //! functionality inside of 1:1 programs. +#[pkgid = "green#0.9-pre"]; #[link(name = "green", package_id = "green", vers = "0.9-pre", @@ -30,17 +31,16 @@ // NB this does *not* include globs, please keep it that way. #[feature(macro_rules)]; -use std::cast; use std::os; -use std::rt::thread::Thread; -use std::rt; use std::rt::crate_map; use std::rt::rtio; -use std::sync::deque; +use std::rt::thread::Thread; +use std::rt; use std::sync::atomics::{SeqCst, AtomicUint, INIT_ATOMIC_UINT}; +use std::sync::deque; use std::task::TaskOpts; -use std::vec; use std::util; +use std::vec; use stdtask = std::rt::task; use sched::{Shutdown, Scheduler, SchedHandle, TaskFromFriend, NewNeighbor}; @@ -58,9 +58,9 @@ pub mod sleeper_list; pub mod stack; pub mod task; -#[cfg(stage0)] #[lang = "start"] pub fn lang_start(main: *u8, argc: int, argv: **u8) -> int { + use std::cast; do start(argc, argv) { let main: extern "Rust" fn() = unsafe { cast::transmute(main) }; main(); @@ -103,7 +103,15 @@ pub fn start(argc: int, argv: **u8, main: proc()) -> int { /// have returned. pub fn run(main: proc()) -> int { let mut pool = SchedPool::new(PoolConfig::new()); - pool.spawn(TaskOpts::new(), main); + let (port, chan) = Chan::new(); + let mut opts = TaskOpts::new(); + opts.notify_chan = Some(chan); + pool.spawn(opts, main); + do pool.spawn(TaskOpts::new()) { + if port.recv().is_err() { + os::set_exit_status(rt::DEFAULT_ERROR_CODE); + } + } unsafe { stdtask::wait_for_completion(); } pool.shutdown(); os::get_exit_status() diff --git a/src/libnative/lib.rs b/src/libnative/lib.rs index b97d9127277..44b66a7804d 100644 --- a/src/libnative/lib.rs +++ b/src/libnative/lib.rs @@ -14,6 +14,7 @@ //! runtime. In addition, all I/O provided by this crate is the thread blocking //! version of I/O. +#[pkgid = "native#0.9-pre"]; #[link(name = "native", package_id = "native", vers = "0.9-pre", @@ -24,6 +25,7 @@ #[crate_type = "rlib"]; #[crate_type = "dylib"]; +// Allow check-stage0-native for now #[cfg(stage0, test)] extern mod green; // NB this crate explicitly does *not* allow glob imports, please seriously diff --git a/src/librustuv/homing.rs b/src/librustuv/homing.rs index 1ee64398ca3..d7be06724a0 100644 --- a/src/librustuv/homing.rs +++ b/src/librustuv/homing.rs @@ -31,6 +31,8 @@ //! This enqueueing is done with a concurrent queue from libstd, and the //! signalling is achieved with an async handle. +#[allow(dead_code)]; + use std::rt::local::Local; use std::rt::rtio::LocalIo; use std::rt::task::{Task, BlockedTask}; diff --git a/src/librustuv/queue.rs b/src/librustuv/queue.rs index 22e7925b211..b36bdf62775 100644 --- a/src/librustuv/queue.rs +++ b/src/librustuv/queue.rs @@ -18,6 +18,8 @@ //! event loop alive we use uv_ref and uv_unref in order to control when the //! async handle is active or not. +#[allow(dead_code)]; + use std::cast; use std::libc::{c_void, c_int}; use std::rt::task::BlockedTask; diff --git a/src/libstd/io/test.rs b/src/libstd/io/test.rs index dd24150e03e..e273aedf7cc 100644 --- a/src/libstd/io/test.rs +++ b/src/libstd/io/test.rs @@ -113,3 +113,82 @@ fn base_port() -> u16 { return final_base; } + +pub fn raise_fd_limit() { + unsafe { darwin_fd_limit::raise_fd_limit() } +} + +#[cfg(target_os="macos")] +#[allow(non_camel_case_types)] +mod darwin_fd_limit { + /*! + * darwin_fd_limit exists to work around an issue where launchctl on Mac OS X defaults the + * rlimit maxfiles to 256/unlimited. The default soft limit of 256 ends up being far too low + * for our multithreaded scheduler testing, depending on the number of cores available. + * + * This fixes issue #7772. + */ + + use libc; + type rlim_t = libc::uint64_t; + struct rlimit { + rlim_cur: rlim_t, + rlim_max: rlim_t + } + #[nolink] + extern { + // name probably doesn't need to be mut, but the C function doesn't specify const + fn sysctl(name: *mut libc::c_int, namelen: libc::c_uint, + oldp: *mut libc::c_void, oldlenp: *mut libc::size_t, + newp: *mut libc::c_void, newlen: libc::size_t) -> libc::c_int; + fn getrlimit(resource: libc::c_int, rlp: *mut rlimit) -> libc::c_int; + fn setrlimit(resource: libc::c_int, rlp: *rlimit) -> libc::c_int; + } + static CTL_KERN: libc::c_int = 1; + static KERN_MAXFILESPERPROC: libc::c_int = 29; + static RLIMIT_NOFILE: libc::c_int = 8; + + pub unsafe fn raise_fd_limit() { + // The strategy here is to fetch the current resource limits, read the kern.maxfilesperproc + // sysctl value, and bump the soft resource limit for maxfiles up to the sysctl value. + use ptr::{to_unsafe_ptr, to_mut_unsafe_ptr, mut_null}; + use mem::size_of_val; + use os::last_os_error; + + // Fetch the kern.maxfilesperproc value + let mut mib: [libc::c_int, ..2] = [CTL_KERN, KERN_MAXFILESPERPROC]; + let mut maxfiles: libc::c_int = 0; + let mut size: libc::size_t = size_of_val(&maxfiles) as libc::size_t; + if sysctl(to_mut_unsafe_ptr(&mut mib[0]), 2, + to_mut_unsafe_ptr(&mut maxfiles) as *mut libc::c_void, + to_mut_unsafe_ptr(&mut size), + mut_null(), 0) != 0 { + let err = last_os_error(); + error!("raise_fd_limit: error calling sysctl: {}", err); + return; + } + + // Fetch the current resource limits + let mut rlim = rlimit{rlim_cur: 0, rlim_max: 0}; + if getrlimit(RLIMIT_NOFILE, to_mut_unsafe_ptr(&mut rlim)) != 0 { + let err = last_os_error(); + error!("raise_fd_limit: error calling getrlimit: {}", err); + return; + } + + // Bump the soft limit to the smaller of kern.maxfilesperproc and the hard limit + rlim.rlim_cur = ::cmp::min(maxfiles as rlim_t, rlim.rlim_max); + + // Set our newly-increased resource limit + if setrlimit(RLIMIT_NOFILE, to_unsafe_ptr(&rlim)) != 0 { + let err = last_os_error(); + error!("raise_fd_limit: error calling setrlimit: {}", err); + return; + } + } +} + +#[cfg(not(target_os="macos"))] +mod darwin_fd_limit { + pub unsafe fn raise_fd_limit() {} +} diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs index 91e285b1061..c0e1086483d 100644 --- a/src/libstd/rt/task.rs +++ b/src/libstd/rt/task.rs @@ -38,8 +38,13 @@ use task::{TaskResult, TaskOpts}; use unstable::finally::Finally; use unstable::mutex::{Mutex, MUTEX_INIT}; -#[cfg(stage0)] pub use rt::unwind::begin_unwind; +#[cfg(stage0)] +pub use rt::unwind::begin_unwind; +// These two statics are used as bookeeping to keep track of the rust runtime's +// count of threads. In 1:1 contexts, this is used to know when to return from +// the main function, and in M:N contexts this is used to know when to shut down +// the pool of schedulers. static mut TASK_COUNT: AtomicUint = INIT_ATOMIC_UINT; static mut TASK_LOCK: Mutex = MUTEX_INIT; @@ -181,10 +186,15 @@ impl Task { // Cleanup the dynamic borrowck debugging info borrowck::clear_task_borrow_list(); - // TODO: dox + // Here we must unsafely borrow the task in order to not remove it from + // TLS. When collecting failure, we may attempt to send on a channel (or + // just run aribitrary code), so we must be sure to still have a local + // task in TLS. unsafe { let me: *mut Task = Local::unsafe_borrow(); (*me).death.collect_failure((*me).unwinder.result()); + + // see comments on these statics for why they're used if TASK_COUNT.fetch_sub(1, SeqCst) == 1 { TASK_LOCK.lock(); TASK_LOCK.signal(); @@ -386,6 +396,10 @@ impl Drop for Death { } } +/// The main function of all rust executables will by default use this function. +/// This function will *block* the OS thread (hence the `unsafe`) waiting for +/// all known tasks to complete. Once this function has returned, it is +/// guaranteed that no more user-defined code is still running. pub unsafe fn wait_for_completion() { TASK_LOCK.lock(); while TASK_COUNT.load(SeqCst) > 0 { diff --git a/src/libstd/sync/arc.rs b/src/libstd/sync/arc.rs index 7b94a3acc2b..b405104c09a 100644 --- a/src/libstd/sync/arc.rs +++ b/src/libstd/sync/arc.rs @@ -32,7 +32,7 @@ use vec; /// An atomically reference counted pointer. /// /// Enforces no shared-memory safety. -#[unsafe_no_drop_flag] +//#[unsafe_no_drop_flag] FIXME: #9758 pub struct UnsafeArc { priv data: *mut ArcData, } diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 930d25e7443..aa7e0d0eced 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -606,7 +606,6 @@ impl AstBuilder for @ExtCtxt { ~[ self.ident_of("std"), self.ident_of("rt"), - self.ident_of("task"), self.ident_of("begin_unwind"), ], ~[ diff --git a/src/test/run-pass/core-rt-smoke.rs b/src/test/run-pass/core-rt-smoke.rs deleted file mode 100644 index 6e3d9629da0..00000000000 --- a/src/test/run-pass/core-rt-smoke.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// xfail-fast - -// A simple test of starting the runtime manually - -#[start] -fn start(argc: int, argv: **u8) -> int { - do std::rt::start(argc, argv) { - info!("creating my own runtime is joy"); - } -} diff --git a/src/test/run-pass/native-print-no-uv.rs b/src/test/run-pass/native-print-no-uv.rs deleted file mode 100644 index d3b6d605984..00000000000 --- a/src/test/run-pass/native-print-no-uv.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// xfail-fast - -#[no_uv]; - -fn main() { - println!("hello"); -} diff --git a/src/test/run-pass/rt-run-twice.rs b/src/test/run-pass/rt-run-twice.rs deleted file mode 100644 index a9a26c2fbb1..00000000000 --- a/src/test/run-pass/rt-run-twice.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// xfail-fast make-check does not like `#[start]` - -use std::rt; - -#[start] -fn start(argc: int, argv: **u8) -> int { - do rt::start(argc, argv) { - println("First invocation"); - }; - - do rt::start(argc, argv) { - println("Second invocation"); - }; - - 0 -} diff --git a/src/test/run-pass/rt-start-main-thread.rs b/src/test/run-pass/rt-start-main-thread.rs deleted file mode 100644 index 47a723ce6e1..00000000000 --- a/src/test/run-pass/rt-start-main-thread.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// xfail-fast - -#[start] -fn start(argc: int, argv: **u8) -> int { - do std::rt::start_on_main_thread(argc, argv) { - info!("running on main thread"); - do spawn { - info!("running on another thread"); - } - } -} diff --git a/src/test/run-pass/spawning-with-debug.rs b/src/test/run-pass/spawning-with-debug.rs index 76975d15c1d..f8094f9fdb9 100644 --- a/src/test/run-pass/spawning-with-debug.rs +++ b/src/test/run-pass/spawning-with-debug.rs @@ -17,6 +17,5 @@ use std::task; fn main() { let mut t = task::task(); - t.sched_mode(task::SingleThreaded); t.spawn(proc() ()); } diff --git a/src/test/run-pass/use.rs b/src/test/run-pass/use.rs index ddd4b10fd5c..56ce5397efb 100644 --- a/src/test/run-pass/use.rs +++ b/src/test/run-pass/use.rs @@ -10,6 +10,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// xfail-fast + #[allow(unused_imports)]; #[no_std]; @@ -25,4 +27,5 @@ mod baz { pub use x = std::str; } -pub fn main() { } +#[start] +pub fn start(_: int, _: **u8) -> int { 3 } -- cgit 1.4.1-3-g733a5 From 51c03c1f35f6b076928a1e5b94ec81e6d00c3ac2 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sun, 15 Dec 2013 00:42:21 -0800 Subject: green: Properly wait for main before shutdown There was a race in the code previously where schedulers would *immediately* shut down after spawning the main task (because the global task count would still be 0). This fixes the logic by blocking the sched pool task in receving on a port instead of spawning a task into the pool to receive on a port. The modifications necessary were to have a "simple task" running by the time the code is executing, but this is a simple enough thing to implement and I forsee this being necessary to have implemented in the future anyway. --- src/libgreen/lib.rs | 49 ++++++++++++++++++++++---------- src/libgreen/simple.rs | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/libnative/lib.rs | 25 +++++++++------- src/libnative/task.rs | 10 ++----- src/libstd/rt/task.rs | 27 ++++++++++-------- 5 files changed, 143 insertions(+), 45 deletions(-) create mode 100644 src/libgreen/simple.rs (limited to 'src/libnative') diff --git a/src/libgreen/lib.rs b/src/libgreen/lib.rs index 57e2a0bfe16..7318eaaf679 100644 --- a/src/libgreen/lib.rs +++ b/src/libgreen/lib.rs @@ -33,7 +33,9 @@ use std::os; use std::rt::crate_map; +use std::rt::local::Local; use std::rt::rtio; +use std::rt::task::Task; use std::rt::thread::Thread; use std::rt; use std::sync::atomics::{SeqCst, AtomicUint, INIT_ATOMIC_UINT}; @@ -41,7 +43,6 @@ use std::sync::deque; use std::task::TaskOpts; use std::util; use std::vec; -use stdtask = std::rt::task; use sched::{Shutdown, Scheduler, SchedHandle, TaskFromFriend, NewNeighbor}; use sleeper_list::SleeperList; @@ -49,6 +50,7 @@ use stack::StackPool; use task::GreenTask; mod macros; +mod simple; pub mod basic; pub mod context; @@ -61,16 +63,20 @@ pub mod task; #[lang = "start"] pub fn lang_start(main: *u8, argc: int, argv: **u8) -> int { use std::cast; - do start(argc, argv) { - let main: extern "Rust" fn() = unsafe { cast::transmute(main) }; - main(); - } + let mut ret = None; + simple::task().run(|| { + ret = Some(do start(argc, argv) { + let main: extern "Rust" fn() = unsafe { cast::transmute(main) }; + main(); + }) + }); + ret.unwrap() } /// Set up a default runtime configuration, given compiler-supplied arguments. /// -/// This function will block the current thread of execution until the entire -/// pool of M:N schedulers have exited. +/// This function will block until the entire pool of M:N schedulers have +/// exited. This function also requires a local task to be available. /// /// # Arguments /// @@ -95,24 +101,37 @@ pub fn start(argc: int, argv: **u8, main: proc()) -> int { /// Execute the main function in a pool of M:N schedulers. /// -/// Configures the runtime according to the environment, by default -/// using a task scheduler with the same number of threads as cores. -/// Returns a process exit code. +/// Configures the runtime according to the environment, by default using a task +/// scheduler with the same number of threads as cores. Returns a process exit +/// code. /// /// This function will not return until all schedulers in the associated pool /// have returned. pub fn run(main: proc()) -> int { + // Create a scheduler pool and spawn the main task into this pool. We will + // get notified over a channel when the main task exits. let mut pool = SchedPool::new(PoolConfig::new()); let (port, chan) = Chan::new(); let mut opts = TaskOpts::new(); opts.notify_chan = Some(chan); pool.spawn(opts, main); - do pool.spawn(TaskOpts::new()) { - if port.recv().is_err() { - os::set_exit_status(rt::DEFAULT_ERROR_CODE); - } + + // Wait for the main task to return, and set the process error code + // appropriately. + if port.recv().is_err() { + os::set_exit_status(rt::DEFAULT_ERROR_CODE); } - unsafe { stdtask::wait_for_completion(); } + + // Once the main task has exited and we've set our exit code, wait for all + // spawned sub-tasks to finish running. This is done to allow all schedulers + // to remain active while there are still tasks possibly running. + unsafe { + let mut task = Local::borrow(None::); + task.get().wait_for_other_tasks(); + } + + // Now that we're sure all tasks are dead, shut down the pool of schedulers, + // waiting for them all to return. pool.shutdown(); os::get_exit_status() } diff --git a/src/libgreen/simple.rs b/src/libgreen/simple.rs new file mode 100644 index 00000000000..6fd2c436b2e --- /dev/null +++ b/src/libgreen/simple.rs @@ -0,0 +1,77 @@ +// 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. + +//! A small module implementing a simple "runtime" used for bootstrapping a rust +//! scheduler pool and then interacting with it. + +use std::cast; +use std::rt::Runtime; +use std::task::TaskOpts; +use std::rt::rtio; +use std::rt::local::Local; +use std::rt::task::{Task, BlockedTask}; +use std::unstable::sync::LittleLock; + +struct SimpleTask { + lock: LittleLock, +} + +impl Runtime for SimpleTask { + // Implement the simple tasks of descheduling and rescheduling, but only in + // a simple number of cases. + fn deschedule(mut ~self, times: uint, mut cur_task: ~Task, + f: |BlockedTask| -> Result<(), BlockedTask>) { + assert!(times == 1); + + let my_lock: *mut LittleLock = &mut self.lock; + cur_task.put_runtime(self as ~Runtime); + + unsafe { + let cur_task_dupe = *cast::transmute::<&~Task, &uint>(&cur_task); + let task = BlockedTask::block(cur_task); + + let mut guard = (*my_lock).lock(); + match f(task) { + Ok(()) => guard.wait(), + Err(task) => { cast::forget(task.wake()); } + } + drop(guard); + cur_task = cast::transmute::(cur_task_dupe); + } + Local::put(cur_task); + } + fn reawaken(mut ~self, mut to_wake: ~Task) { + let lock: *mut LittleLock = &mut self.lock; + to_wake.put_runtime(self as ~Runtime); + unsafe { + cast::forget(to_wake); + let _l = (*lock).lock(); + (*lock).signal(); + } + } + + // These functions are all unimplemented and fail as a result. This is on + // purpose. A "simple task" is just that, a very simple task that can't + // really do a whole lot. The only purpose of the task is to get us off our + // feet and running. + fn yield_now(~self, _cur_task: ~Task) { fail!() } + fn maybe_yield(~self, _cur_task: ~Task) { fail!() } + fn spawn_sibling(~self, _cur_task: ~Task, _opts: TaskOpts, _f: proc()) { + fail!() + } + fn local_io<'a>(&'a mut self) -> Option> { None } + fn wrap(~self) -> ~Any { fail!() } +} + +pub fn task() -> ~Task { + let mut task = ~Task::new(); + task.put_runtime(~SimpleTask { lock: LittleLock::new() } as ~Runtime); + return task; +} diff --git a/src/libnative/lib.rs b/src/libnative/lib.rs index 44b66a7804d..60ae239ee97 100644 --- a/src/libnative/lib.rs +++ b/src/libnative/lib.rs @@ -33,15 +33,16 @@ // answer is that you don't need them) use std::os; +use std::rt::local::Local; +use std::rt::task::Task; use std::rt; -use stdtask = std::rt::task; pub mod io; pub mod task; // XXX: this should not exist here -#[cfg(stage0, notready)] +#[cfg(stage0)] #[lang = "start"] pub fn lang_start(main: *u8, argc: int, argv: **u8) -> int { use std::cast; @@ -72,9 +73,13 @@ pub fn lang_start(main: *u8, argc: int, argv: **u8) -> int { /// exited. pub fn start(argc: int, argv: **u8, main: proc()) -> int { rt::init(argc, argv); - let exit_code = run(main); + let mut exit_code = None; + let mut main = Some(main); + task::new().run(|| { + exit_code = Some(run(main.take_unwrap())); + }); unsafe { rt::cleanup(); } - return exit_code; + return exit_code.unwrap(); } /// Executes a procedure on the current thread in a Rust task context. @@ -82,11 +87,11 @@ pub fn start(argc: int, argv: **u8, main: proc()) -> int { /// This function has all of the same details as `start` except for a different /// number of arguments. pub fn run(main: proc()) -> int { - // Create a task, run the procedure in it, and then wait for everything. - task::run(task::new(), main); - - // Block this OS task waiting for everything to finish. - unsafe { stdtask::wait_for_completion() } - + // Run the main procedure and then wait for everything to finish + main(); + unsafe { + let mut task = Local::borrow(None::); + task.get().wait_for_other_tasks(); + } os::get_exit_status() } diff --git a/src/libnative/task.rs b/src/libnative/task.rs index 48768def067..0d5e08979ca 100644 --- a/src/libnative/task.rs +++ b/src/libnative/task.rs @@ -77,17 +77,11 @@ pub fn spawn_opts(opts: TaskOpts, f: proc()) { stack::record_stack_bounds(my_stack - stack + 1024, my_stack); } - run(task, f); + let mut f = Some(f); + task.run(|| { f.take_unwrap()() }); }) } -/// Runs a task once, consuming the task. The given procedure is run inside of -/// the task. -pub fn run(t: ~Task, f: proc()) { - let mut f = Some(f); - t.run(|| { f.take_unwrap()(); }); -} - // This structure is the glue between channels and the 1:1 scheduling mode. This // structure is allocated once per task. struct Ops { diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs index c0e1086483d..765f0b427cd 100644 --- a/src/libstd/rt/task.rs +++ b/src/libstd/rt/task.rs @@ -292,6 +292,21 @@ impl Task { pub fn local_io<'a>(&'a mut self) -> Option> { self.imp.get_mut_ref().local_io() } + + /// The main function of all rust executables will by default use this + /// function. This function will *block* the OS thread (hence the `unsafe`) + /// waiting for all known tasks to complete. Once this function has + /// returned, it is guaranteed that no more user-defined code is still + /// running. + pub unsafe fn wait_for_other_tasks(&mut self) { + TASK_COUNT.fetch_sub(1, SeqCst); // don't count ourselves + TASK_LOCK.lock(); + while TASK_COUNT.load(SeqCst) > 0 { + TASK_LOCK.wait(); + } + TASK_LOCK.unlock(); + TASK_COUNT.fetch_add(1, SeqCst); // add ourselves back in + } } impl Drop for Task { @@ -396,18 +411,6 @@ impl Drop for Death { } } -/// The main function of all rust executables will by default use this function. -/// This function will *block* the OS thread (hence the `unsafe`) waiting for -/// all known tasks to complete. Once this function has returned, it is -/// guaranteed that no more user-defined code is still running. -pub unsafe fn wait_for_completion() { - TASK_LOCK.lock(); - while TASK_COUNT.load(SeqCst) > 0 { - TASK_LOCK.wait(); - } - TASK_LOCK.unlock(); -} - #[cfg(test)] mod test { use super::*; -- cgit 1.4.1-3-g733a5 From 962af9198f8f2a1e2d5121ac216b6f4d574ae54c Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sun, 15 Dec 2013 22:19:34 -0800 Subject: native: Protect against spurious wakeups on cvars This is a very real problem with cvars on normal systems, and all of channels will not work if spurious wakeups are accepted. This problem is just solved with a synchronized flag (accessed in the cvar's lock) to see whether a signal() actually happened or whether it's spurious. --- src/libgreen/simple.rs | 37 ++++++++++++++++++++++++------------- src/libnative/task.rs | 42 +++++++++++++++++++++++++++++------------- src/libstd/comm/mod.rs | 2 +- 3 files changed, 54 insertions(+), 27 deletions(-) (limited to 'src/libnative') diff --git a/src/libgreen/simple.rs b/src/libgreen/simple.rs index 6fd2c436b2e..0db81c5fea3 100644 --- a/src/libgreen/simple.rs +++ b/src/libgreen/simple.rs @@ -13,14 +13,15 @@ use std::cast; use std::rt::Runtime; -use std::task::TaskOpts; -use std::rt::rtio; use std::rt::local::Local; +use std::rt::rtio; use std::rt::task::{Task, BlockedTask}; +use std::task::TaskOpts; use std::unstable::sync::LittleLock; struct SimpleTask { lock: LittleLock, + awoken: bool, } impl Runtime for SimpleTask { @@ -30,30 +31,37 @@ impl Runtime for SimpleTask { f: |BlockedTask| -> Result<(), BlockedTask>) { assert!(times == 1); - let my_lock: *mut LittleLock = &mut self.lock; + let me = &mut *self as *mut SimpleTask; + let cur_dupe = &*cur_task as *Task; cur_task.put_runtime(self as ~Runtime); + let task = BlockedTask::block(cur_task); + // See libnative/task.rs for what's going on here with the `awoken` + // field and the while loop around wait() unsafe { - let cur_task_dupe = *cast::transmute::<&~Task, &uint>(&cur_task); - let task = BlockedTask::block(cur_task); - - let mut guard = (*my_lock).lock(); + let mut guard = (*me).lock.lock(); + (*me).awoken = false; match f(task) { - Ok(()) => guard.wait(), + Ok(()) => { + while !(*me).awoken { + guard.wait(); + } + } Err(task) => { cast::forget(task.wake()); } } drop(guard); - cur_task = cast::transmute::(cur_task_dupe); + cur_task = cast::transmute(cur_dupe); } Local::put(cur_task); } fn reawaken(mut ~self, mut to_wake: ~Task) { - let lock: *mut LittleLock = &mut self.lock; + let me = &mut *self as *mut SimpleTask; to_wake.put_runtime(self as ~Runtime); unsafe { cast::forget(to_wake); - let _l = (*lock).lock(); - (*lock).signal(); + let _l = (*me).lock.lock(); + (*me).awoken = true; + (*me).lock.signal(); } } @@ -72,6 +80,9 @@ impl Runtime for SimpleTask { pub fn task() -> ~Task { let mut task = ~Task::new(); - task.put_runtime(~SimpleTask { lock: LittleLock::new() } as ~Runtime); + task.put_runtime(~SimpleTask { + lock: LittleLock::new(), + awoken: false, + } as ~Runtime); return task; } diff --git a/src/libnative/task.rs b/src/libnative/task.rs index 0d5e08979ca..12e361d8041 100644 --- a/src/libnative/task.rs +++ b/src/libnative/task.rs @@ -33,6 +33,7 @@ pub fn new() -> ~Task { let mut task = ~Task::new(); task.put_runtime(~Ops { lock: unsafe { Mutex::new() }, + awoken: false, } as ~rt::Runtime); return task; } @@ -85,7 +86,8 @@ pub fn spawn_opts(opts: TaskOpts, f: proc()) { // This structure is the glue between channels and the 1:1 scheduling mode. This // structure is allocated once per task. struct Ops { - lock: Mutex, // native synchronization + lock: Mutex, // native synchronization + awoken: bool, // used to prevent spurious wakeups } impl rt::Runtime for Ops { @@ -139,9 +141,16 @@ impl rt::Runtime for Ops { // reasoning for this is the same logic as above in that the task silently // transfers ownership via the `uint`, not through normal compiler // semantics. + // + // On a mildly unrelated note, it should also be pointed out that OS + // condition variables are susceptible to spurious wakeups, which we need to + // be ready for. In order to accomodate for this fact, we have an extra + // `awoken` field which indicates whether we were actually woken up via some + // invocation of `reawaken`. This flag is only ever accessed inside the + // lock, so there's no need to make it atomic. fn deschedule(mut ~self, times: uint, mut cur_task: ~Task, f: |BlockedTask| -> Result<(), BlockedTask>) { - let my_lock: *mut Mutex = &mut self.lock as *mut Mutex; + let me = &mut *self as *mut Ops; cur_task.put_runtime(self as ~rt::Runtime); unsafe { @@ -149,15 +158,21 @@ impl rt::Runtime for Ops { let task = BlockedTask::block(cur_task); if times == 1 { - (*my_lock).lock(); + (*me).lock.lock(); + (*me).awoken = false; match f(task) { - Ok(()) => (*my_lock).wait(), + Ok(()) => { + while !(*me).awoken { + (*me).lock.wait(); + } + } Err(task) => { cast::forget(task.wake()); } } - (*my_lock).unlock(); + (*me).lock.unlock(); } else { let mut iter = task.make_selectable(times); - (*my_lock).lock(); + (*me).lock.lock(); + (*me).awoken = false; let success = iter.all(|task| { match f(task) { Ok(()) => true, @@ -167,10 +182,10 @@ impl rt::Runtime for Ops { } } }); - if success { - (*my_lock).wait(); + while success && !(*me).awoken { + (*me).lock.wait(); } - (*my_lock).unlock(); + (*me).lock.unlock(); } // re-acquire ownership of the task cur_task = cast::transmute::(cur_task_dupe); @@ -184,12 +199,13 @@ impl rt::Runtime for Ops { // why it's valid to do so. fn reawaken(mut ~self, mut to_wake: ~Task, _can_resched: bool) { unsafe { - let lock: *mut Mutex = &mut self.lock as *mut Mutex; + let me = &mut *self as *mut Ops; to_wake.put_runtime(self as ~rt::Runtime); cast::forget(to_wake); - (*lock).lock(); - (*lock).signal(); - (*lock).unlock(); + (*me).lock.lock(); + (*me).awoken = true; + (*me).lock.signal(); + (*me).lock.unlock(); } } diff --git a/src/libstd/comm/mod.rs b/src/libstd/comm/mod.rs index 7b464bc2f32..ae440894b4e 100644 --- a/src/libstd/comm/mod.rs +++ b/src/libstd/comm/mod.rs @@ -875,7 +875,7 @@ impl Port { let data = self.try_recv_inc(false); if data.is_none() && unsafe { (*packet).cnt.load(SeqCst) } != DISCONNECTED { - fail!("bug: woke up too soon"); + fail!("bug: woke up too soon {}", unsafe { (*packet).cnt.load(SeqCst) }); } return data; } -- cgit 1.4.1-3-g733a5 From 1c4af5e3d93fe2953c31f8a76ee2aed15069204a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 18 Dec 2013 10:14:44 -0800 Subject: rustuv: Remove the id() function from IoFactory The only user of this was the homing code in librustuv, and it just manually does the cast from a pointer to a uint now. --- src/libnative/io/mod.rs | 3 --- src/librustuv/homing.rs | 38 ++++++++++++++++++++------------------ src/librustuv/lib.rs | 7 ++----- src/librustuv/uvio.rs | 7 ++++--- src/librustuv/uvll.rs | 2 +- src/libstd/rt/rtio.rs | 2 -- 6 files changed, 27 insertions(+), 32 deletions(-) (limited to 'src/libnative') diff --git a/src/libnative/io/mod.rs b/src/libnative/io/mod.rs index 36e3f8af190..32056215e7c 100644 --- a/src/libnative/io/mod.rs +++ b/src/libnative/io/mod.rs @@ -111,9 +111,6 @@ fn mkerr_winbool(ret: libc::c_int) -> IoResult<()> { pub struct IoFactory; impl rtio::IoFactory for IoFactory { - // all native io factories are the same - fn id(&self) -> uint { 0 } - // networking fn tcp_connect(&mut self, _addr: SocketAddr) -> IoResult<~RtioTcpStream> { Err(unimpl()) diff --git a/src/librustuv/homing.rs b/src/librustuv/homing.rs index d7be06724a0..16534b7b38b 100644 --- a/src/librustuv/homing.rs +++ b/src/librustuv/homing.rs @@ -33,6 +33,7 @@ #[allow(dead_code)]; +use std::cast; use std::rt::local::Local; use std::rt::rtio::LocalIo; use std::rt::task::{Task, BlockedTask}; @@ -70,6 +71,17 @@ impl Clone for HomeHandle { } } +pub fn local_id() -> uint { + let mut io = match LocalIo::borrow() { + Some(io) => io, None => return 0, + }; + let io = io.get(); + unsafe { + let (_vtable, ptr): (uint, uint) = cast::transmute(io); + return ptr; + } +} + pub trait HomingIO { fn home<'r>(&'r mut self) -> &'r mut HomeHandle; @@ -79,35 +91,26 @@ pub trait HomingIO { fn go_to_IO_home(&mut self) -> uint { let _f = ForbidUnwind::new("going home"); - let mut cur_task: ~Task = Local::take(); - let cur_loop_id = { - let mut io = cur_task.local_io().expect("libuv must have I/O"); - io.get().id() - }; + let cur_loop_id = local_id(); + let destination = self.home().id; // Try at all costs to avoid the homing operation because it is quite // expensive. Hence, we only deschedule/send if we're not on the correct // event loop. If we're already on the home event loop, then we're good // to go (remember we have no preemption, so we're guaranteed to stay on // this event loop as long as we avoid the scheduler). - if cur_loop_id != self.home().id { + if cur_loop_id != destination { + let cur_task: ~Task = Local::take(); cur_task.deschedule(1, |task| { self.home().send(task); Ok(()) }); // Once we wake up, assert that we're in the right location - let cur_loop_id = { - let mut io = LocalIo::borrow().expect("libuv must have I/O"); - io.get().id() - }; - assert_eq!(cur_loop_id, self.home().id); - - cur_loop_id - } else { - Local::put(cur_task); - cur_loop_id + assert_eq!(local_id(), destination); } + + return destination; } /// Fires a single homing missile, returning another missile targeted back @@ -130,8 +133,7 @@ impl HomingMissile { /// Check at runtime that the task has *not* transplanted itself to a /// different I/O loop while executing. pub fn check(&self, msg: &'static str) { - let mut io = LocalIo::borrow().expect("libuv must have I/O"); - assert!(io.get().id() == self.io_home, "{}", msg); + assert!(local_id() == self.io_home, "{}", msg); } } diff --git a/src/librustuv/lib.rs b/src/librustuv/lib.rs index 49d695ea3fb..2ef10dd33ac 100644 --- a/src/librustuv/lib.rs +++ b/src/librustuv/lib.rs @@ -53,7 +53,6 @@ use std::ptr::null; use std::ptr; use std::rt::local::Local; use std::rt::task::{BlockedTask, Task}; -use std::rt::rtio::LocalIo; use std::str::raw::from_c_str; use std::str; use std::task; @@ -161,18 +160,16 @@ pub struct ForbidSwitch { impl ForbidSwitch { fn new(s: &'static str) -> ForbidSwitch { - let mut io = LocalIo::borrow().expect("libuv must have local I/O"); ForbidSwitch { msg: s, - io: io.get().id(), + io: homing::local_id(), } } } impl Drop for ForbidSwitch { fn drop(&mut self) { - let mut io = LocalIo::borrow().expect("libuv must have local I/O"); - assert!(self.io == io.get().id(), + assert!(self.io == homing::local_id(), "didnt want a scheduler switch: {}", self.msg); } diff --git a/src/librustuv/uvio.rs b/src/librustuv/uvio.rs index 322bead8be4..9e7343aa2da 100644 --- a/src/librustuv/uvio.rs +++ b/src/librustuv/uvio.rs @@ -132,13 +132,14 @@ impl UvIoFactory { pub fn uv_loop<'a>(&mut self) -> *uvll::uv_loop_t { self.loop_.handle } pub fn make_handle(&mut self) -> HomeHandle { - HomeHandle::new(self.id(), &mut **self.handle_pool.get_mut_ref()) + // It's understood by the homing code that the "local id" is just the + // pointer of the local I/O factory cast to a uint. + let id: uint = unsafe { cast::transmute_copy(&self) }; + HomeHandle::new(id, &mut **self.handle_pool.get_mut_ref()) } } impl IoFactory for UvIoFactory { - fn id(&self) -> uint { unsafe { cast::transmute(self) } } - // Connect to an address and return a new stream // NB: This blocks the task waiting on the connection. // It would probably be better to return a future diff --git a/src/librustuv/uvll.rs b/src/librustuv/uvll.rs index fa0bb85faed..ad5fad99f20 100644 --- a/src/librustuv/uvll.rs +++ b/src/librustuv/uvll.rs @@ -38,7 +38,7 @@ use std::libc; use std::libc::uintptr_t; pub use self::errors::{EACCES, ECONNREFUSED, ECONNRESET, EPIPE, ECONNABORTED, - ECANCELED, EBADF, ENOTCONN}; + ECANCELED, EBADF, ENOTCONN, ENOENT}; pub static OK: c_int = 0; pub static EOF: c_int = -4095; diff --git a/src/libstd/rt/rtio.rs b/src/libstd/rt/rtio.rs index c1c40cc6dff..97b08cc18ca 100644 --- a/src/libstd/rt/rtio.rs +++ b/src/libstd/rt/rtio.rs @@ -150,8 +150,6 @@ impl<'a> LocalIo<'a> { } pub trait IoFactory { - fn id(&self) -> uint; - // networking fn tcp_connect(&mut self, addr: SocketAddr) -> Result<~RtioTcpStream, IoError>; fn tcp_bind(&mut self, addr: SocketAddr) -> Result<~RtioTcpListener, IoError>; -- cgit 1.4.1-3-g733a5 From 6cad8f4f14da1dd529100779db74b03d6db20faf Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 18 Dec 2013 09:57:58 -0800 Subject: Test fixes and rebase conflicts * vec::raw::to_ptr is gone * Pausible => Pausable * Removing @ * Calling the main task "
" * Removing unused imports * Removing unused mut * Bringing some libextra tests up to date * Allowing compiletest to work at stage0 * Fixing the bootstrap-from-c rmake tests * assert => rtassert in a few cases * printing to stderr instead of stdout in fail!() --- src/compiletest/compiletest.rs | 1 + src/libextra/comm.rs | 11 +- src/libextra/sync.rs | 32 ++-- src/libextra/task_pool.rs | 2 - src/libgreen/basic.rs | 2 +- src/libgreen/lib.rs | 29 ++- src/libgreen/macros.rs | 3 +- src/libgreen/sched.rs | 7 +- src/libnative/io/process.rs | 3 +- src/libnative/lib.rs | 13 +- src/librustc/back/link.rs | 12 +- src/librustpkg/tests.rs | 9 +- src/librustuv/file.rs | 1 - src/librustuv/idle.rs | 2 +- src/librustuv/macros.rs | 3 +- src/librustuv/signal.rs | 2 +- src/librustuv/timer.rs | 2 +- src/librustuv/uvio.rs | 6 +- src/libstd/io/net/unix.rs | 3 +- src/libstd/io/stdio.rs | 3 +- src/libstd/io/test.rs | 1 + src/libstd/rt/local_ptr.rs | 6 +- src/libstd/rt/mod.rs | 1 + src/libstd/rt/rtio.rs | 18 +- src/libstd/rt/task.rs | 8 +- src/libstd/rt/thread.rs | 4 - src/libstd/rt/unwind.rs | 202 ++++++++++++--------- src/libstd/rt/util.rs | 3 +- src/libstd/sync/arc.rs | 2 +- src/libstd/unstable/stack.rs | 2 + src/test/bench/rt-messaging-ping-pong.rs | 6 +- src/test/bench/rt-parfib.rs | 3 +- src/test/bench/shootout-spectralnorm.rs | 2 + src/test/compile-fail/std-uncopyable-atomics.rs | 2 +- .../run-make/bootstrap-from-c-with-green/Makefile | 9 + .../run-make/bootstrap-from-c-with-green/lib.rs | 25 +++ .../run-make/bootstrap-from-c-with-green/main.c | 16 ++ .../run-make/bootstrap-from-c-with-native/Makefile | 9 + .../run-make/bootstrap-from-c-with-native/lib.rs | 24 +++ .../run-make/bootstrap-from-c-with-native/main.c | 16 ++ .../run-make/bootstrap-from-c-with-uvio/Makefile | 9 - .../run-make/bootstrap-from-c-with-uvio/lib.rs | 25 --- .../run-make/bootstrap-from-c-with-uvio/main.c | 16 -- src/test/run-pass/use.rs | 2 +- 44 files changed, 320 insertions(+), 237 deletions(-) create mode 100644 src/test/run-make/bootstrap-from-c-with-green/Makefile create mode 100644 src/test/run-make/bootstrap-from-c-with-green/lib.rs create mode 100644 src/test/run-make/bootstrap-from-c-with-green/main.c create mode 100644 src/test/run-make/bootstrap-from-c-with-native/Makefile create mode 100644 src/test/run-make/bootstrap-from-c-with-native/lib.rs create mode 100644 src/test/run-make/bootstrap-from-c-with-native/main.c delete mode 100644 src/test/run-make/bootstrap-from-c-with-uvio/Makefile delete mode 100644 src/test/run-make/bootstrap-from-c-with-uvio/lib.rs delete mode 100644 src/test/run-make/bootstrap-from-c-with-uvio/main.c (limited to 'src/libnative') diff --git a/src/compiletest/compiletest.rs b/src/compiletest/compiletest.rs index 89b6f06abfc..ae7d1a30a84 100644 --- a/src/compiletest/compiletest.rs +++ b/src/compiletest/compiletest.rs @@ -13,6 +13,7 @@ #[allow(non_camel_case_types)]; #[deny(warnings)]; +#[cfg(stage0)] extern mod green; extern mod extra; use std::os; diff --git a/src/libextra/comm.rs b/src/libextra/comm.rs index c3b17fe9964..52b5bedb7ea 100644 --- a/src/libextra/comm.rs +++ b/src/libextra/comm.rs @@ -96,7 +96,6 @@ pub fn rendezvous() -> (SyncPort, SyncChan) { #[cfg(test)] mod test { use comm::{DuplexStream, rendezvous}; - use std::rt::test::run_in_uv_task; #[test] @@ -124,13 +123,11 @@ mod test { #[test] fn recv_a_lot() { // Rendezvous streams should be able to handle any number of messages being sent - do run_in_uv_task { - let (port, chan) = rendezvous(); - do spawn { - 1000000.times(|| { chan.send(()) }) - } - 1000000.times(|| { port.recv() }) + let (port, chan) = rendezvous(); + do spawn { + 1000000.times(|| { chan.send(()) }) } + 1000000.times(|| { port.recv() }) } #[test] diff --git a/src/libextra/sync.rs b/src/libextra/sync.rs index 2a53775a907..f43329076c8 100644 --- a/src/libextra/sync.rs +++ b/src/libextra/sync.rs @@ -761,23 +761,21 @@ mod tests { fn test_sem_runtime_friendly_blocking() { // Force the runtime to schedule two threads on the same sched_loop. // When one blocks, it should schedule the other one. - do task::spawn_sched(task::SingleThreaded) { - let s = Semaphore::new(1); - let s2 = s.clone(); - let (p, c) = Chan::new(); - let mut child_data = Some((s2, c)); - s.access(|| { - let (s2, c) = child_data.take_unwrap(); - do task::spawn { - c.send(()); - s2.access(|| { }); - c.send(()); - } - let _ = p.recv(); // wait for child to come alive - 5.times(|| { task::deschedule(); }); // let the child contend - }); - let _ = p.recv(); // wait for child to be done - } + let s = Semaphore::new(1); + let s2 = s.clone(); + let (p, c) = Chan::new(); + let mut child_data = Some((s2, c)); + s.access(|| { + let (s2, c) = child_data.take_unwrap(); + do task::spawn { + c.send(()); + s2.access(|| { }); + c.send(()); + } + let _ = p.recv(); // wait for child to come alive + 5.times(|| { task::deschedule(); }); // let the child contend + }); + let _ = p.recv(); // wait for child to be done } /************************************************************************ * Mutex tests diff --git a/src/libextra/task_pool.rs b/src/libextra/task_pool.rs index 649a9a06644..ba38f876287 100644 --- a/src/libextra/task_pool.rs +++ b/src/libextra/task_pool.rs @@ -17,8 +17,6 @@ use std::task; use std::vec; -#[cfg(test)] use std::task::SingleThreaded; - enum Msg { Execute(proc(&T)), Quit diff --git a/src/libgreen/basic.rs b/src/libgreen/basic.rs index e1e489a2a2b..0574792c18d 100644 --- a/src/libgreen/basic.rs +++ b/src/libgreen/basic.rs @@ -16,7 +16,7 @@ //! loop if no other one is provided (and M:N scheduling is desired). use std::cast; -use std::rt::rtio::{EventLoop, IoFactory, RemoteCallback, PausibleIdleCallback, +use std::rt::rtio::{EventLoop, IoFactory, RemoteCallback, PausableIdleCallback, Callback}; use std::unstable::sync::Exclusive; use std::util; diff --git a/src/libgreen/lib.rs b/src/libgreen/lib.rs index 7318eaaf679..3a2e8a2b36c 100644 --- a/src/libgreen/lib.rs +++ b/src/libgreen/lib.rs @@ -18,12 +18,7 @@ //! functionality inside of 1:1 programs. #[pkgid = "green#0.9-pre"]; -#[link(name = "green", - package_id = "green", - vers = "0.9-pre", - uuid = "20c38f8c-bfea-83ed-a068-9dc05277be26", - url = "https://github.com/mozilla/rust/tree/master/src/libgreen")]; - +#[crate_id = "green#0.9-pre"]; #[license = "MIT/ASL2"]; #[crate_type = "rlib"]; #[crate_type = "dylib"]; @@ -61,16 +56,13 @@ pub mod stack; pub mod task; #[lang = "start"] +#[cfg(not(test))] pub fn lang_start(main: *u8, argc: int, argv: **u8) -> int { use std::cast; - let mut ret = None; - simple::task().run(|| { - ret = Some(do start(argc, argv) { - let main: extern "Rust" fn() = unsafe { cast::transmute(main) }; - main(); - }) - }); - ret.unwrap() + do start(argc, argv) { + let main: extern "Rust" fn() = unsafe { cast::transmute(main) }; + main(); + } } /// Set up a default runtime configuration, given compiler-supplied arguments. @@ -93,10 +85,14 @@ pub fn lang_start(main: *u8, argc: int, argv: **u8) -> int { /// error. pub fn start(argc: int, argv: **u8, main: proc()) -> int { rt::init(argc, argv); - let exit_code = run(main); + let mut main = Some(main); + let mut ret = None; + simple::task().run(|| { + ret = Some(run(main.take_unwrap())); + }); // unsafe is ok b/c we're sure that the runtime is gone unsafe { rt::cleanup() } - exit_code + ret.unwrap() } /// Execute the main function in a pool of M:N schedulers. @@ -114,6 +110,7 @@ pub fn run(main: proc()) -> int { let (port, chan) = Chan::new(); let mut opts = TaskOpts::new(); opts.notify_chan = Some(chan); + opts.name = Some(SendStrStatic("
")); pool.spawn(opts, main); // Wait for the main task to return, and set the process error code diff --git a/src/libgreen/macros.rs b/src/libgreen/macros.rs index ad0854e2b1e..56dc3204da8 100644 --- a/src/libgreen/macros.rs +++ b/src/libgreen/macros.rs @@ -54,14 +54,13 @@ macro_rules! rtabort ( pub fn dumb_println(args: &fmt::Arguments) { use std::io; use std::libc; - use std::vec; struct Stderr; impl io::Writer for Stderr { fn write(&mut self, data: &[u8]) { unsafe { libc::write(libc::STDERR_FILENO, - vec::raw::to_ptr(data) as *libc::c_void, + data.as_ptr() as *libc::c_void, data.len() as libc::size_t); } } diff --git a/src/libgreen/sched.rs b/src/libgreen/sched.rs index 95c4d8347d5..ef62f654ddf 100644 --- a/src/libgreen/sched.rs +++ b/src/libgreen/sched.rs @@ -11,7 +11,7 @@ use std::cast; use std::rand::{XorShiftRng, Rng, Rand}; use std::rt::local::Local; -use std::rt::rtio::{RemoteCallback, PausibleIdleCallback, Callback, EventLoop}; +use std::rt::rtio::{RemoteCallback, PausableIdleCallback, Callback, EventLoop}; use std::rt::task::BlockedTask; use std::rt::task::Task; use std::sync::deque; @@ -779,6 +779,9 @@ impl Scheduler { /// randomness is a result of performing a round of work stealing (which /// may end up stealing from the current scheduler). pub fn yield_now(mut ~self, cur: ~GreenTask) { + // Async handles trigger the scheduler by calling yield_now on the local + // task, which eventually gets us to here. See comments in SchedRunner + // for more info on this. if cur.is_sched() { assert!(self.sched_task.is_none()); self.run_sched_once(cur); @@ -1345,7 +1348,7 @@ mod test { impl Drop for S { fn drop(&mut self) { - let _foo = @0; + let _foo = ~0; } } diff --git a/src/libnative/io/process.rs b/src/libnative/io/process.rs index 2277d408ee4..64ce9d7e348 100644 --- a/src/libnative/io/process.rs +++ b/src/libnative/io/process.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::cast; use std::io; use std::libc::{pid_t, c_void, c_int}; use std::libc; @@ -17,6 +16,8 @@ use std::ptr; use std::rt::rtio; use p = std::io::process; +#[cfg(windows)] use std::cast; + use super::file; /** diff --git a/src/libnative/lib.rs b/src/libnative/lib.rs index 60ae239ee97..e0666592651 100644 --- a/src/libnative/lib.rs +++ b/src/libnative/lib.rs @@ -15,12 +15,7 @@ //! version of I/O. #[pkgid = "native#0.9-pre"]; -#[link(name = "native", - package_id = "native", - vers = "0.9-pre", - uuid = "535344a7-890f-5a23-e1f3-e0d118805141", - url = "https://github.com/mozilla/rust/tree/master/src/native")]; - +#[crate_id = "native#0.9-pre"]; #[license = "MIT/ASL2"]; #[crate_type = "rlib"]; #[crate_type = "dylib"]; @@ -46,7 +41,7 @@ pub mod task; #[lang = "start"] pub fn lang_start(main: *u8, argc: int, argv: **u8) -> int { use std::cast; - use std::task::try; + use std::task; do start(argc, argv) { // Instead of invoking main directly on this thread, invoke it on @@ -55,7 +50,9 @@ pub fn lang_start(main: *u8, argc: int, argv: **u8) -> int { // of the main thread's stack, so for stack overflow detection to work // we must spawn the task in a subtask which we know the stack size of. let main: extern "Rust" fn() = unsafe { cast::transmute(main) }; - match do try { main() } { + let mut task = task::task(); + task.name("
"); + match do task.try { main() } { Ok(()) => { os::set_exit_status(0); } Err(..) => { os::set_exit_status(rt::DEFAULT_ERROR_CODE); } } diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs index 0cf91fbba0e..214f60291fe 100644 --- a/src/librustc/back/link.rs +++ b/src/librustc/back/link.rs @@ -333,6 +333,10 @@ pub mod write { } unsafe fn configure_llvm(sess: Session) { + use std::unstable::mutex::{MUTEX_INIT, Mutex}; + static mut LOCK: Mutex = MUTEX_INIT; + static mut CONFIGURED: bool = false; + // Copy what clan does by turning on loop vectorization at O2 and // slp vectorization at O3 let vectorize_loop = !sess.no_vectorize_loops() && @@ -360,7 +364,13 @@ pub mod write { add(*arg); } - llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int, llvm_args.as_ptr()); + LOCK.lock(); + if !CONFIGURED { + llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int, + llvm_args.as_ptr()); + CONFIGURED = true; + } + LOCK.unlock(); } unsafe fn populate_llvm_passes(fpm: lib::llvm::PassManagerRef, diff --git a/src/librustpkg/tests.rs b/src/librustpkg/tests.rs index ecf08df5f18..21f18eda140 100644 --- a/src/librustpkg/tests.rs +++ b/src/librustpkg/tests.rs @@ -487,8 +487,9 @@ fn lib_output_file_name(workspace: &Path, short_name: &str) -> Path { } fn output_file_name(workspace: &Path, short_name: ~str) -> Path { - target_build_dir(workspace).join(short_name.as_slice()).join(format!("{}{}", short_name, - os::EXE_SUFFIX)) + target_build_dir(workspace).join(short_name.as_slice()) + .join(format!("{}{}", short_name, + os::consts::EXE_SUFFIX)) } #[cfg(target_os = "linux")] @@ -1353,7 +1354,7 @@ fn test_import_rustpkg() { command_line_test([~"build", ~"foo"], workspace); debug!("workspace = {}", workspace.display()); assert!(target_build_dir(workspace).join("foo").join(format!("pkg{}", - os::EXE_SUFFIX)).exists()); + os::consts::EXE_SUFFIX)).exists()); } #[test] @@ -1366,7 +1367,7 @@ fn test_macro_pkg_script() { command_line_test([~"build", ~"foo"], workspace); debug!("workspace = {}", workspace.display()); assert!(target_build_dir(workspace).join("foo").join(format!("pkg{}", - os::EXE_SUFFIX)).exists()); + os::consts::EXE_SUFFIX)).exists()); } #[test] diff --git a/src/librustuv/file.rs b/src/librustuv/file.rs index 059bf072a1a..82d0fd823a3 100644 --- a/src/librustuv/file.rs +++ b/src/librustuv/file.rs @@ -18,7 +18,6 @@ use std::rt::task::BlockedTask; use std::io::{FileStat, IoError}; use std::io; use std::rt::rtio; -use std::vec; use homing::{HomingIO, HomeHandle}; use super::{Loop, UvError, uv_error_to_io_error, wait_until_woken_after, wakeup}; diff --git a/src/librustuv/idle.rs b/src/librustuv/idle.rs index 44b74d05096..80d21404e4b 100644 --- a/src/librustuv/idle.rs +++ b/src/librustuv/idle.rs @@ -100,7 +100,7 @@ mod test { use std::cast; use std::cell::RefCell; use std::rc::Rc; - use std::rt::rtio::{Callback, PausibleIdleCallback}; + use std::rt::rtio::{Callback, PausableIdleCallback}; use std::rt::task::{BlockedTask, Task}; use std::rt::local::Local; use super::IdleWatcher; diff --git a/src/librustuv/macros.rs b/src/librustuv/macros.rs index 61b4de57655..6c8c16784a1 100644 --- a/src/librustuv/macros.rs +++ b/src/librustuv/macros.rs @@ -30,14 +30,13 @@ macro_rules! uvdebug ( pub fn dumb_println(args: &fmt::Arguments) { use std::io; use std::libc; - use std::vec; struct Stderr; impl io::Writer for Stderr { fn write(&mut self, data: &[u8]) { unsafe { libc::write(libc::STDERR_FILENO, - vec::raw::to_ptr(data) as *libc::c_void, + data.as_ptr() as *libc::c_void, data.len() as libc::size_t); } } diff --git a/src/librustuv/signal.rs b/src/librustuv/signal.rs index 0f81966b169..6772c6d1936 100644 --- a/src/librustuv/signal.rs +++ b/src/librustuv/signal.rs @@ -68,7 +68,7 @@ impl RtioSignal for SignalWatcher {} impl Drop for SignalWatcher { fn drop(&mut self) { let _m = self.fire_homing_missile(); - self.close_async_(); + self.close(); } } diff --git a/src/librustuv/timer.rs b/src/librustuv/timer.rs index e87090753f5..4a0ad44d311 100644 --- a/src/librustuv/timer.rs +++ b/src/librustuv/timer.rs @@ -169,7 +169,7 @@ impl Drop for TimerWatcher { let _action = { let _m = self.fire_homing_missile(); self.stop(); - self.close_async_(); + self.close(); self.action.take() }; } diff --git a/src/librustuv/uvio.rs b/src/librustuv/uvio.rs index 9e7343aa2da..dbf129d0b69 100644 --- a/src/librustuv/uvio.rs +++ b/src/librustuv/uvio.rs @@ -86,10 +86,10 @@ impl rtio::EventLoop for UvEventLoop { IdleWatcher::onetime(&mut self.uvio.loop_, f); } - fn pausible_idle_callback(&mut self, cb: ~rtio::Callback) - -> ~rtio::PausibleIdleCallback + fn pausable_idle_callback(&mut self, cb: ~rtio::Callback) + -> ~rtio::PausableIdleCallback { - IdleWatcher::new(&mut self.uvio.loop_, cb) as ~rtio::PausibleIdleCallback + IdleWatcher::new(&mut self.uvio.loop_, cb) as ~rtio::PausableIdleCallback } fn remote_callback(&mut self, f: ~rtio::Callback) -> ~rtio::RemoteCallback { diff --git a/src/libstd/io/net/unix.rs b/src/libstd/io/net/unix.rs index 59a6903adbf..01b409d4316 100644 --- a/src/libstd/io/net/unix.rs +++ b/src/libstd/io/net/unix.rs @@ -175,7 +175,8 @@ mod tests { fn connect_error() { let mut called = false; io_error::cond.trap(|e| { - assert_eq!(e.kind, OtherIoError); + assert_eq!(e.kind, + if cfg!(windows) {OtherIoError} else {FileNotFound}); called = true; }).inside(|| { let stream = UnixStream::connect(&("path/to/nowhere")); diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 5249d331f72..1e4fa7968dc 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -34,7 +34,6 @@ use libc; use option::{Option, Some, None}; use result::{Ok, Err}; use rt::rtio::{DontClose, IoFactory, LocalIo, RtioFileStream, RtioTTY}; -use vec; // And so begins the tale of acquiring a uv handle to a stdio stream on all // platforms in all situations. Our story begins by splitting the world into two @@ -137,7 +136,7 @@ fn with_task_stdout(f: |&mut Writer|) { fn write(&mut self, data: &[u8]) { unsafe { libc::write(libc::STDOUT_FILENO, - vec::raw::to_ptr(data) as *libc::c_void, + data.as_ptr() as *libc::c_void, data.len() as libc::size_t); } } diff --git a/src/libstd/io/test.rs b/src/libstd/io/test.rs index e273aedf7cc..4be11227965 100644 --- a/src/libstd/io/test.rs +++ b/src/libstd/io/test.rs @@ -31,6 +31,7 @@ macro_rules! iotest ( use io::net::tcp::*; use io::net::ip::*; use io::net::udp::*; + #[cfg(unix)] use io::net::unix::*; use str; use util; diff --git a/src/libstd/rt/local_ptr.rs b/src/libstd/rt/local_ptr.rs index b75f2927003..42cce272e44 100644 --- a/src/libstd/rt/local_ptr.rs +++ b/src/libstd/rt/local_ptr.rs @@ -42,7 +42,7 @@ impl Drop for Borrowed { } let val: ~T = cast::transmute(self.val); put::(val); - assert!(exists()); + rtassert!(exists()); } } } @@ -110,7 +110,7 @@ pub mod compiled { #[inline] pub unsafe fn take() -> ~T { let ptr = RT_TLS_PTR; - assert!(!ptr.is_null()); + rtassert!(!ptr.is_null()); let ptr: ~T = cast::transmute(ptr); // can't use `as`, due to type not matching with `cfg(test)` RT_TLS_PTR = cast::transmute(0); @@ -180,7 +180,7 @@ pub mod native { } pub unsafe fn cleanup() { - assert!(INITIALIZED); + rtassert!(INITIALIZED); tls::destroy(RT_TLS_KEY); LOCK.destroy(); INITIALIZED = false; diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index d0c062c1274..0dd6c883d5b 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -176,6 +176,7 @@ pub fn init(argc: int, argv: **u8) { args::init(argc, argv); env::init(); logging::init(); + local_ptr::init(); } } diff --git a/src/libstd/rt/rtio.rs b/src/libstd/rt/rtio.rs index 97b08cc18ca..6b3d50a76ac 100644 --- a/src/libstd/rt/rtio.rs +++ b/src/libstd/rt/rtio.rs @@ -95,14 +95,16 @@ impl<'a> LocalIo<'a> { /// Returns the local I/O: either the local scheduler's I/O services or /// the native I/O services. pub fn borrow() -> Option { - // XXX: This is currently very unsafely implemented. We don't actually - // *take* the local I/O so there's a very real possibility that we - // can have two borrows at once. Currently there is not a clear way - // to actually borrow the local I/O factory safely because even if - // ownership were transferred down to the functions that the I/O - // factory implements it's just too much of a pain to know when to - // relinquish ownership back into the local task (but that would be - // the safe way of implementing this function). + // FIXME(#11053): bad + // + // This is currently very unsafely implemented. We don't actually + // *take* the local I/O so there's a very real possibility that we + // can have two borrows at once. Currently there is not a clear way + // to actually borrow the local I/O factory safely because even if + // ownership were transferred down to the functions that the I/O + // factory implements it's just too much of a pain to know when to + // relinquish ownership back into the local task (but that would be + // the safe way of implementing this function). // // In order to get around this, we just transmute a copy out of the task // in order to have what is likely a static lifetime (bad). diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs index 765f0b427cd..e6ab159a769 100644 --- a/src/libstd/rt/task.rs +++ b/src/libstd/rt/task.rs @@ -15,9 +15,10 @@ use any::AnyOwnExt; use borrow; +use cast; use cleanup; use io::Writer; -use libc::{c_char, size_t}; +use iter::{Iterator, Take}; use local_data; use ops::Drop; use option::{Option, Some, None}; @@ -488,7 +489,10 @@ mod test { #[test] #[should_fail] - fn test_begin_unwind() { begin_unwind("cause", file!(), line!()) } + fn test_begin_unwind() { + use rt::unwind::begin_unwind; + begin_unwind("cause", file!(), line!()) + } // Task blocking tests diff --git a/src/libstd/rt/thread.rs b/src/libstd/rt/thread.rs index 11189282f68..f4f4aaa2765 100644 --- a/src/libstd/rt/thread.rs +++ b/src/libstd/rt/thread.rs @@ -144,15 +144,11 @@ impl Drop for Thread { #[cfg(windows)] mod imp { - use super::DEFAULT_STACK_SIZE; - use cast; use libc; use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, SIZE_T, BOOL, LPVOID, DWORD, LPDWORD, HANDLE}; use ptr; - use libc; - use cast; pub type rust_thread = HANDLE; pub type rust_thread_return = DWORD; diff --git a/src/libstd/rt/unwind.rs b/src/libstd/rt/unwind.rs index 8248c6274ca..9706dbae4c6 100644 --- a/src/libstd/rt/unwind.rs +++ b/src/libstd/rt/unwind.rs @@ -10,8 +10,9 @@ // Implementation of Rust stack unwinding // -// For background on exception handling and stack unwinding please see "Exception Handling in LLVM" -// (llvm.org/docs/ExceptionHandling.html) and documents linked from it. +// For background on exception handling and stack unwinding please see +// "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and +// documents linked from it. // These are also good reads: // http://theofilos.cs.columbia.edu/blog/2013/09/22/base_abi/ // http://monoinfinito.wordpress.com/series/exception-handling-in-c/ @@ -20,41 +21,55 @@ // ~~~ A brief summary ~~~ // Exception handling happens in two phases: a search phase and a cleanup phase. // -// In both phases the unwinder walks stack frames from top to bottom using information from -// the stack frame unwind sections of the current process's modules ("module" here refers to -// an OS module, i.e. an executable or a dynamic library). +// In both phases the unwinder walks stack frames from top to bottom using +// information from the stack frame unwind sections of the current process's +// modules ("module" here refers to an OS module, i.e. an executable or a +// dynamic library). // -// For each stack frame, it invokes the associated "personality routine", whose address is also -// stored in the unwind info section. +// For each stack frame, it invokes the associated "personality routine", whose +// address is also stored in the unwind info section. // -// In the search phase, the job of a personality routine is to examine exception object being -// thrown, and to decide whether it should be caught at that stack frame. Once the handler frame -// has been identified, cleanup phase begins. +// In the search phase, the job of a personality routine is to examine exception +// object being thrown, and to decide whether it should be caught at that stack +// frame. Once the handler frame has been identified, cleanup phase begins. // -// In the cleanup phase, personality routines invoke cleanup code associated with their -// stack frames (i.e. destructors). Once stack has been unwound down to the handler frame level, -// unwinding stops and the last personality routine transfers control to its' catch block. +// In the cleanup phase, personality routines invoke cleanup code associated +// with their stack frames (i.e. destructors). Once stack has been unwound down +// to the handler frame level, unwinding stops and the last personality routine +// transfers control to its' catch block. // // ~~~ Frame unwind info registration ~~~ -// Each module has its' own frame unwind info section (usually ".eh_frame"), and unwinder needs -// to know about all of them in order for unwinding to be able to cross module boundaries. +// Each module has its' own frame unwind info section (usually ".eh_frame"), and +// unwinder needs to know about all of them in order for unwinding to be able to +// cross module boundaries. // -// On some platforms, like Linux, this is achieved by dynamically enumerating currently loaded -// modules via the dl_iterate_phdr() API and finding all .eh_frame sections. +// On some platforms, like Linux, this is achieved by dynamically enumerating +// currently loaded modules via the dl_iterate_phdr() API and finding all +// .eh_frame sections. // -// Others, like Windows, require modules to actively register their unwind info sections by calling -// __register_frame_info() API at startup. -// In the latter case it is essential that there is only one copy of the unwinder runtime -// in the process. This is usually achieved by linking to the dynamic version of the unwind -// runtime. +// Others, like Windows, require modules to actively register their unwind info +// sections by calling __register_frame_info() API at startup. In the latter +// case it is essential that there is only one copy of the unwinder runtime in +// the process. This is usually achieved by linking to the dynamic version of +// the unwind runtime. // // Currently Rust uses unwind runtime provided by libgcc. -use prelude::*; -use cast::transmute; -use task::TaskResult; +use any::{Any, AnyRefExt}; +use c_str::CString; +use cast; +use kinds::Send; +use libc::{c_char, size_t}; use libc::{c_void, c_int}; -use self::libunwind::*; +use option::{Some, None, Option}; +use result::{Err, Ok}; +use rt::local::Local; +use rt::task::Task; +use str::Str; +use task::TaskResult; +use unstable::intrinsics; + +use uw = self::libunwind; mod libunwind { //! Unwind library interface @@ -109,34 +124,41 @@ mod libunwind { } pub struct Unwinder { - unwinding: bool, - cause: Option<~Any> + priv unwinding: bool, + priv cause: Option<~Any> } impl Unwinder { + pub fn new() -> Unwinder { + Unwinder { + unwinding: false, + cause: None, + } + } + + pub fn unwinding(&self) -> bool { + self.unwinding + } pub fn try(&mut self, f: ||) { use unstable::raw::Closure; unsafe { - let closure: Closure = transmute(f); - let code = transmute(closure.code); - let env = transmute(closure.env); - - let ep = rust_try(try_fn, code, env); + let closure: Closure = cast::transmute(f); + let ep = rust_try(try_fn, closure.code as *c_void, + closure.env as *c_void); if !ep.is_null() { rtdebug!("Caught {}", (*ep).exception_class); - _Unwind_DeleteException(ep); + uw::_Unwind_DeleteException(ep); } } extern fn try_fn(code: *c_void, env: *c_void) { unsafe { - let closure: Closure = Closure { - code: transmute(code), - env: transmute(env), - }; - let closure: || = transmute(closure); + let closure: || = cast::transmute(Closure { + code: code as *(), + env: env as *(), + }); closure(); } } @@ -144,10 +166,11 @@ impl Unwinder { extern { // Rust's try-catch // When f(...) returns normally, the return value is null. - // When f(...) throws, the return value is a pointer to the caught exception object. + // When f(...) throws, the return value is a pointer to the caught + // exception object. fn rust_try(f: extern "C" fn(*c_void, *c_void), code: *c_void, - data: *c_void) -> *_Unwind_Exception; + data: *c_void) -> *uw::_Unwind_Exception; } } @@ -158,21 +181,21 @@ impl Unwinder { self.cause = Some(cause); unsafe { - let exception = ~_Unwind_Exception { + let exception = ~uw::_Unwind_Exception { exception_class: rust_exception_class(), exception_cleanup: exception_cleanup, private_1: 0, private_2: 0 }; - let error = _Unwind_RaiseException(transmute(exception)); + let error = uw::_Unwind_RaiseException(cast::transmute(exception)); rtabort!("Could not unwind stack, error = {}", error as int) } - extern "C" fn exception_cleanup(_unwind_code: _Unwind_Reason_Code, - exception: *_Unwind_Exception) { + extern "C" fn exception_cleanup(_unwind_code: uw::_Unwind_Reason_Code, + exception: *uw::_Unwind_Exception) { rtdebug!("exception_cleanup()"); unsafe { - let _: ~_Unwind_Exception = transmute(exception); + let _: ~uw::_Unwind_Exception = cast::transmute(exception); } } } @@ -188,68 +211,75 @@ impl Unwinder { // Rust's exception class identifier. This is used by personality routines to // determine whether the exception was thrown by their own runtime. -fn rust_exception_class() -> _Unwind_Exception_Class { - let bytes = bytes!("MOZ\0RUST"); // vendor, language - unsafe { - let ptr: *_Unwind_Exception_Class = transmute(bytes.as_ptr()); - *ptr - } +fn rust_exception_class() -> uw::_Unwind_Exception_Class { + // M O Z \0 R U S T -- vendor, language + 0x4d4f5a_00_52555354 } - -// We could implement our personality routine in pure Rust, however exception info decoding -// is tedious. More importantly, personality routines have to handle various platform -// quirks, which are not fun to maintain. For this reason, we attempt to reuse personality -// routine of the C language: __gcc_personality_v0. +// We could implement our personality routine in pure Rust, however exception +// info decoding is tedious. More importantly, personality routines have to +// handle various platform quirks, which are not fun to maintain. For this +// reason, we attempt to reuse personality routine of the C language: +// __gcc_personality_v0. // -// Since C does not support exception catching, __gcc_personality_v0 simply always -// returns _URC_CONTINUE_UNWIND in search phase, and always returns _URC_INSTALL_CONTEXT -// (i.e. "invoke cleanup code") in cleanup phase. +// Since C does not support exception catching, __gcc_personality_v0 simply +// always returns _URC_CONTINUE_UNWIND in search phase, and always returns +// _URC_INSTALL_CONTEXT (i.e. "invoke cleanup code") in cleanup phase. // -// This is pretty close to Rust's exception handling approach, except that Rust does have -// a single "catch-all" handler at the bottom of each task's stack. +// This is pretty close to Rust's exception handling approach, except that Rust +// does have a single "catch-all" handler at the bottom of each task's stack. // So we have two versions: -// - rust_eh_personality, used by all cleanup landing pads, which never catches, so -// the behavior of __gcc_personality_v0 is perfectly adequate there, and -// - rust_eh_personality_catch, used only by rust_try(), which always catches. This is -// achieved by overriding the return value in search phase to always say "catch!". +// - rust_eh_personality, used by all cleanup landing pads, which never catches, +// so the behavior of __gcc_personality_v0 is perfectly adequate there, and +// - rust_eh_personality_catch, used only by rust_try(), which always catches. +// This is achieved by overriding the return value in search phase to always +// say "catch!". extern "C" { fn __gcc_personality_v0(version: c_int, - actions: _Unwind_Action, - exception_class: _Unwind_Exception_Class, - ue_header: *_Unwind_Exception, - context: *_Unwind_Context) -> _Unwind_Reason_Code; + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + ue_header: *uw::_Unwind_Exception, + context: *uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code; } #[lang="eh_personality"] #[no_mangle] // so we can reference it by name from middle/trans/base.rs #[doc(hidden)] #[cfg(not(test))] -pub extern "C" fn rust_eh_personality(version: c_int, - actions: _Unwind_Action, - exception_class: _Unwind_Exception_Class, - ue_header: *_Unwind_Exception, - context: *_Unwind_Context) -> _Unwind_Reason_Code { +pub extern "C" fn rust_eh_personality( + version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + ue_header: *uw::_Unwind_Exception, + context: *uw::_Unwind_Context +) -> uw::_Unwind_Reason_Code +{ unsafe { - __gcc_personality_v0(version, actions, exception_class, ue_header, context) + __gcc_personality_v0(version, actions, exception_class, ue_header, + context) } } #[no_mangle] // referenced from rust_try.ll #[doc(hidden)] #[cfg(not(test))] -pub extern "C" fn rust_eh_personality_catch(version: c_int, - actions: _Unwind_Action, - exception_class: _Unwind_Exception_Class, - ue_header: *_Unwind_Exception, - context: *_Unwind_Context) -> _Unwind_Reason_Code { - if (actions as c_int & _UA_SEARCH_PHASE as c_int) != 0 { // search phase - _URC_HANDLER_FOUND // catch! +pub extern "C" fn rust_eh_personality_catch( + version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + ue_header: *uw::_Unwind_Exception, + context: *uw::_Unwind_Context +) -> uw::_Unwind_Reason_Code +{ + if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase + uw::_URC_HANDLER_FOUND // catch! } else { // cleanup phase unsafe { - __gcc_personality_v0(version, actions, exception_class, ue_header, context) + __gcc_personality_v0(version, actions, exception_class, ue_header, + context) } } } @@ -307,11 +337,11 @@ pub fn begin_unwind(msg: M, file: &'static str, line: uint) -> ! let n = (*task).name.as_ref() .map(|n| n.as_slice()).unwrap_or(""); - println!("task '{}' failed at '{}', {}:{}", n, msg_s, + rterrln!("task '{}' failed at '{}', {}:{}", n, msg_s, file, line); } None => { - println!("failed at '{}', {}:{}", msg_s, file, line); + rterrln!("failed at '{}', {}:{}", msg_s, file, line); intrinsics::abort(); } } diff --git a/src/libstd/rt/util.rs b/src/libstd/rt/util.rs index 69c1da39abc..730a38ce886 100644 --- a/src/libstd/rt/util.rs +++ b/src/libstd/rt/util.rs @@ -69,14 +69,13 @@ pub fn default_sched_threads() -> uint { pub fn dumb_println(args: &fmt::Arguments) { use io; use libc; - use vec; struct Stderr; impl io::Writer for Stderr { fn write(&mut self, data: &[u8]) { unsafe { libc::write(libc::STDERR_FILENO, - vec::raw::to_ptr(data) as *libc::c_void, + data.as_ptr() as *libc::c_void, data.len() as libc::size_t); } } diff --git a/src/libstd/sync/arc.rs b/src/libstd/sync/arc.rs index b405104c09a..7b94a3acc2b 100644 --- a/src/libstd/sync/arc.rs +++ b/src/libstd/sync/arc.rs @@ -32,7 +32,7 @@ use vec; /// An atomically reference counted pointer. /// /// Enforces no shared-memory safety. -//#[unsafe_no_drop_flag] FIXME: #9758 +#[unsafe_no_drop_flag] pub struct UnsafeArc { priv data: *mut ArcData, } diff --git a/src/libstd/unstable/stack.rs b/src/libstd/unstable/stack.rs index b8788b8c55c..d6cd690eaa9 100644 --- a/src/libstd/unstable/stack.rs +++ b/src/libstd/unstable/stack.rs @@ -192,6 +192,7 @@ pub unsafe fn record_sp_limit(limit: uint) { #[cfg(target_arch = "mips")] #[cfg(target_arch = "arm")] #[inline(always)] unsafe fn target_record_sp_limit(limit: uint) { + use libc::c_void; return record_sp_limit(limit as *c_void); extern { fn record_sp_limit(limit: *c_void); @@ -265,6 +266,7 @@ pub unsafe fn get_sp_limit() -> uint { #[cfg(target_arch = "mips")] #[cfg(target_arch = "arm")] #[inline(always)] unsafe fn target_get_sp_limit() -> uint { + use libc::c_void; return get_sp_limit() as uint; extern { fn get_sp_limit() -> *c_void; diff --git a/src/test/bench/rt-messaging-ping-pong.rs b/src/test/bench/rt-messaging-ping-pong.rs index 90d81aa7c3e..6eef71622c5 100644 --- a/src/test/bench/rt-messaging-ping-pong.rs +++ b/src/test/bench/rt-messaging-ping-pong.rs @@ -1,4 +1,3 @@ -// 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. // @@ -12,7 +11,6 @@ extern mod extra; use std::os; use std::uint; -use std::rt::test::spawntask_later; // This is a simple bench that creates M pairs of of tasks. These // tasks ping-pong back and forth over a pair of streams. This is a @@ -28,7 +26,7 @@ fn ping_pong_bench(n: uint, m: uint) { // Create a stream B->A let (pb,cb) = Chan::<()>::new(); - do spawntask_later() || { + do spawn() || { let chan = ca; let port = pb; n.times(|| { @@ -37,7 +35,7 @@ fn ping_pong_bench(n: uint, m: uint) { }) } - do spawntask_later() || { + do spawn() || { let chan = cb; let port = pa; n.times(|| { diff --git a/src/test/bench/rt-parfib.rs b/src/test/bench/rt-parfib.rs index ab607d9aebc..6e3c42f2a4d 100644 --- a/src/test/bench/rt-parfib.rs +++ b/src/test/bench/rt-parfib.rs @@ -12,7 +12,6 @@ extern mod extra; use std::os; use std::uint; -use std::rt::test::spawntask_later; // A simple implementation of parfib. One subtree is found in a new // task and communicated over a oneshot pipe, the other is found @@ -24,7 +23,7 @@ fn parfib(n: uint) -> uint { } let (port,chan) = Chan::new(); - do spawntask_later { + do spawn { chan.send(parfib(n-1)); }; let m2 = parfib(n-2); diff --git a/src/test/bench/shootout-spectralnorm.rs b/src/test/bench/shootout-spectralnorm.rs index 87cd01f9aad..8174347e386 100644 --- a/src/test/bench/shootout-spectralnorm.rs +++ b/src/test/bench/shootout-spectralnorm.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// xfail-test arcs no longer unwrap + extern mod extra; use std::from_str::FromStr; diff --git a/src/test/compile-fail/std-uncopyable-atomics.rs b/src/test/compile-fail/std-uncopyable-atomics.rs index a46dec7830a..57c66974fcd 100644 --- a/src/test/compile-fail/std-uncopyable-atomics.rs +++ b/src/test/compile-fail/std-uncopyable-atomics.rs @@ -12,7 +12,7 @@ #[feature(globs)]; -use std::unstable::atomics::*; +use std::sync::atomics::*; use std::ptr; fn main() { diff --git a/src/test/run-make/bootstrap-from-c-with-green/Makefile b/src/test/run-make/bootstrap-from-c-with-green/Makefile new file mode 100644 index 00000000000..7f466573da7 --- /dev/null +++ b/src/test/run-make/bootstrap-from-c-with-green/Makefile @@ -0,0 +1,9 @@ +-include ../tools.mk + +all: + $(RUSTC) lib.rs -Z gen-crate-map + ln -nsf $(call DYLIB,boot-*) $(call DYLIB,boot) + $(CC) main.c -o $(call RUN,main) -lboot -Wl,-rpath,$(TMPDIR) + $(call RUN,main) + rm $(call DYLIB,boot) + $(call FAIL,main) diff --git a/src/test/run-make/bootstrap-from-c-with-green/lib.rs b/src/test/run-make/bootstrap-from-c-with-green/lib.rs new file mode 100644 index 00000000000..9a03c772f3a --- /dev/null +++ b/src/test/run-make/bootstrap-from-c-with-green/lib.rs @@ -0,0 +1,25 @@ +// 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. + +#[crate_id="boot#0.1"]; +#[crate_type="lib"]; +#[no_uv]; + +extern mod rustuv; +extern mod green; + +#[no_mangle] // this needs to get called from C +pub extern "C" fn foo(argc: int, argv: **u8) -> int { + do green::start(argc, argv) { + do spawn { + println!("hello"); + } + } +} diff --git a/src/test/run-make/bootstrap-from-c-with-green/main.c b/src/test/run-make/bootstrap-from-c-with-green/main.c new file mode 100644 index 00000000000..1872c1ea43b --- /dev/null +++ b/src/test/run-make/bootstrap-from-c-with-green/main.c @@ -0,0 +1,16 @@ +// 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. + +// this is the rust entry point that we're going to call. +int foo(int argc, char *argv[]); + +int main(int argc, char *argv[]) { + return foo(argc, argv); +} diff --git a/src/test/run-make/bootstrap-from-c-with-native/Makefile b/src/test/run-make/bootstrap-from-c-with-native/Makefile new file mode 100644 index 00000000000..7f466573da7 --- /dev/null +++ b/src/test/run-make/bootstrap-from-c-with-native/Makefile @@ -0,0 +1,9 @@ +-include ../tools.mk + +all: + $(RUSTC) lib.rs -Z gen-crate-map + ln -nsf $(call DYLIB,boot-*) $(call DYLIB,boot) + $(CC) main.c -o $(call RUN,main) -lboot -Wl,-rpath,$(TMPDIR) + $(call RUN,main) + rm $(call DYLIB,boot) + $(call FAIL,main) diff --git a/src/test/run-make/bootstrap-from-c-with-native/lib.rs b/src/test/run-make/bootstrap-from-c-with-native/lib.rs new file mode 100644 index 00000000000..d0639d45fa5 --- /dev/null +++ b/src/test/run-make/bootstrap-from-c-with-native/lib.rs @@ -0,0 +1,24 @@ +// 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. + +#[crate_id="boot#0.1"]; +#[crate_type="lib"]; +#[no_uv]; + +extern mod native; + +#[no_mangle] // this needs to get called from C +pub extern "C" fn foo(argc: int, argv: **u8) -> int { + do native::start(argc, argv) { + do spawn { + println!("hello"); + } + } +} diff --git a/src/test/run-make/bootstrap-from-c-with-native/main.c b/src/test/run-make/bootstrap-from-c-with-native/main.c new file mode 100644 index 00000000000..1872c1ea43b --- /dev/null +++ b/src/test/run-make/bootstrap-from-c-with-native/main.c @@ -0,0 +1,16 @@ +// 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. + +// this is the rust entry point that we're going to call. +int foo(int argc, char *argv[]); + +int main(int argc, char *argv[]) { + return foo(argc, argv); +} diff --git a/src/test/run-make/bootstrap-from-c-with-uvio/Makefile b/src/test/run-make/bootstrap-from-c-with-uvio/Makefile deleted file mode 100644 index 7f466573da7..00000000000 --- a/src/test/run-make/bootstrap-from-c-with-uvio/Makefile +++ /dev/null @@ -1,9 +0,0 @@ --include ../tools.mk - -all: - $(RUSTC) lib.rs -Z gen-crate-map - ln -nsf $(call DYLIB,boot-*) $(call DYLIB,boot) - $(CC) main.c -o $(call RUN,main) -lboot -Wl,-rpath,$(TMPDIR) - $(call RUN,main) - rm $(call DYLIB,boot) - $(call FAIL,main) diff --git a/src/test/run-make/bootstrap-from-c-with-uvio/lib.rs b/src/test/run-make/bootstrap-from-c-with-uvio/lib.rs deleted file mode 100644 index 06a06c967f4..00000000000 --- a/src/test/run-make/bootstrap-from-c-with-uvio/lib.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#[crate_id="boot#0.1"]; -#[crate_type="lib"]; - -extern mod rustuv; // pull in uvio - -use std::rt; - -#[no_mangle] // this needs to get called from C -pub extern "C" fn foo(argc: int, argv: **u8) -> int { - do rt::start(argc, argv) { - do spawn { - println!("hello"); - } - } -} diff --git a/src/test/run-make/bootstrap-from-c-with-uvio/main.c b/src/test/run-make/bootstrap-from-c-with-uvio/main.c deleted file mode 100644 index 1872c1ea43b..00000000000 --- a/src/test/run-make/bootstrap-from-c-with-uvio/main.c +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// this is the rust entry point that we're going to call. -int foo(int argc, char *argv[]); - -int main(int argc, char *argv[]) { - return foo(argc, argv); -} diff --git a/src/test/run-pass/use.rs b/src/test/run-pass/use.rs index 56ce5397efb..013487e5803 100644 --- a/src/test/run-pass/use.rs +++ b/src/test/run-pass/use.rs @@ -28,4 +28,4 @@ mod baz { } #[start] -pub fn start(_: int, _: **u8) -> int { 3 } +pub fn start(_: int, _: **u8) -> int { 0 } -- cgit 1.4.1-3-g733a5