diff options
| author | Patrick Walton <pcwalton@mimiga.net> | 2013-05-21 18:24:42 -0700 |
|---|---|---|
| committer | Patrick Walton <pcwalton@mimiga.net> | 2013-05-22 21:57:11 -0700 |
| commit | 18df18c817b5e109710c58f512a2cc5ad14fa8b2 (patch) | |
| tree | 09cb14a7fa03754cc978d4824a47979acc6d836e /src/libcore/rt | |
| parent | ee52865c8848657e737e3c2071728b062ec9c8de (diff) | |
libstd: Fix merge fallout.
Diffstat (limited to 'src/libcore/rt')
| -rw-r--r-- | src/libcore/rt/comm.rs | 618 | ||||
| -rw-r--r-- | src/libcore/rt/io/mock.rs | 50 | ||||
| -rw-r--r-- | src/libcore/rt/local.rs | 118 | ||||
| -rw-r--r-- | src/libcore/rt/local_ptr.rs | 145 | ||||
| -rw-r--r-- | src/libcore/rt/logging.rs | 68 | ||||
| -rw-r--r-- | src/libcore/rt/message_queue.rs | 53 | ||||
| -rw-r--r-- | src/libcore/rt/rc.rs | 142 | ||||
| -rw-r--r-- | src/libcore/rt/sched.rs | 554 | ||||
| -rw-r--r-- | src/libcore/rt/tube.rs | 185 | ||||
| -rw-r--r-- | src/libcore/rt/uv/idle.rs | 91 | ||||
| -rw-r--r-- | src/libcore/rt/uv/timer.rs | 183 |
11 files changed, 0 insertions, 2207 deletions
diff --git a/src/libcore/rt/comm.rs b/src/libcore/rt/comm.rs deleted file mode 100644 index 576a402b709..00000000000 --- a/src/libcore/rt/comm.rs +++ /dev/null @@ -1,618 +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 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Ports and channels. -//! -//! XXX: Carefully consider whether the sequentially consistent -//! atomics here can be converted to acq/rel. I'm not sure they can, -//! because there is data being transerred in both directions (the payload -//! goes from sender to receiver and the task pointer goes the other way). - -use option::*; -use cast; -use util; -use ops::Drop; -use kinds::Owned; -use rt::sched::{Scheduler, Coroutine}; -use rt::local::Local; -use unstable::intrinsics::{atomic_xchg, atomic_load}; -use util::Void; -use comm::{GenericChan, GenericSmartChan, GenericPort, Peekable}; -use cell::Cell; - -/// A combined refcount / ~Task pointer. -/// -/// Can be equal to the following values: -/// -/// * 2 - both endpoints are alive -/// * 1 - either the sender or the receiver is dead, determined by context -/// * <ptr> - A pointer to a blocked Task that can be transmuted to ~Task -type State = int; - -static STATE_BOTH: State = 2; -static STATE_ONE: State = 1; - -/// The heap-allocated structure shared between two endpoints. -struct Packet<T> { - state: State, - payload: Option<T>, -} - -/// A one-shot channel. -pub struct ChanOne<T> { - // XXX: Hack extra allocation to make by-val self work - inner: ~ChanOneHack<T> -} - - -/// A one-shot port. -pub struct PortOne<T> { - // XXX: Hack extra allocation to make by-val self work - inner: ~PortOneHack<T> -} - -pub struct ChanOneHack<T> { - void_packet: *mut Void, - suppress_finalize: bool -} - -pub struct PortOneHack<T> { - void_packet: *mut Void, - suppress_finalize: bool -} - -pub fn oneshot<T: Owned>() -> (PortOne<T>, ChanOne<T>) { - let packet: ~Packet<T> = ~Packet { - state: STATE_BOTH, - payload: None - }; - - unsafe { - let packet: *mut Void = cast::transmute(packet); - let port = PortOne { - inner: ~PortOneHack { - void_packet: packet, - suppress_finalize: false - } - }; - let chan = ChanOne { - inner: ~ChanOneHack { - void_packet: packet, - suppress_finalize: false - } - }; - return (port, chan); - } -} - -impl<T> ChanOne<T> { - - pub fn send(self, val: T) { - self.try_send(val); - } - - pub fn try_send(self, val: T) -> bool { - let mut this = self; - let mut recvr_active = true; - let packet = this.inner.packet(); - - unsafe { - - // Install the payload - assert!((*packet).payload.is_none()); - (*packet).payload = Some(val); - - // Atomically swap out the old state to figure out what - // the port's up to, issuing a release barrier to prevent - // reordering of the payload write. This also issues an - // acquire barrier that keeps the subsequent access of the - // ~Task pointer from being reordered. - let oldstate = atomic_xchg(&mut (*packet).state, STATE_ONE); - match oldstate { - STATE_BOTH => { - // Port is not waiting yet. Nothing to do - } - STATE_ONE => { - // Port has closed. Need to clean up. - let _packet: ~Packet<T> = cast::transmute(this.inner.void_packet); - recvr_active = false; - } - task_as_state => { - // Port is blocked. Wake it up. - let recvr: ~Coroutine = cast::transmute(task_as_state); - let sched = Local::take::<Scheduler>(); - sched.schedule_task(recvr); - } - } - } - - // Suppress the synchronizing actions in the finalizer. We're done with the packet. - this.inner.suppress_finalize = true; - return recvr_active; - } -} - - -impl<T> PortOne<T> { - pub fn recv(self) -> T { - match self.try_recv() { - Some(val) => val, - None => { - fail!("receiving on closed channel"); - } - } - } - - pub fn try_recv(self) -> Option<T> { - let mut this = self; - let packet = this.inner.packet(); - - // XXX: Optimize this to not require the two context switches when data is available - - // Switch to the scheduler to put the ~Task into the Packet state. - let sched = Local::take::<Scheduler>(); - do sched.deschedule_running_task_and_then |task| { - unsafe { - // Atomically swap the task pointer into the Packet state, issuing - // an acquire barrier to prevent reordering of the subsequent read - // of the payload. Also issues a release barrier to prevent reordering - // of any previous writes to the task structure. - let task_as_state: State = cast::transmute(task); - let oldstate = atomic_xchg(&mut (*packet).state, task_as_state); - match oldstate { - STATE_BOTH => { - // Data has not been sent. Now we're blocked. - } - STATE_ONE => { - // Channel is closed. Switch back and check the data. - let task: ~Coroutine = cast::transmute(task_as_state); - let sched = Local::take::<Scheduler>(); - sched.resume_task_immediately(task); - } - _ => util::unreachable() - } - } - } - - // Task resumes. - - // No further memory barrier is needed here to access the - // payload. Some scenarios: - // - // 1) We encountered STATE_ONE above - the atomic_xchg was the acq barrier. We're fine. - // 2) We encountered STATE_BOTH above and blocked. The sending task then ran us - // and ran on its thread. The sending task issued a read barrier when taking the - // pointer to the receiving task. - // 3) We encountered STATE_BOTH above and blocked, but the receiving task (this task) - // is pinned to some other scheduler, so the sending task had to give us to - // a different scheduler for resuming. That send synchronized memory. - - unsafe { - let payload = util::replace(&mut (*packet).payload, None); - - // The sender has closed up shop. Drop the packet. - let _packet: ~Packet<T> = cast::transmute(this.inner.void_packet); - // Suppress the synchronizing actions in the finalizer. We're done with the packet. - this.inner.suppress_finalize = true; - return payload; - } - } -} - -impl<T> Peekable<T> for PortOne<T> { - fn peek(&self) -> bool { - unsafe { - let packet: *mut Packet<T> = self.inner.packet(); - let oldstate = atomic_load(&mut (*packet).state); - match oldstate { - STATE_BOTH => false, - STATE_ONE => (*packet).payload.is_some(), - _ => util::unreachable() - } - } - } -} - -#[unsafe_destructor] -impl<T> Drop for ChanOneHack<T> { - fn finalize(&self) { - if self.suppress_finalize { return } - - unsafe { - let this = cast::transmute_mut(self); - let oldstate = atomic_xchg(&mut (*this.packet()).state, STATE_ONE); - match oldstate { - STATE_BOTH => { - // Port still active. It will destroy the Packet. - }, - STATE_ONE => { - let _packet: ~Packet<T> = cast::transmute(this.void_packet); - }, - task_as_state => { - // The port is blocked waiting for a message we will never send. Wake it. - assert!((*this.packet()).payload.is_none()); - let recvr: ~Coroutine = cast::transmute(task_as_state); - let sched = Local::take::<Scheduler>(); - sched.schedule_task(recvr); - } - } - } - } -} - -#[unsafe_destructor] -impl<T> Drop for PortOneHack<T> { - fn finalize(&self) { - if self.suppress_finalize { return } - - unsafe { - let this = cast::transmute_mut(self); - let oldstate = atomic_xchg(&mut (*this.packet()).state, STATE_ONE); - match oldstate { - STATE_BOTH => { - // Chan still active. It will destroy the packet. - }, - STATE_ONE => { - let _packet: ~Packet<T> = cast::transmute(this.void_packet); - } - _ => { - util::unreachable() - } - } - } - } -} - -impl<T> ChanOneHack<T> { - fn packet(&self) -> *mut Packet<T> { - unsafe { - let p: *mut ~Packet<T> = cast::transmute(&self.void_packet); - let p: *mut Packet<T> = &mut **p; - return p; - } - } -} - -impl<T> PortOneHack<T> { - fn packet(&self) -> *mut Packet<T> { - unsafe { - let p: *mut ~Packet<T> = cast::transmute(&self.void_packet); - let p: *mut Packet<T> = &mut **p; - return p; - } - } -} - -struct StreamPayload<T> { - val: T, - next: PortOne<StreamPayload<T>> -} - -/// A channel with unbounded size. -pub struct Chan<T> { - // FIXME #5372. Using Cell because we don't take &mut self - next: Cell<ChanOne<StreamPayload<T>>> -} - -/// An port with unbounded size. -pub struct Port<T> { - // FIXME #5372. Using Cell because we don't take &mut self - next: Cell<PortOne<StreamPayload<T>>> -} - -pub fn stream<T: Owned>() -> (Port<T>, Chan<T>) { - let (pone, cone) = oneshot(); - let port = Port { next: Cell(pone) }; - let chan = Chan { next: Cell(cone) }; - return (port, chan); -} - -impl<T: Owned> GenericChan<T> for Chan<T> { - fn send(&self, val: T) { - self.try_send(val); - } -} - -impl<T: Owned> GenericSmartChan<T> for Chan<T> { - fn try_send(&self, val: T) -> bool { - let (next_pone, next_cone) = oneshot(); - let cone = self.next.take(); - self.next.put_back(next_cone); - cone.try_send(StreamPayload { val: val, next: next_pone }) - } -} - -impl<T> GenericPort<T> for Port<T> { - fn recv(&self) -> T { - match self.try_recv() { - Some(val) => val, - None => { - fail!("receiving on closed channel"); - } - } - } - - fn try_recv(&self) -> Option<T> { - let pone = self.next.take(); - match pone.try_recv() { - Some(StreamPayload { val, next }) => { - self.next.put_back(next); - Some(val) - } - None => None - } - } -} - -impl<T> Peekable<T> for Port<T> { - fn peek(&self) -> bool { - self.next.with_mut_ref(|p| p.peek()) - } -} - -#[cfg(test)] -mod test { - use super::*; - use option::*; - use rt::test::*; - use cell::Cell; - use iter::Times; - - #[test] - fn oneshot_single_thread_close_port_first() { - // Simple test of closing without sending - do run_in_newsched_task { - let (port, _chan) = oneshot::<int>(); - { let _p = port; } - } - } - - #[test] - fn oneshot_single_thread_close_chan_first() { - // Simple test of closing without sending - do run_in_newsched_task { - let (_port, chan) = oneshot::<int>(); - { let _c = chan; } - } - } - - #[test] - fn oneshot_single_thread_send_port_close() { - // Testing that the sender cleans up the payload if receiver is closed - do run_in_newsched_task { - let (port, chan) = oneshot::<~int>(); - { let _p = port; } - chan.send(~0); - } - } - - #[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) = oneshot::<~int>(); - { let _c = chan; } - port.recv(); - }; - assert!(res.is_err()); - } - } - - #[test] - fn oneshot_single_thread_send_then_recv() { - do run_in_newsched_task { - let (port, chan) = oneshot::<~int>(); - chan.send(~10); - assert!(port.recv() == ~10); - } - } - - #[test] - fn oneshot_single_thread_try_send_open() { - do run_in_newsched_task { - let (port, chan) = oneshot::<int>(); - assert!(chan.try_send(10)); - assert!(port.recv() == 10); - } - } - - #[test] - fn oneshot_single_thread_try_send_closed() { - do run_in_newsched_task { - let (port, chan) = oneshot::<int>(); - { let _p = port; } - assert!(!chan.try_send(10)); - } - } - - #[test] - fn oneshot_single_thread_try_recv_open() { - do run_in_newsched_task { - let (port, chan) = oneshot::<int>(); - chan.send(10); - assert!(port.try_recv() == Some(10)); - } - } - - #[test] - fn oneshot_single_thread_try_recv_closed() { - do run_in_newsched_task { - let (port, chan) = oneshot::<int>(); - { let _c = chan; } - assert!(port.try_recv() == None); - } - } - - #[test] - fn oneshot_single_thread_peek_data() { - do run_in_newsched_task { - let (port, chan) = oneshot::<int>(); - assert!(!port.peek()); - chan.send(10); - assert!(port.peek()); - } - } - - #[test] - fn oneshot_single_thread_peek_close() { - do run_in_newsched_task { - let (port, chan) = oneshot::<int>(); - { let _c = chan; } - assert!(!port.peek()); - assert!(!port.peek()); - } - } - - #[test] - fn oneshot_single_thread_peek_open() { - do run_in_newsched_task { - let (port, chan) = oneshot::<int>(); - assert!(!port.peek()); - } - } - - #[test] - fn oneshot_multi_task_recv_then_send() { - do run_in_newsched_task { - let (port, chan) = oneshot::<~int>(); - let port_cell = Cell(port); - do spawntask_immediately { - assert!(port_cell.take().recv() == ~10); - } - - chan.send(~10); - } - } - - #[test] - fn oneshot_multi_task_recv_then_close() { - do run_in_newsched_task { - let (port, chan) = oneshot::<~int>(); - let port_cell = Cell(port); - let chan_cell = Cell(chan); - do spawntask_later { - let _cell = chan_cell.take(); - } - let res = do spawntask_try { - assert!(port_cell.take().recv() == ~10); - }; - assert!(res.is_err()); - } - } - - #[test] - fn oneshot_multi_thread_close_stress() { - for stress_factor().times { - do run_in_newsched_task { - let (port, chan) = oneshot::<int>(); - let port_cell = Cell(port); - let _thread = do spawntask_thread { - let _p = port_cell.take(); - }; - let _chan = chan; - } - } - } - - #[test] - fn oneshot_multi_thread_send_close_stress() { - for stress_factor().times { - do run_in_newsched_task { - let (port, chan) = oneshot::<int>(); - let chan_cell = Cell(chan); - let port_cell = Cell(port); - let _thread1 = do spawntask_thread { - let _p = port_cell.take(); - }; - let _thread2 = do spawntask_thread { - let c = chan_cell.take(); - c.send(1); - }; - } - } - } - - #[test] - fn oneshot_multi_thread_recv_close_stress() { - for stress_factor().times { - do run_in_newsched_task { - let (port, chan) = oneshot::<int>(); - let chan_cell = Cell(chan); - let port_cell = Cell(port); - let _thread1 = do spawntask_thread { - let port_cell = Cell(port_cell.take()); - let res = do spawntask_try { - port_cell.take().recv(); - }; - assert!(res.is_err()); - }; - let _thread2 = do spawntask_thread { - let chan_cell = Cell(chan_cell.take()); - do spawntask { - chan_cell.take(); - } - }; - } - } - } - - #[test] - fn oneshot_multi_thread_send_recv_stress() { - for stress_factor().times { - do run_in_newsched_task { - let (port, chan) = oneshot::<~int>(); - let chan_cell = Cell(chan); - let port_cell = Cell(port); - let _thread1 = do spawntask_thread { - chan_cell.take().send(~10); - }; - let _thread2 = do spawntask_thread { - assert!(port_cell.take().recv() == ~10); - }; - } - } - } - - #[test] - fn stream_send_recv_stress() { - for stress_factor().times { - do run_in_newsched_task { - let (port, chan) = stream::<~int>(); - - send(chan, 0); - recv(port, 0); - - fn send(chan: Chan<~int>, i: int) { - if i == 10 { return } - - let chan_cell = Cell(chan); - let _thread = do spawntask_thread { - let chan = chan_cell.take(); - chan.send(~i); - send(chan, i + 1); - }; - } - - fn recv(port: Port<~int>, i: int) { - if i == 10 { return } - - let port_cell = Cell(port); - let _thread = do spawntask_thread { - let port = port_cell.take(); - assert!(port.recv() == ~i); - recv(port, i + 1); - }; - } - } - } - } -} - diff --git a/src/libcore/rt/io/mock.rs b/src/libcore/rt/io/mock.rs deleted file mode 100644 index b580b752bd9..00000000000 --- a/src/libcore/rt/io/mock.rs +++ /dev/null @@ -1,50 +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 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use option::{Option, None}; -use rt::io::{Reader, Writer}; - -pub struct MockReader { - read: ~fn(buf: &mut [u8]) -> Option<uint>, - eof: ~fn() -> bool -} - -impl MockReader { - pub fn new() -> MockReader { - MockReader { - read: |_| None, - eof: || false - } - } -} - -impl Reader for MockReader { - fn read(&mut self, buf: &mut [u8]) -> Option<uint> { (self.read)(buf) } - fn eof(&mut self) -> bool { (self.eof)() } -} - -pub struct MockWriter { - write: ~fn(buf: &[u8]), - flush: ~fn() -} - -impl MockWriter { - pub fn new() -> MockWriter { - MockWriter { - write: |_| (), - flush: || () - } - } -} - -impl Writer for MockWriter { - fn write(&mut self, buf: &[u8]) { (self.write)(buf) } - fn flush(&mut self) { (self.flush)() } -} \ No newline at end of file diff --git a/src/libcore/rt/local.rs b/src/libcore/rt/local.rs deleted file mode 100644 index 64a384ddff0..00000000000 --- a/src/libcore/rt/local.rs +++ /dev/null @@ -1,118 +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 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use option::{Option, Some, None}; -use rt::sched::Scheduler; -use rt::task::Task; -use rt::local_ptr; -use rt::rtio::{EventLoop, IoFactoryObject}; - -pub trait Local { - fn put(value: ~Self); - fn take() -> ~Self; - fn exists() -> bool; - fn borrow(f: &fn(&mut Self)); - unsafe fn unsafe_borrow() -> *mut Self; - unsafe fn try_unsafe_borrow() -> Option<*mut Self>; -} - -impl Local for Scheduler { - fn put(value: ~Scheduler) { unsafe { local_ptr::put(value) }} - fn take() -> ~Scheduler { unsafe { local_ptr::take() } } - fn exists() -> bool { local_ptr::exists() } - fn borrow(f: &fn(&mut Scheduler)) { unsafe { local_ptr::borrow(f) } } - unsafe fn unsafe_borrow() -> *mut Scheduler { local_ptr::unsafe_borrow() } - unsafe fn try_unsafe_borrow() -> Option<*mut Scheduler> { abort!("unimpl") } -} - -impl Local for Task { - fn put(value: ~Task) { abort!("unimpl") } - fn take() -> ~Task { abort!("unimpl") } - fn exists() -> bool { abort!("unimpl") } - fn borrow(f: &fn(&mut Task)) { - do Local::borrow::<Scheduler> |sched| { - match sched.current_task { - Some(~ref mut task) => { - f(&mut *task.task) - } - None => { - abort!("no scheduler") - } - } - } - } - unsafe fn unsafe_borrow() -> *mut Task { - match (*Local::unsafe_borrow::<Scheduler>()).current_task { - Some(~ref mut task) => { - let s: *mut Task = &mut *task.task; - return s; - } - None => { - // Don't fail. Infinite recursion - abort!("no scheduler") - } - } - } - unsafe fn try_unsafe_borrow() -> Option<*mut Task> { - if Local::exists::<Scheduler>() { - Some(Local::unsafe_borrow()) - } else { - None - } - } -} - -// XXX: This formulation won't work once ~IoFactoryObject is a real trait pointer -impl Local for IoFactoryObject { - fn put(value: ~IoFactoryObject) { abort!("unimpl") } - fn take() -> ~IoFactoryObject { abort!("unimpl") } - fn exists() -> bool { abort!("unimpl") } - fn borrow(f: &fn(&mut IoFactoryObject)) { abort!("unimpl") } - unsafe fn unsafe_borrow() -> *mut IoFactoryObject { - let sched = Local::unsafe_borrow::<Scheduler>(); - let io: *mut IoFactoryObject = (*sched).event_loop.io().unwrap(); - return io; - } - unsafe fn try_unsafe_borrow() -> Option<*mut IoFactoryObject> { abort!("unimpl") } -} - -#[cfg(test)] -mod test { - use rt::sched::Scheduler; - use rt::uv::uvio::UvEventLoop; - use super::*; - - #[test] - fn thread_local_scheduler_smoke_test() { - let scheduler = ~UvEventLoop::new_scheduler(); - Local::put(scheduler); - let _scheduler: ~Scheduler = Local::take(); - } - - #[test] - fn thread_local_scheduler_two_instances() { - let scheduler = ~UvEventLoop::new_scheduler(); - Local::put(scheduler); - let _scheduler: ~Scheduler = Local::take(); - let scheduler = ~UvEventLoop::new_scheduler(); - Local::put(scheduler); - let _scheduler: ~Scheduler = Local::take(); - } - - #[test] - fn borrow_smoke_test() { - let scheduler = ~UvEventLoop::new_scheduler(); - Local::put(scheduler); - unsafe { - let _scheduler: *mut Scheduler = Local::unsafe_borrow(); - } - let _scheduler: ~Scheduler = Local::take(); - } -} \ No newline at end of file diff --git a/src/libcore/rt/local_ptr.rs b/src/libcore/rt/local_ptr.rs deleted file mode 100644 index 80d797e8c65..00000000000 --- a/src/libcore/rt/local_ptr.rs +++ /dev/null @@ -1,145 +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 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Access to a single thread-local pointer. -//! -//! The runtime will use this for storing ~Task. -//! -//! XXX: Add runtime checks for usage of inconsistent pointer types. -//! and for overwriting an existing pointer. - -use libc::c_void; -use cast; -use ptr; -use cell::Cell; -use option::{Option, Some, None}; -use unstable::finally::Finally; -use tls = rt::thread_local_storage; - -/// Initialize the TLS key. Other ops will fail if this isn't executed first. -pub fn init_tls_key() { - unsafe { - rust_initialize_rt_tls_key(); - extern { - fn rust_initialize_rt_tls_key(); - } - } -} - -/// Give a pointer to thread-local storage. -/// -/// # Safety note -/// -/// Does not validate the pointer type. -pub unsafe fn put<T>(sched: ~T) { - let key = tls_key(); - let void_ptr: *mut c_void = cast::transmute(sched); - tls::set(key, void_ptr); -} - -/// Take ownership of a pointer from thread-local storage. -/// -/// # Safety note -/// -/// Does not validate the pointer type. -pub unsafe fn take<T>() -> ~T { - let key = tls_key(); - let void_ptr: *mut c_void = tls::get(key); - rtassert!(void_ptr.is_not_null()); - let ptr: ~T = cast::transmute(void_ptr); - tls::set(key, ptr::mut_null()); - return ptr; -} - -/// Check whether there is a thread-local pointer installed. -pub fn exists() -> bool { - unsafe { - match maybe_tls_key() { - Some(key) => tls::get(key).is_not_null(), - None => false - } - } -} - -/// Borrow the thread-local scheduler from thread-local storage. -/// While the scheduler is borrowed it is not available in TLS. -/// -/// # Safety note -/// -/// Does not validate the pointer type. -pub unsafe fn borrow<T>(f: &fn(&mut T)) { - let mut value = take(); - - // XXX: Need a different abstraction from 'finally' here to avoid unsafety - let unsafe_ptr = cast::transmute_mut_region(&mut *value); - let value_cell = Cell(value); - - do (|| { - f(unsafe_ptr); - }).finally { - put(value_cell.take()); - } -} - -/// Borrow a mutable reference to the thread-local Scheduler -/// -/// # Safety Note -/// -/// Because this leaves the Scheduler in thread-local storage it is possible -/// For the Scheduler pointer to be aliased -pub unsafe fn unsafe_borrow<T>() -> *mut T { - let key = tls_key(); - let mut void_sched: *mut c_void = tls::get(key); - rtassert!(void_sched.is_not_null()); - { - let sched: *mut *mut c_void = &mut void_sched; - let sched: *mut ~T = sched as *mut ~T; - let sched: *mut T = &mut **sched; - return sched; - } -} - -fn tls_key() -> tls::Key { - match maybe_tls_key() { - Some(key) => key, - None => abort!("runtime tls key not initialized") - } -} - -fn maybe_tls_key() -> Option<tls::Key> { - unsafe { - let key: *mut c_void = rust_get_rt_tls_key(); - let key: &mut tls::Key = cast::transmute(key); - let key = *key; - // Check that the key has been initialized. - - // NB: This is a little racy because, while the key is - // initalized under a mutex and it's assumed to be initalized - // in the Scheduler ctor by any thread that needs to use it, - // we are not accessing the key under a mutex. Threads that - // are not using the new Scheduler but still *want to check* - // whether they are running under a new Scheduler may see a 0 - // value here that is in the process of being initialized in - // another thread. I think this is fine since the only action - // they could take if it was initialized would be to check the - // thread-local value and see that it's not set. - if key != -1 { - return Some(key); - } else { - return None; - } - } - - extern { - #[fast_ffi] - fn rust_get_rt_tls_key() -> *mut c_void; - } - -} diff --git a/src/libcore/rt/logging.rs b/src/libcore/rt/logging.rs deleted file mode 100644 index a0d05397689..00000000000 --- a/src/libcore/rt/logging.rs +++ /dev/null @@ -1,68 +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 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use either::*; - -pub trait Logger { - fn log(&mut self, msg: Either<~str, &'static str>); -} - -pub struct StdErrLogger; - -impl Logger for StdErrLogger { - fn log(&mut self, msg: Either<~str, &'static str>) { - use io::{Writer, WriterUtil}; - - let s: &str = match msg { - Left(ref s) => { - let s: &str = *s; - s - } - Right(ref s) => { - let s: &str = *s; - s - } - }; - let dbg = ::libc::STDERR_FILENO as ::io::fd_t; - dbg.write_str(s); - dbg.write_str("\n"); - dbg.flush(); - } -} - -/// Configure logging by traversing the crate map and setting the -/// per-module global logging flags based on the logging spec -pub fn init(crate_map: *u8) { - use os; - use str; - use ptr; - use option::{Some, None}; - use libc::c_char; - - let log_spec = os::getenv("RUST_LOG"); - match log_spec { - Some(spec) => { - do str::as_c_str(spec) |s| { - unsafe { - rust_update_log_settings(crate_map, s); - } - } - } - None => { - unsafe { - rust_update_log_settings(crate_map, ptr::null()); - } - } - } - - extern { - fn rust_update_log_settings(crate_map: *u8, settings: *c_char); - } -} diff --git a/src/libcore/rt/message_queue.rs b/src/libcore/rt/message_queue.rs deleted file mode 100644 index eaab9288ac8..00000000000 --- a/src/libcore/rt/message_queue.rs +++ /dev/null @@ -1,53 +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 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use container::Container; -use kinds::Owned; -use vec::OwnedVector; -use cell::Cell; -use option::*; -use unstable::sync::{Exclusive, exclusive}; -use clone::Clone; - -pub struct MessageQueue<T> { - // XXX: Another mystery bug fixed by boxing this lock - priv queue: ~Exclusive<~[T]> -} - -impl<T: Owned> MessageQueue<T> { - pub fn new() -> MessageQueue<T> { - MessageQueue { - queue: ~exclusive(~[]) - } - } - - pub fn push(&mut self, value: T) { - let value = Cell(value); - self.queue.with(|q| q.push(value.take()) ); - } - - pub fn pop(&mut self) -> Option<T> { - do self.queue.with |q| { - if !q.is_empty() { - Some(q.shift()) - } else { - None - } - } - } -} - -impl<T> Clone for MessageQueue<T> { - fn clone(&self) -> MessageQueue<T> { - MessageQueue { - queue: self.queue.clone() - } - } -} diff --git a/src/libcore/rt/rc.rs b/src/libcore/rt/rc.rs deleted file mode 100644 index 1c0c8c14fdf..00000000000 --- a/src/libcore/rt/rc.rs +++ /dev/null @@ -1,142 +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 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! An owned, task-local, reference counted type -//! -//! # Safety note -//! -//! XXX There is currently no type-system mechanism for enforcing that -//! reference counted types are both allocated on the exchange heap -//! and also non-sendable -//! -//! This doesn't prevent borrowing multiple aliasable mutable pointers - -use ops::Drop; -use clone::Clone; -use libc::c_void; -use cast; - -pub struct RC<T> { - p: *c_void // ~(uint, T) -} - -impl<T> RC<T> { - pub fn new(val: T) -> RC<T> { - unsafe { - let v = ~(1, val); - let p: *c_void = cast::transmute(v); - RC { p: p } - } - } - - fn get_mut_state(&mut self) -> *mut (uint, T) { - unsafe { - let p: &mut ~(uint, T) = cast::transmute(&mut self.p); - let p: *mut (uint, T) = &mut **p; - return p; - } - } - - fn get_state(&self) -> *(uint, T) { - unsafe { - let p: &~(uint, T) = cast::transmute(&self.p); - let p: *(uint, T) = &**p; - return p; - } - } - - pub fn unsafe_borrow_mut(&mut self) -> *mut T { - unsafe { - match *self.get_mut_state() { - (_, ref mut p) => { - let p: *mut T = p; - return p; - } - } - } - } - - pub fn refcount(&self) -> uint { - unsafe { - match *self.get_state() { - (count, _) => count - } - } - } -} - -#[unsafe_destructor] -impl<T> Drop for RC<T> { - fn finalize(&self) { - assert!(self.refcount() > 0); - - unsafe { - // XXX: Mutable finalizer - let this: &mut RC<T> = cast::transmute_mut(self); - - match *this.get_mut_state() { - (ref mut count, _) => { - *count = *count - 1 - } - } - - if this.refcount() == 0 { - let _: ~(uint, T) = cast::transmute(this.p); - } - } - } -} - -impl<T> Clone for RC<T> { - fn clone(&self) -> RC<T> { - unsafe { - // XXX: Mutable clone - let this: &mut RC<T> = cast::transmute_mut(self); - - match *this.get_mut_state() { - (ref mut count, _) => { - *count = *count + 1; - } - } - } - - RC { p: self.p } - } -} - -#[cfg(test)] -mod test { - use super::RC; - - #[test] - fn smoke_test() { - unsafe { - let mut v1 = RC::new(100); - assert!(*v1.unsafe_borrow_mut() == 100); - assert!(v1.refcount() == 1); - - let mut v2 = v1.clone(); - assert!(*v2.unsafe_borrow_mut() == 100); - assert!(v2.refcount() == 2); - - *v2.unsafe_borrow_mut() = 200; - assert!(*v2.unsafe_borrow_mut() == 200); - assert!(*v1.unsafe_borrow_mut() == 200); - - let v3 = v2.clone(); - assert!(v3.refcount() == 3); - { - let _v1 = v1; - let _v2 = v2; - } - assert!(v3.refcount() == 1); - } - } -} diff --git a/src/libcore/rt/sched.rs b/src/libcore/rt/sched.rs deleted file mode 100644 index 50c6a894093..00000000000 --- a/src/libcore/rt/sched.rs +++ /dev/null @@ -1,554 +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 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use option::*; -use sys; -use cast::transmute; -use cell::Cell; - -use super::work_queue::WorkQueue; -use super::stack::{StackPool, StackSegment}; -use super::rtio::{EventLoop, EventLoopObject}; -use super::context::Context; -use super::task::Task; -use rt::local_ptr; -use rt::local::Local; -use rt::rtio::IoFactoryObject; - -/// The Scheduler is responsible for coordinating execution of Coroutines -/// on a single thread. When the scheduler is running it is owned by -/// thread local storage and the running task is owned by the -/// scheduler. -pub struct Scheduler { - priv work_queue: WorkQueue<~Coroutine>, - stack_pool: StackPool, - /// The event loop used to drive the scheduler and perform I/O - event_loop: ~EventLoopObject, - /// The scheduler's saved context. - /// Always valid when a task is executing, otherwise not - priv saved_context: Context, - /// The currently executing task - current_task: Option<~Coroutine>, - /// An action performed after a context switch on behalf of the - /// code running before the context switch - priv cleanup_job: Option<CleanupJob> -} - -// XXX: Some hacks to put a &fn in Scheduler without borrowck -// complaining -type UnsafeTaskReceiver = sys::Closure; -trait ClosureConverter { - fn from_fn(&fn(~Coroutine)) -> Self; - fn to_fn(self) -> &fn(~Coroutine); -} -impl ClosureConverter for UnsafeTaskReceiver { - fn from_fn(f: &fn(~Coroutine)) -> UnsafeTaskReceiver { unsafe { transmute(f) } } - fn to_fn(self) -> &fn(~Coroutine) { unsafe { transmute(self) } } -} - -enum CleanupJob { - DoNothing, - GiveTask(~Coroutine, UnsafeTaskReceiver) -} - -pub impl Scheduler { - - fn in_task_context(&self) -> bool { self.current_task.is_some() } - - fn new(event_loop: ~EventLoopObject) -> Scheduler { - - // Lazily initialize the runtime TLS key - local_ptr::init_tls_key(); - - Scheduler { - event_loop: event_loop, - work_queue: WorkQueue::new(), - stack_pool: StackPool::new(), - saved_context: Context::empty(), - current_task: None, - cleanup_job: None - } - } - - // XXX: This may eventually need to be refactored so that - // the scheduler itself doesn't have to call event_loop.run. - // That will be important for embedding the runtime into external - // event loops. - fn run(~self) -> ~Scheduler { - assert!(!self.in_task_context()); - - let mut self_sched = self; - - unsafe { - let event_loop: *mut ~EventLoopObject = { - let event_loop: *mut ~EventLoopObject = &mut self_sched.event_loop; - event_loop - }; - - // Give ownership of the scheduler (self) to the thread - Local::put(self_sched); - - (*event_loop).run(); - } - - let sched = Local::take::<Scheduler>(); - assert!(sched.work_queue.is_empty()); - return sched; - } - - /// Schedule a task to be executed later. - /// - /// Pushes the task onto the work stealing queue and tells the event loop - /// to run it later. Always use this instead of pushing to the work queue - /// directly. - fn enqueue_task(&mut self, task: ~Coroutine) { - self.work_queue.push(task); - self.event_loop.callback(resume_task_from_queue); - - fn resume_task_from_queue() { - let scheduler = Local::take::<Scheduler>(); - scheduler.resume_task_from_queue(); - } - } - - // * Scheduler-context operations - - fn resume_task_from_queue(~self) { - assert!(!self.in_task_context()); - - rtdebug!("looking in work queue for task to schedule"); - - let mut this = self; - match this.work_queue.pop() { - Some(task) => { - rtdebug!("resuming task from work queue"); - this.resume_task_immediately(task); - } - None => { - rtdebug!("no tasks in queue"); - Local::put(this); - } - } - } - - // * Task-context operations - - /// Called by a running task to end execution, after which it will - /// be recycled by the scheduler for reuse in a new task. - fn terminate_current_task(~self) { - assert!(self.in_task_context()); - - rtdebug!("ending running task"); - - do self.deschedule_running_task_and_then |dead_task| { - let dead_task = Cell(dead_task); - do Local::borrow::<Scheduler> |sched| { - dead_task.take().recycle(&mut sched.stack_pool); - } - } - - abort!("control reached end of task"); - } - - fn schedule_new_task(~self, task: ~Coroutine) { - assert!(self.in_task_context()); - - do self.switch_running_tasks_and_then(task) |last_task| { - let last_task = Cell(last_task); - do Local::borrow::<Scheduler> |sched| { - sched.enqueue_task(last_task.take()); - } - } - } - - fn schedule_task(~self, task: ~Coroutine) { - assert!(self.in_task_context()); - - do self.switch_running_tasks_and_then(task) |last_task| { - let last_task = Cell(last_task); - do Local::borrow::<Scheduler> |sched| { - sched.enqueue_task(last_task.take()); - } - } - } - - // Core scheduling ops - - fn resume_task_immediately(~self, task: ~Coroutine) { - let mut this = self; - assert!(!this.in_task_context()); - - rtdebug!("scheduling a task"); - - // Store the task in the scheduler so it can be grabbed later - this.current_task = Some(task); - this.enqueue_cleanup_job(DoNothing); - - Local::put(this); - - // Take pointers to both the task and scheduler's saved registers. - unsafe { - let sched = Local::unsafe_borrow::<Scheduler>(); - let (sched_context, _, next_task_context) = (*sched).get_contexts(); - let next_task_context = next_task_context.unwrap(); - // Context switch to the task, restoring it's registers - // and saving the scheduler's - Context::swap(sched_context, next_task_context); - - let sched = Local::unsafe_borrow::<Scheduler>(); - // The running task should have passed ownership elsewhere - assert!((*sched).current_task.is_none()); - - // Running tasks may have asked us to do some cleanup - (*sched).run_cleanup_job(); - } - } - - /// Block a running task, context switch to the scheduler, then pass the - /// blocked task to a closure. - /// - /// # Safety note - /// - /// The closure here is a *stack* closure that lives in the - /// running task. It gets transmuted to the scheduler's lifetime - /// and called while the task is blocked. - fn deschedule_running_task_and_then(~self, f: &fn(~Coroutine)) { - let mut this = self; - assert!(this.in_task_context()); - - rtdebug!("blocking task"); - - unsafe { - let blocked_task = this.current_task.swap_unwrap(); - let f_fake_region = transmute::<&fn(~Coroutine), &fn(~Coroutine)>(f); - let f_opaque = ClosureConverter::from_fn(f_fake_region); - this.enqueue_cleanup_job(GiveTask(blocked_task, f_opaque)); - } - - Local::put(this); - - unsafe { - let sched = Local::unsafe_borrow::<Scheduler>(); - let (sched_context, last_task_context, _) = (*sched).get_contexts(); - let last_task_context = last_task_context.unwrap(); - Context::swap(last_task_context, sched_context); - - // We could be executing in a different thread now - let sched = Local::unsafe_borrow::<Scheduler>(); - (*sched).run_cleanup_job(); - } - } - - /// Switch directly to another task, without going through the scheduler. - /// You would want to think hard about doing this, e.g. if there are - /// pending I/O events it would be a bad idea. - fn switch_running_tasks_and_then(~self, next_task: ~Coroutine, f: &fn(~Coroutine)) { - let mut this = self; - assert!(this.in_task_context()); - - rtdebug!("switching tasks"); - - let old_running_task = this.current_task.swap_unwrap(); - let f_fake_region = unsafe { transmute::<&fn(~Coroutine), &fn(~Coroutine)>(f) }; - let f_opaque = ClosureConverter::from_fn(f_fake_region); - this.enqueue_cleanup_job(GiveTask(old_running_task, f_opaque)); - this.current_task = Some(next_task); - - Local::put(this); - - unsafe { - let sched = Local::unsafe_borrow::<Scheduler>(); - let (_, last_task_context, next_task_context) = (*sched).get_contexts(); - let last_task_context = last_task_context.unwrap(); - let next_task_context = next_task_context.unwrap(); - Context::swap(last_task_context, next_task_context); - - // We could be executing in a different thread now - let sched = Local::unsafe_borrow::<Scheduler>(); - (*sched).run_cleanup_job(); - } - } - - - - // * Other stuff - - fn enqueue_cleanup_job(&mut self, job: CleanupJob) { - assert!(self.cleanup_job.is_none()); - self.cleanup_job = Some(job); - } - - fn run_cleanup_job(&mut self) { - rtdebug!("running cleanup job"); - - assert!(self.cleanup_job.is_some()); - - let cleanup_job = self.cleanup_job.swap_unwrap(); - match cleanup_job { - DoNothing => { } - GiveTask(task, f) => (f.to_fn())(task) - } - } - - /// Get mutable references to all the contexts that may be involved in a - /// context switch. - /// - /// Returns (the scheduler context, the optional context of the - /// task in the cleanup list, the optional context of the task in - /// the current task slot). When context switching to a task, - /// callers should first arrange for that task to be located in the - /// Scheduler's current_task slot and set up the - /// post-context-switch cleanup job. - fn get_contexts<'a>(&'a mut self) -> (&'a mut Context, - Option<&'a mut Context>, - Option<&'a mut Context>) { - let last_task = match self.cleanup_job { - Some(GiveTask(~ref task, _)) => { - Some(task) - } - Some(DoNothing) => { - None - } - None => fail!("all context switches should have a cleanup job") - }; - // XXX: Pattern matching mutable pointers above doesn't work - // because borrowck thinks the three patterns are conflicting - // borrows - unsafe { - let last_task = transmute::<Option<&Coroutine>, Option<&mut Coroutine>>(last_task); - let last_task_context = match last_task { - Some(t) => Some(&mut t.saved_context), None => None - }; - let next_task_context = match self.current_task { - Some(ref mut t) => Some(&mut t.saved_context), None => None - }; - // XXX: These transmutes can be removed after snapshot - return (transmute(&mut self.saved_context), - last_task_context, - transmute(next_task_context)); - } - } -} - -static MIN_STACK_SIZE: uint = 10000000; // XXX: Too much stack - -pub struct Coroutine { - /// The segment of stack on which the task is currently running or, - /// if the task is blocked, on which the task will resume execution - priv current_stack_segment: StackSegment, - /// These are always valid when the task is not running, unless - /// the task is dead - priv saved_context: Context, - /// The heap, GC, unwinding, local storage, logging - task: ~Task -} - -pub impl Coroutine { - fn new(stack_pool: &mut StackPool, start: ~fn()) -> Coroutine { - Coroutine::with_task(stack_pool, ~Task::new(), start) - } - - fn with_task(stack_pool: &mut StackPool, - task: ~Task, - start: ~fn()) -> Coroutine { - let start = Coroutine::build_start_wrapper(start); - let mut stack = stack_pool.take_segment(MIN_STACK_SIZE); - // NB: Context holds a pointer to that ~fn - let initial_context = Context::new(start, &mut stack); - return Coroutine { - current_stack_segment: stack, - saved_context: initial_context, - task: task - }; - } - - priv fn build_start_wrapper(start: ~fn()) -> ~fn() { - // XXX: The old code didn't have this extra allocation - let wrapper: ~fn() = || { - // This is the first code to execute after the initial - // context switch to the task. The previous context may - // have asked us to do some cleanup. - unsafe { - let sched = Local::unsafe_borrow::<Scheduler>(); - (*sched).run_cleanup_job(); - - let sched = Local::unsafe_borrow::<Scheduler>(); - let task = (*sched).current_task.get_mut_ref(); - // FIXME #6141: shouldn't neet to put `start()` in another closure - task.task.run(||start()); - } - - let sched = Local::take::<Scheduler>(); - sched.terminate_current_task(); - }; - return wrapper; - } - - /// Destroy the task and try to reuse its components - fn recycle(~self, stack_pool: &mut StackPool) { - match self { - ~Coroutine {current_stack_segment, _} => { - stack_pool.give_segment(current_stack_segment); - } - } - } -} - -#[cfg(test)] -mod test { - use int; - use cell::Cell; - use rt::uv::uvio::UvEventLoop; - use unstable::run_in_bare_thread; - use task::spawn; - use rt::local::Local; - use rt::test::*; - use super::*; - - #[test] - fn test_simple_scheduling() { - do run_in_bare_thread { - let mut task_ran = false; - let task_ran_ptr: *mut bool = &mut task_ran; - - let mut sched = ~UvEventLoop::new_scheduler(); - let task = ~do Coroutine::new(&mut sched.stack_pool) { - unsafe { *task_ran_ptr = true; } - }; - sched.enqueue_task(task); - sched.run(); - assert!(task_ran); - } - } - - #[test] - fn test_several_tasks() { - do run_in_bare_thread { - let total = 10; - let mut task_count = 0; - let task_count_ptr: *mut int = &mut task_count; - - let mut sched = ~UvEventLoop::new_scheduler(); - for int::range(0, total) |_| { - let task = ~do Coroutine::new(&mut sched.stack_pool) { - unsafe { *task_count_ptr = *task_count_ptr + 1; } - }; - sched.enqueue_task(task); - } - sched.run(); - assert_eq!(task_count, total); - } - } - - #[test] - fn test_swap_tasks_then() { - do run_in_bare_thread { - let mut count = 0; - let count_ptr: *mut int = &mut count; - - let mut sched = ~UvEventLoop::new_scheduler(); - let task1 = ~do Coroutine::new(&mut sched.stack_pool) { - unsafe { *count_ptr = *count_ptr + 1; } - let mut sched = Local::take::<Scheduler>(); - let task2 = ~do Coroutine::new(&mut sched.stack_pool) { - unsafe { *count_ptr = *count_ptr + 1; } - }; - // Context switch directly to the new task - do sched.switch_running_tasks_and_then(task2) |task1| { - let task1 = Cell(task1); - do Local::borrow::<Scheduler> |sched| { - sched.enqueue_task(task1.take()); - } - } - unsafe { *count_ptr = *count_ptr + 1; } - }; - sched.enqueue_task(task1); - sched.run(); - assert_eq!(count, 3); - } - } - - #[bench] #[test] #[ignore(reason = "long test")] - fn test_run_a_lot_of_tasks_queued() { - do run_in_bare_thread { - static MAX: int = 1000000; - let mut count = 0; - let count_ptr: *mut int = &mut count; - - let mut sched = ~UvEventLoop::new_scheduler(); - - let start_task = ~do Coroutine::new(&mut sched.stack_pool) { - run_task(count_ptr); - }; - sched.enqueue_task(start_task); - sched.run(); - - assert_eq!(count, MAX); - - fn run_task(count_ptr: *mut int) { - do Local::borrow::<Scheduler> |sched| { - let task = ~do Coroutine::new(&mut sched.stack_pool) { - unsafe { - *count_ptr = *count_ptr + 1; - if *count_ptr != MAX { - run_task(count_ptr); - } - } - }; - sched.enqueue_task(task); - } - }; - } - } - - #[test] - fn test_block_task() { - do run_in_bare_thread { - let mut sched = ~UvEventLoop::new_scheduler(); - let task = ~do Coroutine::new(&mut sched.stack_pool) { - let sched = Local::take::<Scheduler>(); - assert!(sched.in_task_context()); - do sched.deschedule_running_task_and_then() |task| { - let task = Cell(task); - do Local::borrow::<Scheduler> |sched| { - assert!(!sched.in_task_context()); - sched.enqueue_task(task.take()); - } - } - }; - sched.enqueue_task(task); - sched.run(); - } - } - - #[test] - fn test_io_callback() { - // 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_newsched_task { - do spawn { - let sched = Local::take::<Scheduler>(); - do sched.deschedule_running_task_and_then |task| { - let mut sched = Local::take::<Scheduler>(); - let task = Cell(task); - do sched.event_loop.callback_ms(10) { - rtdebug!("in callback"); - let mut sched = Local::take::<Scheduler>(); - sched.enqueue_task(task.take()); - Local::put(sched); - } - Local::put(sched); - } - } - } - } -} diff --git a/src/libcore/rt/tube.rs b/src/libcore/rt/tube.rs deleted file mode 100644 index b2f475a6966..00000000000 --- a/src/libcore/rt/tube.rs +++ /dev/null @@ -1,185 +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 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A very simple unsynchronized channel type for sending buffered data from -//! scheduler context to task context. -//! -//! XXX: This would be safer to use if split into two types like Port/Chan - -use option::*; -use clone::Clone; -use super::rc::RC; -use rt::sched::{Scheduler, Coroutine}; -use rt::{context, TaskContext, SchedulerContext}; -use rt::local::Local; -use vec::OwnedVector; -use container::Container; - -struct TubeState<T> { - blocked_task: Option<~Coroutine>, - buf: ~[T] -} - -pub struct Tube<T> { - p: RC<TubeState<T>> -} - -impl<T> Tube<T> { - pub fn new() -> Tube<T> { - Tube { - p: RC::new(TubeState { - blocked_task: None, - buf: ~[] - }) - } - } - - pub fn send(&mut self, val: T) { - rtdebug!("tube send"); - assert!(context() == SchedulerContext); - - unsafe { - let state = self.p.unsafe_borrow_mut(); - (*state).buf.push(val); - - if (*state).blocked_task.is_some() { - // There's a waiting task. Wake it up - rtdebug!("waking blocked tube"); - let task = (*state).blocked_task.swap_unwrap(); - let sched = Local::take::<Scheduler>(); - sched.resume_task_immediately(task); - } - } - } - - pub fn recv(&mut self) -> T { - assert!(context() == TaskContext); - - unsafe { - let state = self.p.unsafe_borrow_mut(); - if !(*state).buf.is_empty() { - return (*state).buf.shift(); - } else { - // Block and wait for the next message - rtdebug!("blocking on tube recv"); - assert!(self.p.refcount() > 1); // There better be somebody to wake us up - assert!((*state).blocked_task.is_none()); - let sched = Local::take::<Scheduler>(); - do sched.deschedule_running_task_and_then |task| { - (*state).blocked_task = Some(task); - } - rtdebug!("waking after tube recv"); - let buf = &mut (*state).buf; - assert!(!buf.is_empty()); - return buf.shift(); - } - } - } -} - -impl<T> Clone for Tube<T> { - fn clone(&self) -> Tube<T> { - Tube { p: self.p.clone() } - } -} - -#[cfg(test)] -mod test { - use int; - use cell::Cell; - use rt::test::*; - use rt::rtio::EventLoop; - use rt::sched::Scheduler; - use rt::local::Local; - use super::*; - - #[test] - fn simple_test() { - do run_in_newsched_task { - let mut tube: Tube<int> = Tube::new(); - let tube_clone = tube.clone(); - let tube_clone_cell = Cell(tube_clone); - let sched = Local::take::<Scheduler>(); - do sched.deschedule_running_task_and_then |task| { - let mut tube_clone = tube_clone_cell.take(); - tube_clone.send(1); - let sched = Local::take::<Scheduler>(); - sched.resume_task_immediately(task); - } - - assert!(tube.recv() == 1); - } - } - - #[test] - fn blocking_test() { - do run_in_newsched_task { - let mut tube: Tube<int> = Tube::new(); - let tube_clone = tube.clone(); - let tube_clone = Cell(Cell(Cell(tube_clone))); - let sched = Local::take::<Scheduler>(); - do sched.deschedule_running_task_and_then |task| { - let tube_clone = tube_clone.take(); - do Local::borrow::<Scheduler> |sched| { - let tube_clone = tube_clone.take(); - do sched.event_loop.callback { - let mut tube_clone = tube_clone.take(); - // The task should be blocked on this now and - // sending will wake it up. - tube_clone.send(1); - } - } - let sched = Local::take::<Scheduler>(); - sched.resume_task_immediately(task); - } - - assert!(tube.recv() == 1); - } - } - - #[test] - fn many_blocking_test() { - static MAX: int = 100; - - do run_in_newsched_task { - let mut tube: Tube<int> = Tube::new(); - let tube_clone = tube.clone(); - let tube_clone = Cell(tube_clone); - let sched = Local::take::<Scheduler>(); - do sched.deschedule_running_task_and_then |task| { - callback_send(tube_clone.take(), 0); - - fn callback_send(tube: Tube<int>, i: int) { - if i == 100 { return; } - - let tube = Cell(Cell(tube)); - do Local::borrow::<Scheduler> |sched| { - let tube = tube.take(); - do sched.event_loop.callback { - let mut tube = tube.take(); - // The task should be blocked on this now and - // sending will wake it up. - tube.send(i); - callback_send(tube, i + 1); - } - } - } - - let sched = Local::take::<Scheduler>(); - sched.resume_task_immediately(task); - } - - for int::range(0, MAX) |i| { - let j = tube.recv(); - assert!(j == i); - } - } - } -} diff --git a/src/libcore/rt/uv/idle.rs b/src/libcore/rt/uv/idle.rs deleted file mode 100644 index 2cf0b5c4872..00000000000 --- a/src/libcore/rt/uv/idle.rs +++ /dev/null @@ -1,91 +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 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use libc::c_int; -use option::Some; -use rt::uv::uvll; -use rt::uv::{Watcher, Loop, NativeHandle, IdleCallback, NullCallback}; -use rt::uv::status_to_maybe_uv_error; - -pub struct IdleWatcher(*uvll::uv_idle_t); -impl Watcher for IdleWatcher { } - -pub impl IdleWatcher { - fn new(loop_: &mut Loop) -> IdleWatcher { - unsafe { - let handle = uvll::idle_new(); - assert!(handle.is_not_null()); - assert!(0 == uvll::idle_init(loop_.native_handle(), handle)); - let mut watcher: IdleWatcher = NativeHandle::from_native_handle(handle); - watcher.install_watcher_data(); - return watcher - } - } - - fn start(&mut self, cb: IdleCallback) { - { - let data = self.get_watcher_data(); - data.idle_cb = Some(cb); - } - - unsafe { - assert!(0 == uvll::idle_start(self.native_handle(), idle_cb)) - }; - - extern fn idle_cb(handle: *uvll::uv_idle_t, status: c_int) { - let mut idle_watcher: IdleWatcher = NativeHandle::from_native_handle(handle); - let data = idle_watcher.get_watcher_data(); - let cb: &IdleCallback = data.idle_cb.get_ref(); - let status = status_to_maybe_uv_error(handle, status); - (*cb)(idle_watcher, status); - } - } - - fn stop(&mut self) { - // NB: Not resetting the Rust idle_cb to None here because `stop` is likely - // called from *within* the idle callback, causing a use after free - - unsafe { - assert!(0 == uvll::idle_stop(self.native_handle())); - } - } - - fn close(self, cb: NullCallback) { - { - let mut this = self; - let data = this.get_watcher_data(); - assert!(data.close_cb.is_none()); - data.close_cb = Some(cb); - } - - unsafe { uvll::close(self.native_handle(), close_cb) }; - - extern fn close_cb(handle: *uvll::uv_idle_t) { - unsafe { - let mut idle_watcher: IdleWatcher = NativeHandle::from_native_handle(handle); - { - let data = idle_watcher.get_watcher_data(); - data.close_cb.swap_unwrap()(); - } - idle_watcher.drop_watcher_data(); - uvll::idle_delete(handle); - } - } - } -} - -impl NativeHandle<*uvll::uv_idle_t> for IdleWatcher { - fn from_native_handle(handle: *uvll::uv_idle_t) -> IdleWatcher { - IdleWatcher(handle) - } - fn native_handle(&self) -> *uvll::uv_idle_t { - match self { &IdleWatcher(ptr) => ptr } - } -} diff --git a/src/libcore/rt/uv/timer.rs b/src/libcore/rt/uv/timer.rs deleted file mode 100644 index 5557a580987..00000000000 --- a/src/libcore/rt/uv/timer.rs +++ /dev/null @@ -1,183 +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 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use libc::{c_void, c_int}; -use option::Some; -use rt::uv::uvll; -use rt::uv::{Watcher, Loop, NativeHandle, TimerCallback, NullCallback}; -use rt::uv::status_to_maybe_uv_error; - -pub struct TimerWatcher(*uvll::uv_timer_t); -impl Watcher for TimerWatcher { } - -impl TimerWatcher { - pub fn new(loop_: &mut Loop) -> TimerWatcher { - unsafe { - let handle = uvll::malloc_handle(uvll::UV_TIMER); - assert!(handle.is_not_null()); - assert!(0 == uvll::timer_init(loop_.native_handle(), handle)); - let mut watcher: TimerWatcher = NativeHandle::from_native_handle(handle); - watcher.install_watcher_data(); - return watcher; - } - } - - pub fn start(&mut self, timeout: u64, repeat: u64, cb: TimerCallback) { - { - let data = self.get_watcher_data(); - data.timer_cb = Some(cb); - } - - unsafe { - uvll::timer_start(self.native_handle(), timer_cb, timeout, repeat); - } - - extern fn timer_cb(handle: *uvll::uv_timer_t, status: c_int) { - let mut watcher: TimerWatcher = NativeHandle::from_native_handle(handle); - let data = watcher.get_watcher_data(); - let cb = data.timer_cb.get_ref(); - let status = status_to_maybe_uv_error(handle, status); - (*cb)(watcher, status); - } - } - - pub fn stop(&mut self) { - unsafe { - uvll::timer_stop(self.native_handle()); - } - } - - pub fn close(self, cb: NullCallback) { - let mut watcher = self; - { - let data = watcher.get_watcher_data(); - assert!(data.close_cb.is_none()); - data.close_cb = Some(cb); - } - - unsafe { - uvll::close(watcher.native_handle(), close_cb); - } - - extern fn close_cb(handle: *uvll::uv_timer_t) { - let mut watcher: TimerWatcher = NativeHandle::from_native_handle(handle); - { - let data = watcher.get_watcher_data(); - data.close_cb.swap_unwrap()(); - } - watcher.drop_watcher_data(); - unsafe { - uvll::free_handle(handle as *c_void); - } - } - } -} - -impl NativeHandle<*uvll::uv_timer_t> for TimerWatcher { - fn from_native_handle(handle: *uvll::uv_timer_t) -> TimerWatcher { - TimerWatcher(handle) - } - fn native_handle(&self) -> *uvll::uv_idle_t { - match self { &TimerWatcher(ptr) => ptr } - } -} - -#[cfg(test)] -mod test { - use super::*; - use rt::uv::Loop; - use unstable::run_in_bare_thread; - - #[test] - fn smoke_test() { - do run_in_bare_thread { - let mut count = 0; - let count_ptr: *mut int = &mut count; - let mut loop_ = Loop::new(); - let mut timer = TimerWatcher::new(&mut loop_); - do timer.start(10, 0) |timer, status| { - assert!(status.is_none()); - unsafe { *count_ptr += 1 }; - timer.close(||()); - } - loop_.run(); - loop_.close(); - assert!(count == 1); - } - } - - #[test] - fn start_twice() { - do run_in_bare_thread { - let mut count = 0; - let count_ptr: *mut int = &mut count; - let mut loop_ = Loop::new(); - let mut timer = TimerWatcher::new(&mut loop_); - do timer.start(10, 0) |timer, status| { - let mut timer = timer; - assert!(status.is_none()); - unsafe { *count_ptr += 1 }; - do timer.start(10, 0) |timer, status| { - assert!(status.is_none()); - unsafe { *count_ptr += 1 }; - timer.close(||()); - } - } - loop_.run(); - loop_.close(); - assert!(count == 2); - } - } - - #[test] - fn repeat_stop() { - do run_in_bare_thread { - let mut count = 0; - let count_ptr: *mut int = &mut count; - let mut loop_ = Loop::new(); - let mut timer = TimerWatcher::new(&mut loop_); - do timer.start(10, 20) |timer, status| { - assert!(status.is_none()); - unsafe { - *count_ptr += 1; - - if *count_ptr == 10 { - - // Stop the timer and do something else - let mut timer = timer; - timer.stop(); - // Freeze timer so it can be captured - let timer = timer; - - let mut loop_ = timer.event_loop(); - let mut timer2 = TimerWatcher::new(&mut loop_); - do timer2.start(10, 0) |timer2, _| { - - unsafe { *count_ptr += 1; } - - timer2.close(||()); - - // Restart the original timer - let mut timer = timer; - do timer.start(10, 0) |timer, _| { - unsafe { *count_ptr += 1; } - timer.close(||()); - } - } - } - }; - } - loop_.run(); - loop_.close(); - assert!(count == 12); - } - } - -} |
