core: Serialize all access to the environment using a weak global task

This commit is contained in:
Brian Anderson 2012-04-30 17:28:14 -07:00
parent 30385022c1
commit 46cc11ea88
8 changed files with 150 additions and 71 deletions

View File

@ -199,6 +199,7 @@ mod extfmt;
mod unicode;
mod priv;
mod cmath;
mod global_env;
// Local Variables:

124
src/libcore/global_env.rs Normal file
View File

@ -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);
}
}
}
}

View File

@ -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))
};
fn getenv(n: str) -> option<str> {
global_env::getenv(n)
}
#[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);
}
}
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;

View File

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

View File

@ -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++

View File

@ -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

View File

@ -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 <typename T> struct kernel_owned {

View File

@ -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