diff options
| author | bors <bors@rust-lang.org> | 2013-07-22 15:40:36 -0700 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2013-07-22 15:40:36 -0700 |
| commit | 73921f91a326e51118077ff3fd5c5c6196ff7c3a (patch) | |
| tree | 23b03c59e5ea158fc0db7095cf94393c0f4546a1 /src/libstd | |
| parent | 9ed82fbb43804ebc7e06daca5812079630ec8952 (diff) | |
| parent | 407bffb33e59db9c2ed0c0c5a6533f2ab88743e0 (diff) | |
| download | rust-73921f91a326e51118077ff3fd5c5c6196ff7c3a.tar.gz rust-73921f91a326e51118077ff3fd5c5c6196ff7c3a.zip | |
auto merge of #7883 : brson/rust/rm-std-net, r=graydon
This removes all the code from libextra that depends on libuv. After that it removes three runtime features that existed to support the global uv loop: weak tasks, runtime-global variables, and at_exit handlers. The networking code doesn't have many users besides servo, so shouldn't have much fallout. The timer code though is useful and will probably break out-of-tree code until the new scheduler lands, but I expect that to be soon. It also incidentally moves `os::change_dir_locked` to `std::unstable`. This is a function used by test cases to avoid cwd races and in my opinion shouldn't be public (#7870). Closes #7251 and #7870
Diffstat (limited to 'src/libstd')
| -rw-r--r-- | src/libstd/os.rs | 28 | ||||
| -rw-r--r-- | src/libstd/unstable/at_exit.rs | 100 | ||||
| -rw-r--r-- | src/libstd/unstable/global.rs | 281 | ||||
| -rw-r--r-- | src/libstd/unstable/mod.rs | 54 | ||||
| -rw-r--r-- | src/libstd/unstable/weak_task.rs | 211 |
5 files changed, 50 insertions, 624 deletions
diff --git a/src/libstd/os.rs b/src/libstd/os.rs index 4bfd3bbcd3f..5981926fce3 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -863,34 +863,6 @@ pub fn change_dir(p: &Path) -> bool { } } -/// Changes the current working directory to the specified -/// path while acquiring a global lock, then calls `action`. -/// If the change is successful, releases the lock and restores the -/// CWD to what it was before, returning true. -/// Returns false if the directory doesn't exist or if the directory change -/// is otherwise unsuccessful. -pub fn change_dir_locked(p: &Path, action: &fn()) -> bool { - use unstable::global::global_data_clone_create; - use unstable::sync::{Exclusive, exclusive}; - - fn key(_: Exclusive<()>) { } - - unsafe { - let result = global_data_clone_create(key, || { ~exclusive(()) }); - - do result.with_imm() |_| { - let old_dir = os::getcwd(); - if change_dir(p) { - action(); - change_dir(&old_dir) - } - else { - false - } - } - } -} - /// Copies a file from one location to another pub fn copy_file(from: &Path, to: &Path) -> bool { return do_copy_file(from, to); diff --git a/src/libstd/unstable/at_exit.rs b/src/libstd/unstable/at_exit.rs deleted file mode 100644 index 20ddf941a7b..00000000000 --- a/src/libstd/unstable/at_exit.rs +++ /dev/null @@ -1,100 +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 cast; -use libc::size_t; -use rand::RngUtil; -use rand; -use sys; -use task; -use vec; - -#[cfg(test)] use uint; - -/** -Register a function to be run during runtime shutdown. - -After all non-weak tasks have exited, registered exit functions will -execute, in random order, on the primary scheduler. Each function runs -in its own unsupervised task. -*/ -pub fn at_exit(f: ~fn()) { - unsafe { - let runner: &fn(*ExitFunctions) = exit_runner; - let runner_pair: sys::Closure = cast::transmute(runner); - let runner_ptr = runner_pair.code; - let runner_ptr = cast::transmute(runner_ptr); - rustrt::rust_register_exit_function(runner_ptr, ~f); - } -} - -// NB: The double pointer indirection here is because ~fn() is a fat -// pointer and due to FFI problems I am more comfortable making the -// interface use a normal pointer -mod rustrt { - use libc::c_void; - - extern { - pub fn rust_register_exit_function(runner: *c_void, f: ~~fn()); - } -} - -struct ExitFunctions { - // The number of exit functions - count: size_t, - // The buffer of exit functions - start: *~~fn() -} - -fn exit_runner(exit_fns: *ExitFunctions) { - let exit_fns = unsafe { &*exit_fns }; - let count = (*exit_fns).count; - let start = (*exit_fns).start; - - // NB: from_buf memcpys from the source, which will - // give us ownership of the array of functions - let mut exit_fns_vec = unsafe { vec::from_buf(start, count as uint) }; - // Let's not make any promises about execution order - let mut rng = rand::rng(); - rng.shuffle_mut(exit_fns_vec); - - debug!("running %u exit functions", exit_fns_vec.len()); - - while !exit_fns_vec.is_empty() { - match exit_fns_vec.pop() { - ~f => { - let mut task = task::task(); - task.supervised(); - task.spawn(f); - } - } - } -} - -#[test] -fn test_at_exit() { - let i = 10; - do at_exit { - debug!("at_exit1"); - assert_eq!(i, 10); - } -} - -#[test] -fn test_at_exit_many() { - let i = 10; - for uint::range(20, 100) |j| { - do at_exit { - debug!("at_exit2"); - assert_eq!(i, 10); - assert!(j > i); - } - } -} diff --git a/src/libstd/unstable/global.rs b/src/libstd/unstable/global.rs deleted file mode 100644 index af28879f739..00000000000 --- a/src/libstd/unstable/global.rs +++ /dev/null @@ -1,281 +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. - -/*! -Global data - -An interface for creating and retrieving values with global -(per-runtime) scope. - -Global values are stored in a map and protected by a single global -mutex. Operations are provided for accessing and cloning the value -under the mutex. - -Because all globals go through a single mutex, they should be used -sparingly. The interface is intended to be used with clonable, -atomically reference counted synchronization types, like ARCs, in -which case the value should be cached locally whenever possible to -avoid hitting the mutex. -*/ - -use cast::{transmute}; -use clone::Clone; -use kinds::Send; -use libc::{c_void, intptr_t}; -use option::{Option, Some, None}; -use ops::Drop; -use unstable::sync::{Exclusive, exclusive}; -use unstable::at_exit::at_exit; -use unstable::intrinsics::atomic_cxchg; -use hashmap::HashMap; -use sys::Closure; - -#[cfg(test)] use unstable::sync::{UnsafeAtomicRcBox}; -#[cfg(test)] use task::spawn; -#[cfg(test)] use uint; - -pub type GlobalDataKey<'self,T> = &'self fn(v: T); - -pub unsafe fn global_data_clone_create<T:Send + Clone>( - key: GlobalDataKey<T>, create: &fn() -> ~T) -> T { - /*! - * Clone a global value or, if it has not been created, - * first construct the value then return a clone. - * - * # Safety note - * - * Both the clone operation and the constructor are - * called while the global lock is held. Recursive - * use of the global interface in either of these - * operations will result in deadlock. - */ - global_data_clone_create_(key_ptr(key), create) -} - -unsafe fn global_data_clone_create_<T:Send + Clone>( - key: uint, create: &fn() -> ~T) -> T { - - let mut clone_value: Option<T> = None; - do global_data_modify_(key) |value: Option<~T>| { - match value { - None => { - let value = create(); - clone_value = Some((*value).clone()); - Some(value) - } - Some(value) => { - clone_value = Some((*value).clone()); - Some(value) - } - } - } - return clone_value.unwrap(); -} - -unsafe fn global_data_modify<T:Send>( - key: GlobalDataKey<T>, op: &fn(Option<~T>) -> Option<~T>) { - - global_data_modify_(key_ptr(key), op) -} - -unsafe fn global_data_modify_<T:Send>( - key: uint, op: &fn(Option<~T>) -> Option<~T>) { - - let mut old_dtor = None; - do get_global_state().with |gs| { - let (maybe_new_value, maybe_dtor) = match gs.map.pop(&key) { - Some((ptr, dtor)) => { - let value: ~T = transmute(ptr); - (op(Some(value)), Some(dtor)) - } - None => { - (op(None), None) - } - }; - match maybe_new_value { - Some(value) => { - let data: *c_void = transmute(value); - let dtor: ~fn() = match maybe_dtor { - Some(dtor) => dtor, - None => { - let dtor: ~fn() = || { - let _destroy_value: ~T = transmute(data); - }; - dtor - } - }; - let value = (data, dtor); - gs.map.insert(key, value); - } - None => { - match maybe_dtor { - Some(dtor) => old_dtor = Some(dtor), - None => () - } - } - } - } -} - -pub unsafe fn global_data_clone<T:Send + Clone>( - key: GlobalDataKey<T>) -> Option<T> { - let mut maybe_clone: Option<T> = None; - do global_data_modify(key) |current| { - match ¤t { - &Some(~ref value) => { - maybe_clone = Some(value.clone()); - } - &None => () - } - current - } - return maybe_clone; -} - -// GlobalState is a map from keys to unique pointers and a -// destructor. Keys are pointers derived from the type of the -// global value. There is a single GlobalState instance per runtime. -struct GlobalState { - map: HashMap<uint, (*c_void, ~fn())> -} - -impl Drop for GlobalState { - fn drop(&self) { - for self.map.each_value |v| { - match v { - &(_, ref dtor) => (*dtor)() - } - } - } -} - -fn get_global_state() -> Exclusive<GlobalState> { - - static POISON: int = -1; - - // FIXME #4728: Doing atomic_cxchg to initialize the global state - // lazily, which wouldn't be necessary with a runtime written - // in Rust - let global_ptr = unsafe { rust_get_global_data_ptr() }; - - if unsafe { *global_ptr } == 0 { - // Global state doesn't exist yet, probably - - // The global state object - let state = GlobalState { - map: HashMap::new() - }; - - // It's under a reference-counted mutex - let state = ~exclusive(state); - - // Convert it to an integer - let state_i: int = unsafe { - let state_ptr: &Exclusive<GlobalState> = state; - transmute(state_ptr) - }; - - // Swap our structure into the global pointer - let prev_i = unsafe { atomic_cxchg(&mut *global_ptr, 0, state_i) }; - - // Sanity check that we're not trying to reinitialize after shutdown - assert!(prev_i != POISON); - - if prev_i == 0 { - // Successfully installed the global pointer - - // Take a handle to return - let clone = (*state).clone(); - - // Install a runtime exit function to destroy the global object - do at_exit { - // Poison the global pointer - let prev_i = unsafe { - atomic_cxchg(&mut *global_ptr, state_i, POISON) - }; - assert_eq!(prev_i, state_i); - - // Capture the global state object in the at_exit closure - // so that it is destroyed at the right time - let _capture_global_state = &state; - }; - return clone; - } else { - // Somebody else initialized the globals first - let state: &Exclusive<GlobalState> = unsafe { transmute(prev_i) }; - return state.clone(); - } - } else { - let state: &Exclusive<GlobalState> = unsafe { - transmute(*global_ptr) - }; - return state.clone(); - } -} - -fn key_ptr<T:Send>(key: GlobalDataKey<T>) -> uint { - unsafe { - let closure: Closure = transmute(key); - return transmute(closure.code); - } -} - -extern { - fn rust_get_global_data_ptr() -> *mut intptr_t; -} - -#[test] -fn test_clone_rc() { - fn key(_v: UnsafeAtomicRcBox<int>) { } - - for uint::range(0, 100) |_| { - do spawn { - unsafe { - let val = do global_data_clone_create(key) { - ~UnsafeAtomicRcBox::new(10) - }; - - assert!(val.get() == &10); - } - } - } -} - -#[test] -fn test_modify() { - fn key(_v: UnsafeAtomicRcBox<int>) { } - - unsafe { - do global_data_modify(key) |v| { - match v { - None => { Some(~UnsafeAtomicRcBox::new(10)) } - _ => fail!() - } - } - - do global_data_modify(key) |v| { - match v { - Some(sms) => { - let v = sms.get(); - assert!(*v == 10); - None - }, - _ => fail!() - } - } - - do global_data_modify(key) |v| { - match v { - None => { Some(~UnsafeAtomicRcBox::new(10)) } - _ => fail!() - } - } - } -} diff --git a/src/libstd/unstable/mod.rs b/src/libstd/unstable/mod.rs index 0a46ef619af..d6fd2cbcd1e 100644 --- a/src/libstd/unstable/mod.rs +++ b/src/libstd/unstable/mod.rs @@ -16,13 +16,9 @@ use libc; use prelude::*; use task; -pub mod at_exit; - pub mod dynamic_lib; -pub mod global; pub mod finally; -pub mod weak_task; pub mod intrinsics; pub mod simd; pub mod extfmt; @@ -80,3 +76,53 @@ extern { fn rust_raw_thread_start(f: &(&fn())) -> *raw_thread; fn rust_raw_thread_join_delete(thread: *raw_thread); } + + +/// Changes the current working directory to the specified +/// path while acquiring a global lock, then calls `action`. +/// If the change is successful, releases the lock and restores the +/// CWD to what it was before, returning true. +/// Returns false if the directory doesn't exist or if the directory change +/// is otherwise unsuccessful. +/// +/// This is used by test cases to avoid cwd races. +/// +/// # Safety Note +/// +/// This uses a pthread mutex so descheduling in the action callback +/// can lead to deadlock. Calling change_dir_locked recursively will +/// also deadlock. +pub fn change_dir_locked(p: &Path, action: &fn()) -> bool { + use os; + use os::change_dir; + use task; + use unstable::finally::Finally; + + unsafe { + // This is really sketchy. Using a pthread mutex so descheduling + // in the `action` callback can cause deadlock. Doing it in + // `task::atomically` to try to avoid that, but ... I don't know + // this is all bogus. + return do task::atomically { + rust_take_change_dir_lock(); + + do (||{ + let old_dir = os::getcwd(); + if change_dir(p) { + action(); + change_dir(&old_dir) + } + else { + false + } + }).finally { + rust_drop_change_dir_lock(); + } + } + } + + extern { + fn rust_take_change_dir_lock(); + fn rust_drop_change_dir_lock(); + } +} diff --git a/src/libstd/unstable/weak_task.rs b/src/libstd/unstable/weak_task.rs deleted file mode 100644 index f5dfa1feb9b..00000000000 --- a/src/libstd/unstable/weak_task.rs +++ /dev/null @@ -1,211 +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. - -/*! -Weak tasks - -Weak tasks are a runtime feature for building global services that -do not keep the runtime alive. Normally the runtime exits when all -tasks exits, but if a task is weak then the runtime may exit while -it is running, sending a notification to the task that the runtime -is trying to shut down. -*/ - -use cell::Cell; -use comm::{GenericSmartChan, stream}; -use comm::{Port, Chan, SharedChan, GenericChan, GenericPort}; -use hashmap::HashMap; -use option::{Some, None}; -use unstable::at_exit::at_exit; -use unstable::finally::Finally; -use unstable::global::global_data_clone_create; -use task::rt::{task_id, get_task_id}; -use task::task; - -#[cfg(test)] use task::spawn; - -type ShutdownMsg = (); - -// FIXME #4729: This could be a PortOne but I've experienced bugginess -// with oneshot pipes and try_send -pub unsafe fn weaken_task(f: &fn(Port<ShutdownMsg>)) { - let service = global_data_clone_create(global_data_key, - create_global_service); - let (shutdown_port, shutdown_chan) = stream::<ShutdownMsg>(); - let shutdown_port = Cell::new(shutdown_port); - let task = get_task_id(); - // Expect the weak task service to be alive - assert!(service.try_send(RegisterWeakTask(task, shutdown_chan))); - rust_dec_kernel_live_count(); - do (|| { - f(shutdown_port.take()) - }).finally || { - rust_inc_kernel_live_count(); - // Service my have already exited - service.send(UnregisterWeakTask(task)); - } -} - -type WeakTaskService = SharedChan<ServiceMsg>; -type TaskHandle = task_id; - -fn global_data_key(_v: WeakTaskService) { } - -enum ServiceMsg { - RegisterWeakTask(TaskHandle, Chan<ShutdownMsg>), - UnregisterWeakTask(TaskHandle), - Shutdown -} - -fn create_global_service() -> ~WeakTaskService { - - debug!("creating global weak task service"); - let (port, chan) = stream::<ServiceMsg>(); - let port = Cell::new(port); - let chan = SharedChan::new(chan); - let chan_clone = chan.clone(); - - let mut task = task(); - task.unlinked(); - do task.spawn { - debug!("running global weak task service"); - let port = Cell::new(port.take()); - do (|| { - let port = port.take(); - // The weak task service is itself a weak task - debug!("weakening the weak service task"); - unsafe { rust_dec_kernel_live_count(); } - run_weak_task_service(port); - }).finally { - debug!("unweakening the weak service task"); - unsafe { rust_inc_kernel_live_count(); } - } - } - - do at_exit { - debug!("shutting down weak task service"); - chan.send(Shutdown); - } - - return ~chan_clone; -} - -fn run_weak_task_service(port: Port<ServiceMsg>) { - - let mut shutdown_map = HashMap::new(); - - loop { - match port.recv() { - RegisterWeakTask(task, shutdown_chan) => { - let previously_unregistered = - shutdown_map.insert(task, shutdown_chan); - assert!(previously_unregistered); - } - UnregisterWeakTask(task) => { - match shutdown_map.pop(&task) { - Some(shutdown_chan) => { - // Oneshot pipes must send, even though - // nobody will receive this - shutdown_chan.send(()); - } - None => fail!() - } - } - Shutdown => break - } - } - - for shutdown_map.consume().advance |(_, shutdown_chan)| { - // Weak task may have already exited - shutdown_chan.send(()); - } -} - -extern { - unsafe fn rust_inc_kernel_live_count(); - unsafe fn rust_dec_kernel_live_count(); -} - -#[test] -fn test_simple() { - let (port, chan) = stream(); - do spawn { - unsafe { - do weaken_task |_signal| { - } - } - chan.send(()); - } - port.recv(); -} - -#[test] -fn test_weak_weak() { - let (port, chan) = stream(); - do spawn { - unsafe { - do weaken_task |_signal| { - } - do weaken_task |_signal| { - } - } - chan.send(()); - } - port.recv(); -} - -#[test] -fn test_wait_for_signal() { - do spawn { - unsafe { - do weaken_task |signal| { - signal.recv(); - } - } - } -} - -#[test] -fn test_wait_for_signal_many() { - use uint; - for uint::range(0, 100) |_| { - do spawn { - unsafe { - do weaken_task |signal| { - signal.recv(); - } - } - } - } -} - -#[test] -fn test_select_stream_and_oneshot() { - use comm::select2i; - use either::{Left, Right}; - - let (port, chan) = stream(); - let port = Cell::new(port); - let (waitport, waitchan) = stream(); - do spawn { - unsafe { - do weaken_task |mut signal| { - let mut port = port.take(); - match select2i(&mut port, &mut signal) { - Left(*) => (), - Right(*) => fail!() - } - } - } - waitchan.send(()); - } - chan.send(()); - waitport.recv(); -} |
