Systems
·
v0.0.13
Threads and atomics
import "stdlib/thread" as thread;
fn worker() -> i32 { return 42; }
fn main() -> i32 {
let h: thread::JoinHandle[i32] = thread::spawn::[i32](worker);
return h.join();
}
Passing data into the worker
For non-Copy input, use spawn_with, which moves the value into the thread:
fn proc(move s: string) -> i32 { return s.len() as i32; }
let s = "hello".to_string();
let h = thread::spawn_with::[string, i32](s, proc);
let n = h.join();
The safe pattern: partition and join
This is the first pattern to reach for. Race-freedom is mechanical, because there is no shared memory:
struct Range { start: i64, end: i64 }
fn sum_range(r: Range) -> i64 {
let mut total: i64 = 0 as i64;
let mut i: i64 = r.start;
while i < r.end { total = total +% i; i = i +% (1 as i64); }
return total;
}
let h1 = thread::spawn_with::[Range, i64](left, sum_range);
let h2 = thread::spawn_with::[Range, i64](right, sum_range);
let total: i64 = h1.join() +% h2.join();
Note that Rc[T] is !Send and is rejected at spawn; share across threads with Arc[T]. See Real-time for the Send / Sync rules (E0502).
Atomics
For the rare cases that cannot partition:
import "stdlib/atomic" as atomic;
let p: *u64 = unsafe { ... }; // pointer to a shared u64
unsafe {
atomic::atomic_fetch_add_u64(p, 1 as u64, atomic::Ordering::Relaxed);
}
Ordering values: Relaxed, Acquire, Release, AcqRel, SeqCst. Widths: i32 / i64 / u32 / u64.
Mutex
The mutex is internally refcounted, so a clone shares it across threads (C+ has no &T to make Arc[Mutex[T]] work):
import "stdlib/mutex" as mutex;
let m = mutex::new::[i32](10);
let m2 = m.clone(); // share across threads
{
let mut g = m.lock();
g.set(g.get() +% 1);
} // the guard's Drop releases
Two guards in the same scope deadlock: the borrow checker does not yet prevent this, so use block scopes to bound each guard's lifetime.