2012-05-24 01:34:00 +02:00
|
|
|
#[doc = "An atomically reference counted wrapper that can be used to
|
|
|
|
share immutable data between tasks."]
|
2012-05-23 23:53:44 +02:00
|
|
|
|
2012-05-26 00:29:27 +02:00
|
|
|
import comm::{port, chan, methods};
|
2012-06-07 02:58:15 +02:00
|
|
|
import sys::methods;
|
2012-05-26 00:29:27 +02:00
|
|
|
|
|
|
|
export arc, get, clone, shared_arc, get_arc;
|
2012-05-23 23:53:44 +02:00
|
|
|
|
2012-06-07 02:58:15 +02:00
|
|
|
export exclusive, methods;
|
|
|
|
|
2012-05-23 23:53:44 +02:00
|
|
|
#[abi = "cdecl"]
|
|
|
|
native mod rustrt {
|
|
|
|
#[rust_stack]
|
2012-05-24 04:21:50 +02:00
|
|
|
fn rust_atomic_increment(p: &mut libc::intptr_t)
|
2012-05-23 23:53:44 +02:00
|
|
|
-> libc::intptr_t;
|
|
|
|
|
|
|
|
#[rust_stack]
|
2012-05-24 04:21:50 +02:00
|
|
|
fn rust_atomic_decrement(p: &mut libc::intptr_t)
|
2012-05-23 23:53:44 +02:00
|
|
|
-> libc::intptr_t;
|
|
|
|
}
|
|
|
|
|
2012-06-07 02:58:15 +02:00
|
|
|
type arc_data<T> = {
|
2012-05-23 23:53:44 +02:00
|
|
|
mut count: libc::intptr_t,
|
|
|
|
data: T
|
|
|
|
};
|
|
|
|
|
2012-06-22 06:46:43 +02:00
|
|
|
class arc_destruct<T> {
|
|
|
|
let data: *libc::c_void;
|
|
|
|
new(data: *libc::c_void) { self.data = data; }
|
|
|
|
drop unsafe {
|
|
|
|
let data: ~arc_data<T> = unsafe::reinterpret_cast(self.data);
|
|
|
|
let new_count = rustrt::rust_atomic_decrement(&mut data.count);
|
|
|
|
assert new_count >= 0;
|
|
|
|
if new_count == 0 {
|
|
|
|
// drop glue takes over.
|
|
|
|
} else {
|
|
|
|
unsafe::forget(data);
|
|
|
|
}
|
|
|
|
}
|
2012-05-23 23:53:44 +02:00
|
|
|
}
|
|
|
|
|
2012-05-26 00:29:27 +02:00
|
|
|
type arc<T: const> = arc_destruct<T>;
|
2012-05-23 23:53:44 +02:00
|
|
|
|
2012-05-24 01:34:00 +02:00
|
|
|
#[doc="Create an atomically reference counted wrapper."]
|
2012-05-26 00:29:27 +02:00
|
|
|
fn arc<T: const>(-data: T) -> arc<T> {
|
2012-05-23 23:53:44 +02:00
|
|
|
let data = ~{mut count: 1, data: data};
|
|
|
|
unsafe {
|
2012-06-08 09:28:29 +02:00
|
|
|
let ptr = unsafe::transmute(data);
|
2012-05-23 23:53:44 +02:00
|
|
|
arc_destruct(ptr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-24 01:34:00 +02:00
|
|
|
#[doc="Access the underlying data in an atomically reference counted
|
|
|
|
wrapper."]
|
2012-05-26 00:29:27 +02:00
|
|
|
fn get<T: const>(rc: &a.arc<T>) -> &a.T {
|
2012-05-23 23:53:44 +02:00
|
|
|
unsafe {
|
2012-06-22 06:46:43 +02:00
|
|
|
let ptr: ~arc_data<T> = unsafe::reinterpret_cast((*rc).data);
|
2012-05-26 01:22:29 +02:00
|
|
|
// Cast us back into the correct region
|
|
|
|
let r = unsafe::reinterpret_cast(&ptr.data);
|
|
|
|
unsafe::forget(ptr);
|
|
|
|
ret r;
|
2012-05-23 23:53:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-24 01:34:00 +02:00
|
|
|
#[doc="Duplicate an atomically reference counted wrapper.
|
|
|
|
|
|
|
|
The resulting two `arc` objects will point to the same underlying data
|
|
|
|
object. However, one of the `arc` objects can be sent to another task,
|
|
|
|
allowing them to share the underlying data."]
|
2012-05-26 00:29:27 +02:00
|
|
|
fn clone<T: const>(rc: &arc<T>) -> arc<T> {
|
2012-05-23 23:53:44 +02:00
|
|
|
unsafe {
|
2012-06-22 06:46:43 +02:00
|
|
|
let ptr: ~arc_data<T> = unsafe::reinterpret_cast((*rc).data);
|
2012-06-07 21:18:34 +02:00
|
|
|
let new_count = rustrt::rust_atomic_increment(&mut ptr.count);
|
|
|
|
assert new_count >= 2;
|
2012-05-26 01:22:29 +02:00
|
|
|
unsafe::forget(ptr);
|
2012-05-23 23:53:44 +02:00
|
|
|
}
|
2012-06-22 06:46:43 +02:00
|
|
|
arc_destruct((*rc).data)
|
2012-05-23 23:53:44 +02:00
|
|
|
}
|
2012-05-26 00:29:27 +02:00
|
|
|
|
2012-06-07 02:58:15 +02:00
|
|
|
// An arc over mutable data that is protected by a lock.
|
2012-06-07 21:18:34 +02:00
|
|
|
type ex_data<T: send> = {lock: sys::lock_and_signal, data: T};
|
|
|
|
type exclusive<T: send> = arc_destruct<ex_data<T>>;
|
2012-06-07 02:58:15 +02:00
|
|
|
|
2012-06-07 21:18:34 +02:00
|
|
|
fn exclusive<T:send >(-data: T) -> exclusive<T> {
|
2012-06-07 02:58:15 +02:00
|
|
|
let data = ~{mut count: 1, data: {lock: sys::create_lock(),
|
|
|
|
data: data}};
|
|
|
|
unsafe {
|
|
|
|
let ptr = unsafe::reinterpret_cast(data);
|
|
|
|
unsafe::forget(data);
|
|
|
|
arc_destruct(ptr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-07 21:18:34 +02:00
|
|
|
impl methods<T: send> for exclusive<T> {
|
2012-06-07 02:58:15 +02:00
|
|
|
fn clone() -> exclusive<T> {
|
|
|
|
unsafe {
|
|
|
|
// this makes me nervous...
|
2012-06-22 06:46:43 +02:00
|
|
|
let ptr: ~arc_data<ex_data<T>> =
|
|
|
|
unsafe::reinterpret_cast(self.data);
|
2012-06-07 21:18:34 +02:00
|
|
|
let new_count = rustrt::rust_atomic_increment(&mut ptr.count);
|
|
|
|
assert new_count > 1;
|
2012-06-07 02:58:15 +02:00
|
|
|
unsafe::forget(ptr);
|
|
|
|
}
|
2012-06-22 06:46:43 +02:00
|
|
|
arc_destruct(self.data)
|
2012-06-07 02:58:15 +02:00
|
|
|
}
|
|
|
|
|
2012-06-27 19:24:51 +02:00
|
|
|
unsafe fn with<U>(f: fn(sys::condition, x: &T) -> U) -> U {
|
|
|
|
let ptr: ~arc_data<ex_data<T>> =
|
|
|
|
unsafe::reinterpret_cast(self.data);
|
|
|
|
let r = {
|
|
|
|
let rec: &ex_data<T> = &(*ptr).data;
|
2012-07-01 01:19:07 +02:00
|
|
|
rec.lock.lock_cond(|c| f(c, &rec.data))
|
2012-06-27 19:24:51 +02:00
|
|
|
};
|
|
|
|
unsafe::forget(ptr);
|
|
|
|
r
|
2012-06-07 02:58:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-26 00:29:27 +02:00
|
|
|
// Convenience code for sharing arcs between tasks
|
|
|
|
|
2012-05-29 22:13:31 +02:00
|
|
|
type get_chan<T: const send> = chan<chan<arc<T>>>;
|
|
|
|
|
|
|
|
// (terminate, get)
|
|
|
|
type shared_arc<T: const send> = (shared_arc_res, get_chan<T>);
|
2012-05-26 00:29:27 +02:00
|
|
|
|
2012-06-22 06:46:43 +02:00
|
|
|
class shared_arc_res {
|
|
|
|
let c: comm::chan<()>;
|
|
|
|
new(c: comm::chan<()>) { self.c = c; }
|
|
|
|
drop { self.c.send(()); }
|
2012-05-26 00:29:27 +02:00
|
|
|
}
|
|
|
|
|
2012-05-29 22:13:31 +02:00
|
|
|
fn shared_arc<T: send const>(-data: T) -> shared_arc<T> {
|
2012-05-26 00:29:27 +02:00
|
|
|
let a = arc::arc(data);
|
2012-05-29 22:13:31 +02:00
|
|
|
let p = port();
|
|
|
|
let c = chan(p);
|
2012-07-01 01:19:07 +02:00
|
|
|
do task::spawn() |move a| {
|
2012-05-26 00:29:27 +02:00
|
|
|
let mut live = true;
|
2012-05-29 22:13:31 +02:00
|
|
|
let terminate = port();
|
|
|
|
let get = port();
|
|
|
|
|
|
|
|
c.send((chan(terminate), chan(get)));
|
|
|
|
|
2012-05-26 00:29:27 +02:00
|
|
|
while live {
|
2012-05-29 22:13:31 +02:00
|
|
|
alt comm::select2(terminate, get) {
|
|
|
|
either::left(()) { live = false; }
|
|
|
|
either::right(cc) {
|
|
|
|
comm::send(cc, arc::clone(&a));
|
2012-05-26 00:29:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2012-05-29 22:13:31 +02:00
|
|
|
let (terminate, get) = p.recv();
|
|
|
|
(shared_arc_res(terminate), get)
|
2012-05-26 00:29:27 +02:00
|
|
|
}
|
|
|
|
|
2012-05-29 22:13:31 +02:00
|
|
|
fn get_arc<T: send const>(c: get_chan<T>) -> arc::arc<T> {
|
2012-05-26 00:29:27 +02:00
|
|
|
let p = port();
|
2012-05-29 22:13:31 +02:00
|
|
|
c.send(chan(p));
|
2012-05-26 00:29:27 +02:00
|
|
|
p.recv()
|
|
|
|
}
|
2012-05-29 20:30:10 +02:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
import comm::*;
|
2012-07-04 00:54:26 +02:00
|
|
|
import future::extensions;
|
2012-05-29 20:30:10 +02:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn manually_share_arc() {
|
2012-06-30 01:26:56 +02:00
|
|
|
let v = ~[1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
2012-05-29 20:30:10 +02:00
|
|
|
let arc_v = arc::arc(v);
|
|
|
|
|
|
|
|
let p = port();
|
|
|
|
let c = chan(p);
|
|
|
|
|
2012-07-01 01:19:07 +02:00
|
|
|
do task::spawn() || {
|
2012-05-29 20:30:10 +02:00
|
|
|
let p = port();
|
|
|
|
c.send(chan(p));
|
|
|
|
|
|
|
|
let arc_v = p.recv();
|
|
|
|
|
2012-06-30 01:26:56 +02:00
|
|
|
let v = *arc::get::<~[int]>(&arc_v);
|
2012-05-29 20:30:10 +02:00
|
|
|
assert v[3] == 4;
|
|
|
|
};
|
|
|
|
|
|
|
|
let c = p.recv();
|
|
|
|
c.send(arc::clone(&arc_v));
|
|
|
|
|
|
|
|
assert (*arc::get(&arc_v))[2] == 3;
|
|
|
|
|
|
|
|
log(info, arc_v);
|
|
|
|
}
|
2012-05-29 22:13:31 +02:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn auto_share_arc() {
|
2012-06-30 01:26:56 +02:00
|
|
|
let v = ~[1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
2012-06-01 20:10:27 +02:00
|
|
|
let (_res, arc_c) = shared_arc(v);
|
2012-05-29 22:13:31 +02:00
|
|
|
|
|
|
|
let p = port();
|
|
|
|
let c = chan(p);
|
|
|
|
|
2012-07-01 01:19:07 +02:00
|
|
|
do task::spawn() || {
|
2012-05-29 22:13:31 +02:00
|
|
|
let arc_v = get_arc(arc_c);
|
|
|
|
let v = *get(&arc_v);
|
|
|
|
assert v[2] == 3;
|
|
|
|
|
|
|
|
c.send(());
|
|
|
|
};
|
|
|
|
|
|
|
|
assert p.recv() == ();
|
|
|
|
}
|
2012-06-07 02:58:15 +02:00
|
|
|
|
|
|
|
#[test]
|
2012-06-20 03:03:28 +02:00
|
|
|
#[ignore] // this can probably infinite loop too.
|
2012-06-07 02:58:15 +02:00
|
|
|
fn exclusive_arc() {
|
2012-06-30 01:26:56 +02:00
|
|
|
let mut futures = ~[];
|
2012-06-07 02:58:15 +02:00
|
|
|
|
|
|
|
let num_tasks = 10u;
|
|
|
|
let count = 1000u;
|
|
|
|
|
|
|
|
let total = exclusive(~mut 0u);
|
|
|
|
|
2012-07-01 01:19:07 +02:00
|
|
|
for uint::range(0u, num_tasks) |_i| {
|
2012-06-07 02:58:15 +02:00
|
|
|
let total = total.clone();
|
2012-07-01 01:19:07 +02:00
|
|
|
futures += ~[future::spawn(|| {
|
|
|
|
for uint::range(0u, count) |_i| {
|
|
|
|
do total.with |_cond, count| {
|
2012-06-07 02:58:15 +02:00
|
|
|
**count += 1u;
|
|
|
|
}
|
|
|
|
}
|
2012-06-30 01:26:56 +02:00
|
|
|
})];
|
2012-06-07 02:58:15 +02:00
|
|
|
};
|
|
|
|
|
2012-07-01 01:19:07 +02:00
|
|
|
for futures.each |f| { f.get() }
|
2012-06-07 02:58:15 +02:00
|
|
|
|
2012-07-01 01:19:07 +02:00
|
|
|
do total.with |_cond, total| {
|
2012-06-07 02:58:15 +02:00
|
|
|
assert **total == num_tasks * count
|
|
|
|
};
|
|
|
|
}
|
2012-05-29 20:30:10 +02:00
|
|
|
}
|