diff options
| -rw-r--r-- | src/libcore/core.rc | 8 | ||||
| -rw-r--r-- | src/libcore/priv.rs | 145 | ||||
| -rw-r--r-- | src/rt/rust_builtin.cpp | 6 | ||||
| -rw-r--r-- | src/rt/rustrt.def.in | 1 |
4 files changed, 159 insertions, 1 deletions
diff --git a/src/libcore/core.rc b/src/libcore/core.rc index 4a03247c642..c0d46a9fad5 100644 --- a/src/libcore/core.rc +++ b/src/libcore/core.rc @@ -37,6 +37,11 @@ export extfmt; export tuple; export to_str; +// FIXME: This creates some APIs that I do not want to commit to. It is +// currently exported for the uv code in std, but when that code moves into +// core this should become unexported +export priv; + // Built-in-type support modules mod box; @@ -61,7 +66,8 @@ mod bool; // For internal use by char, not exported mod unicode; - +// Do not export +mod priv; // Ubiquitous-utility-type modules diff --git a/src/libcore/priv.rs b/src/libcore/priv.rs new file mode 100644 index 00000000000..ec432a69ef2 --- /dev/null +++ b/src/libcore/priv.rs @@ -0,0 +1,145 @@ +#[doc(hidden)]; + +export chan_from_global_ptr; + +import compare_and_swap = rustrt::rust_compare_and_swap_ptr; + +native mod rustrt { + fn rust_compare_and_swap_ptr(address: *libc::uintptr_t, + oldval: libc::uintptr_t, + newval: libc::uintptr_t) -> bool; +} + +type global_ptr<T: send> = *libc::uintptr_t; + +#[doc = " +Atomically gets a channel from a pointer to a pointer-sized memory location +or, if no channel exists creates and installs a new channel and sets up a new +task to receive from it. +"] +unsafe fn chan_from_global_ptr<T: send>( + global: global_ptr<T>, + builder: fn() -> task::builder, + f: fn~(comm::port<T>) +) -> comm::chan<T> { + + enum msg { + proceed, + abort + } + + let is_probably_zero = *global == 0u; + if is_probably_zero { + // There's no global channel. We must make it + + let setup_po = comm::port(); + let setup_ch = comm::chan(setup_po); + let setup_ch = task::run_listener(builder()) {|setup_po| + let po = comm::port::<T>(); + let ch = comm::chan(po); + comm::send(setup_ch, ch); + + // Wait to hear if we are the official instance of + // this global task + alt comm::recv::<msg>(setup_po) { + proceed { f(po); } + abort { } + } + }; + + // This is the proposed global channel + let ch = comm::recv(setup_po); + // 0 is our sentinal value. It is not a valid channel + assert unsafe::reinterpret_cast(ch) != 0u; + + // Install the channel + let swapped = compare_and_swap( + global, 0u, unsafe::reinterpret_cast(ch)); + + if swapped { + // Success! + comm::send(setup_ch, proceed); + ch + } else { + // Somebody else got in before we did + comm::send(setup_ch, abort); + unsafe::reinterpret_cast(*global) + } + } else { + unsafe::reinterpret_cast(*global) + } +} + +#[test] +fn test_from_global_chan1() unsafe { + + // This is unreadable, right? + + // The global channel + let globchan = 0u; + let globchanp = ptr::addr_of(globchan); + + // Create the global channel, attached to a new task + let ch = chan_from_global_ptr(globchanp, task::builder) {|po| + let ch = comm::recv(po); + comm::send(ch, true); + let ch = comm::recv(po); + comm::send(ch, true); + }; + // Talk to it + let po = comm::port(); + comm::send(ch, comm::chan(po)); + assert comm::recv(po) == true; + + // This one just reuses the previous channel + let ch = chan_from_global_ptr(globchanp, task::builder) {|po| + let ch = comm::recv(po); + comm::send(ch, false); + }; + + // Talk to the original global task + let po = comm::port(); + comm::send(ch, comm::chan(po)); + assert comm::recv(po) == true; +} + +#[test] +fn test_from_global_chan2() unsafe { + + iter::repeat(100u) {|| + // The global channel + let globchan = 0u; + let globchanp = ptr::addr_of(globchan); + + let resultpo = comm::port(); + let resultch = comm::chan(resultpo); + + // Spawn a bunch of tasks that all want to compete to + // create the global channel + uint::range(0u, 10u) {|i| + task::spawn() {|| + let ch = chan_from_global_ptr( + globchanp, task::builder) {|po| + + uint::range(0u, 10u) {|_j| + let ch = comm::recv(po); + comm::send(ch, {i}); + } + }; + let po = comm::port(); + comm::send(ch, comm::chan(po)); + // We are the winner if our version of the + // task was installed + let winner = comm::recv(po); + comm::send(resultch, winner == i); + } + } + // There should be only one winner + let mut winners = 0u; + uint::range(0u, 10u) {|_i| + let res = comm::recv(resultpo); + if res { winners += 1u }; + } + assert winners == 1u; + } +} diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index 9c2e2c3276c..910331fa7c0 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -776,6 +776,12 @@ rust_osmain_sched_id() { return task->kernel->osmain_sched_id(); } +extern "C" CDECL bool +rust_compare_and_swap_ptr(intptr_t *address, + intptr_t oldval, intptr_t newval) { + return sync::compare_and_swap(address, oldval, newval); +} + // // Local Variables: // mode: C++ diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index e21edfbdd80..ec4f5bedb24 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -136,3 +136,4 @@ 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 |
