From 46cc11ea882681a3b524df97d86442c967bf30e2 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 30 Apr 2012 17:28:14 -0700 Subject: [PATCH] core: Serialize all access to the environment using a weak global task --- src/libcore/core.rc | 1 + src/libcore/global_env.rs | 124 ++++++++++++++++++++++++++++++++++++++ src/libcore/os.rs | 56 +---------------- src/libstd/time.rs | 22 +++---- src/rt/rust_builtin.cpp | 6 ++ src/rt/rust_kernel.cpp | 6 +- src/rt/rust_kernel.h | 3 + src/rt/rustrt.def.in | 3 +- 8 files changed, 150 insertions(+), 71 deletions(-) create mode 100644 src/libcore/global_env.rs diff --git a/src/libcore/core.rc b/src/libcore/core.rc index fa423809404..6b47a69b90a 100644 --- a/src/libcore/core.rc +++ b/src/libcore/core.rc @@ -199,6 +199,7 @@ mod extfmt; mod unicode; mod priv; mod cmath; +mod global_env; // Local Variables: diff --git a/src/libcore/global_env.rs b/src/libcore/global_env.rs new file mode 100644 index 00000000000..5a63ec400be --- /dev/null +++ b/src/libcore/global_env.rs @@ -0,0 +1,124 @@ +#[doc = "Internal module for serializing access to getenv/setenv"]; + +export getenv; +export setenv; + +native mod rustrt { + fn rust_global_env_chan_ptr() -> *libc::uintptr_t; +} + +enum msg { + msg_getenv(str, comm::chan>), + msg_setenv(str, str, comm::chan<()>) +} + +fn getenv(n: str) -> option { + let env_ch = get_global_env_chan(); + let po = comm::port(); + comm::send(env_ch, msg_getenv(n, comm::chan(po))); + comm::recv(po) +} + +fn setenv(n: str, v: str) { + let env_ch = get_global_env_chan(); + let po = comm::port(); + comm::send(env_ch, msg_setenv(n, v, comm::chan(po))); + comm::recv(po) +} + +fn get_global_env_chan() -> comm::chan { + let global_ptr = rustrt::rust_global_env_chan_ptr(); + let builder_fn = {|| + let builder = task::builder(); + task::unsupervise(builder); + task::set_opts(builder, { + sched: some({ + mode: task::single_threaded, + // FIXME: This would be a good place to use + // a very small native stack + native_stack_size: none + }) + with task::get_opts(builder) + }); + builder + }; + unsafe { + priv::chan_from_global_ptr( + global_ptr, builder_fn, global_env_task) + } +} + +fn global_env_task(msg_po: comm::port) unsafe { + priv::weaken_task {|weak_po| + loop { + alt comm::select2(msg_po, weak_po) { + either::left(msg_getenv(n, resp_ch)) { + comm::send(resp_ch, impl::getenv(n)) + } + either::left(msg_setenv(n, v, resp_ch)) { + comm::send(resp_ch, impl::setenv(n, v)) + } + either::right(_) { + break; + } + } + } + } +} + +mod impl { + + #[cfg(target_os = "linux")] + #[cfg(target_os = "macos")] + #[cfg(target_os = "freebsd")] + fn getenv(n: str) -> option unsafe { + let s = str::as_c_str(n, libc::getenv); + ret if unsafe::reinterpret_cast(s) == 0 { + option::none:: + } else { + let s = unsafe::reinterpret_cast(s); + option::some::(str::unsafe::from_buf(s)) + }; + } + + #[cfg(target_os = "win32")] + fn getenv(n: str) -> option unsafe { + import libc::types::os::arch::extra::*; + import libc::funcs::extra::kernel32::*; + import win32::*; + as_utf16_p(n) {|u| + fill_utf16_buf_and_decode() {|buf, sz| + GetEnvironmentVariableW(u, buf, sz) + } + } + } + + + #[cfg(target_os = "linux")] + #[cfg(target_os = "macos")] + #[cfg(target_os = "freebsd")] + fn setenv(n: str, v: str) { + + // FIXME: remove this when export globs work properly. + import libc::funcs::posix01::unistd::setenv; + str::as_c_str(n) {|nbuf| + str::as_c_str(v) {|vbuf| + setenv(nbuf, vbuf, 1i32); + } + } + } + + + #[cfg(target_os = "win32")] + fn setenv(n: str, v: str) { + // FIXME: remove imports when export globs work properly. + import libc::funcs::extra::kernel32::*; + import win32::*; + as_utf16_p(n) {|nbuf| + as_utf16_p(v) {|vbuf| + SetEnvironmentVariableW(nbuf, vbuf); + } + } + } + +} \ No newline at end of file diff --git a/src/libcore/os.rs b/src/libcore/os.rs index 12b51d43f98..d55c9aebdd3 100644 --- a/src/libcore/os.rs +++ b/src/libcore/os.rs @@ -121,61 +121,14 @@ mod win32 { } } - -#[cfg(target_os = "linux")] -#[cfg(target_os = "macos")] -#[cfg(target_os = "freebsd")] -fn getenv(n: str) -> option unsafe { - let s = as_c_charp(n, libc::getenv); - ret if unsafe::reinterpret_cast(s) == 0 { - option::none:: - } else { - let s = unsafe::reinterpret_cast(s); - option::some::(str::unsafe::from_buf(s)) - }; +fn getenv(n: str) -> option { + global_env::getenv(n) } -#[cfg(target_os = "win32")] -fn getenv(n: str) -> option unsafe { - import libc::types::os::arch::extra::*; - import libc::funcs::extra::kernel32::*; - import win32::*; - as_utf16_p(n) {|u| - fill_utf16_buf_and_decode() {|buf, sz| - GetEnvironmentVariableW(u, buf, sz) - } - } -} - - -#[cfg(target_os = "linux")] -#[cfg(target_os = "macos")] -#[cfg(target_os = "freebsd")] fn setenv(n: str, v: str) { - - // FIXME: remove this when export globs work properly. - import libc::funcs::posix01::unistd::setenv; - as_c_charp(n) {|nbuf| - as_c_charp(v) {|vbuf| - setenv(nbuf, vbuf, 1i32); - } - } + global_env::setenv(n, v) } - -#[cfg(target_os = "win32")] -fn setenv(n: str, v: str) { - // FIXME: remove imports when export globs work properly. - import libc::funcs::extra::kernel32::*; - import win32::*; - as_utf16_p(n) {|nbuf| - as_utf16_p(v) {|vbuf| - SetEnvironmentVariableW(nbuf, vbuf); - } - } -} - - fn fdopen(fd: c_int) -> *FILE { ret as_c_charp("r") {|modebuf| libc::fdopen(fd, modebuf) @@ -726,7 +679,6 @@ mod tests { } #[test] - #[ignore(reason = "fails periodically on mac")] fn test_setenv() { let n = make_rand_name(); setenv(n, "VALUE"); @@ -734,7 +686,6 @@ mod tests { } #[test] - #[ignore(reason = "fails periodically on mac")] fn test_setenv_overwrite() { let n = make_rand_name(); setenv(n, "1"); @@ -747,7 +698,6 @@ mod tests { // Windows GetEnvironmentVariable requires some extra work to make sure // the buffer the variable is copied into is the right size #[test] - #[ignore(reason = "fails periodically on mac")] fn test_getenv_big() { let mut s = ""; let mut i = 0; diff --git a/src/libstd/time.rs b/src/libstd/time.rs index da1edbdf895..cbc18d32bc1 100644 --- a/src/libstd/time.rs +++ b/src/libstd/time.rs @@ -823,21 +823,7 @@ impl tm for tm { mod tests { import task; - // FIXME #2160: These tests are all run in the same task because - // getenv/setenv interacts poorly with threads on OS X #[test] - fn test_all() { - test_get_time(); - test_precise_time(); - test_at_utc(); - test_at(); - test_to_timespec(); - test_conversions(); - test_strptime(); - test_ctime(); - test_strftime(); - } - fn test_get_time() { const some_recent_date: i64 = 1325376000i64; // 2012-01-01T00:00:00Z const some_future_date: i64 = 1577836800i64; // 2020-01-01T00:00:00Z @@ -861,6 +847,7 @@ mod tests { } } + #[test] fn test_precise_time() { let s0 = precise_time_s(); let ns1 = precise_time_ns(); @@ -878,6 +865,7 @@ mod tests { assert ns2 >= ns1; } + #[test] fn test_at_utc() { os::setenv("TZ", "America/Los_Angeles"); @@ -898,6 +886,7 @@ mod tests { assert utc.tm_nsec == 54321_i32; } + #[test] fn test_at() { os::setenv("TZ", "America/Los_Angeles"); @@ -923,6 +912,7 @@ mod tests { assert local.tm_nsec == 54321_i32; } + #[test] fn test_to_timespec() { os::setenv("TZ", "America/Los_Angeles"); @@ -933,6 +923,7 @@ mod tests { assert utc.to_local().to_timespec() == time; } + #[test] fn test_conversions() { os::setenv("TZ", "America/Los_Angeles"); @@ -948,6 +939,7 @@ mod tests { assert utc.to_local().to_utc() == utc; } + #[test] fn test_strptime() { os::setenv("TZ", "America/Los_Angeles"); @@ -1092,6 +1084,7 @@ mod tests { assert test("%", "%%"); } + #[test] fn test_ctime() { os::setenv("TZ", "America/Los_Angeles"); @@ -1103,6 +1096,7 @@ mod tests { assert local.ctime() == "Fri Feb 13 15:31:30 2009"; } + #[test] fn test_strftime() { os::setenv("TZ", "America/Los_Angeles"); diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index f2714c4d7c3..5866c1a8dc0 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -781,6 +781,12 @@ rust_task_unweaken(rust_port_id chan) { task->kernel->unweaken_task(chan); } +extern "C" CDECL uintptr_t* +rust_global_env_chan_ptr() { + rust_task *task = rust_get_current_task(); + return task->kernel->get_global_env_chan(); +} + // // Local Variables: // mode: C++ diff --git a/src/rt/rust_kernel.cpp b/src/rt/rust_kernel.cpp index e041256eaaa..44c6be2421d 100644 --- a/src/rt/rust_kernel.cpp +++ b/src/rt/rust_kernel.cpp @@ -22,11 +22,11 @@ rust_kernel::rust_kernel(rust_env *env) : sched_reaper(this), osmain_driver(NULL), non_weak_tasks(0), + global_loop_chan(0), + global_env_chan(0), env(env) + { - // set up storage of pointers needed to - // access the global loop. - global_loop_chan = 0; // Create the single threaded scheduler that will run on the platform's // main thread diff --git a/src/rt/rust_kernel.h b/src/rt/rust_kernel.h index 3f313f614a6..97db7cb3778 100644 --- a/src/rt/rust_kernel.h +++ b/src/rt/rust_kernel.h @@ -75,6 +75,8 @@ class rust_kernel { // Used to communicate with the process-side, global libuv loop uintptr_t global_loop_chan; + // Used to serialize access to getenv/setenv + uintptr_t global_env_chan; public: struct rust_env *env; @@ -122,6 +124,7 @@ public: bool send_to_port(rust_port_id chan, void *sptr); uintptr_t* get_global_loop() { return &global_loop_chan; } + uintptr_t* get_global_env_chan() { return &global_env_chan; } }; template struct kernel_owned { diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index 3ad2f98f025..2289f38e724 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -147,4 +147,5 @@ rust_dbg_lock_wait rust_dbg_lock_signal rust_dbg_call rust_osmain_sched_id -rust_compare_and_swap_ptr \ No newline at end of file +rust_compare_and_swap_ptr +rust_global_env_chan_ptr