Skip to content

Commit

Permalink
core: Serialize all access to the environment using a weak global task
Browse files Browse the repository at this point in the history
  • Loading branch information
brson committed May 1, 2012
1 parent 3038502 commit 46cc11e
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 71 deletions.
1 change: 1 addition & 0 deletions src/libcore/core.rc
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ mod extfmt;
mod unicode;
mod priv;
mod cmath;
mod global_env;


// Local Variables:
Expand Down
124 changes: 124 additions & 0 deletions src/libcore/global_env.rs
Original file line number Diff line number Diff line change
@@ -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<option<str>>),
msg_setenv(str, str, comm::chan<()>)
}

fn getenv(n: str) -> option<str> {
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<msg> {
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<msg>) 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<str> unsafe {
let s = str::as_c_str(n, libc::getenv);
ret if unsafe::reinterpret_cast(s) == 0 {
option::none::<str>
} else {
let s = unsafe::reinterpret_cast(s);
option::some::<str>(str::unsafe::from_buf(s))
};
}

#[cfg(target_os = "win32")]
fn getenv(n: str) -> option<str> 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);
}
}
}

}
56 changes: 3 additions & 53 deletions src/libcore/os.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,61 +121,14 @@ mod win32 {
}
}


#[cfg(target_os = "linux")]
#[cfg(target_os = "macos")]
#[cfg(target_os = "freebsd")]
fn getenv(n: str) -> option<str> unsafe {
let s = as_c_charp(n, libc::getenv);
ret if unsafe::reinterpret_cast(s) == 0 {
option::none::<str>
} else {
let s = unsafe::reinterpret_cast(s);
option::some::<str>(str::unsafe::from_buf(s))
};
}

#[cfg(target_os = "win32")]
fn getenv(n: str) -> option<str> 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);
}
}
fn getenv(n: str) -> option<str> {
global_env::getenv(n)
}


#[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);
}
}
global_env::setenv(n, v)
}


fn fdopen(fd: c_int) -> *FILE {
ret as_c_charp("r") {|modebuf|
libc::fdopen(fd, modebuf)
Expand Down Expand Up @@ -726,15 +679,13 @@ mod tests {
}

#[test]
#[ignore(reason = "fails periodically on mac")]
fn test_setenv() {
let n = make_rand_name();
setenv(n, "VALUE");
assert getenv(n) == option::some("VALUE");
}

#[test]
#[ignore(reason = "fails periodically on mac")]
fn test_setenv_overwrite() {
let n = make_rand_name();
setenv(n, "1");
Expand All @@ -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;
Expand Down
22 changes: 8 additions & 14 deletions src/libstd/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -861,6 +847,7 @@ mod tests {
}
}

#[test]
fn test_precise_time() {
let s0 = precise_time_s();
let ns1 = precise_time_ns();
Expand All @@ -878,6 +865,7 @@ mod tests {
assert ns2 >= ns1;
}

#[test]
fn test_at_utc() {
os::setenv("TZ", "America/Los_Angeles");

Expand All @@ -898,6 +886,7 @@ mod tests {
assert utc.tm_nsec == 54321_i32;
}

#[test]
fn test_at() {
os::setenv("TZ", "America/Los_Angeles");

Expand All @@ -923,6 +912,7 @@ mod tests {
assert local.tm_nsec == 54321_i32;
}

#[test]
fn test_to_timespec() {
os::setenv("TZ", "America/Los_Angeles");

Expand All @@ -933,6 +923,7 @@ mod tests {
assert utc.to_local().to_timespec() == time;
}

#[test]
fn test_conversions() {
os::setenv("TZ", "America/Los_Angeles");

Expand All @@ -948,6 +939,7 @@ mod tests {
assert utc.to_local().to_utc() == utc;
}

#[test]
fn test_strptime() {
os::setenv("TZ", "America/Los_Angeles");

Expand Down Expand Up @@ -1092,6 +1084,7 @@ mod tests {
assert test("%", "%%");
}

#[test]
fn test_ctime() {
os::setenv("TZ", "America/Los_Angeles");

Expand All @@ -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");

Expand Down
6 changes: 6 additions & 0 deletions src/rt/rust_builtin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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++
Expand Down
6 changes: 3 additions & 3 deletions src/rt/rust_kernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/rt/rust_kernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -122,6 +124,7 @@ class rust_kernel {
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 <typename T> struct kernel_owned {
Expand Down
3 changes: 2 additions & 1 deletion src/rt/rustrt.def.in
Original file line number Diff line number Diff line change
Expand Up @@ -147,4 +147,5 @@ rust_dbg_lock_wait
rust_dbg_lock_signal
rust_dbg_call
rust_osmain_sched_id
rust_compare_and_swap_ptr
rust_compare_and_swap_ptr
rust_global_env_chan_ptr

0 comments on commit 46cc11e

Please sign in to comment.