Threads and atomics
This example combines two checked recipes from the C+ source tree:
docs/examples/recipes/parallel_sum and
docs/examples/recipes/concurrent_counter.
Together they back the concurrency claim without overclaiming. The first recipe
shows the preferred safe shape: partition data, move each piece into a worker,
then join. The second shows the explicit unsafe atomic shape for the rarer case
where workers must share mutable state.
Partition and join
parallel_sum splits 1..1000 into two owned ranges. Each range is moved into
one worker with thread::spawn_with, and the parent joins both handles:
import "stdlib/thread" as thread;
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::JoinHandle[i64] = thread::spawn_with::[Range, i64](left, sum_range);
let h2: thread::JoinHandle[i64] = thread::spawn_with::[Range, i64](right, sum_range);
let total: i64 = h1.join() +% h2.join();
if total != (500500 as i64) { return 1; }
There is no shared mutable state in this recipe, so there is no lock or atomic operation for the user to reason about.
Shared counter with atomics
concurrent_counter is the companion recipe for the case that cannot be
partitioned. Two workers increment the same heap-allocated u64 100,000 times
each, using an explicit atomic fetch-add:
import "stdlib/thread" as thread;
import "stdlib/atomic" as atomic;
fn bump(counter: *u64) -> i32 {
let mut i: i32 = 0 as i32;
while i < (100000 as i32) {
let _prev: u64 = unsafe {
atomic::atomic_fetch_add_u64(counter, 1 as u64, atomic::Ordering::SeqCst)
};
i = i +% (1 as i32);
}
return 0 as i32;
}
After joining both workers, the parent loads the final value and expects exactly
200000:
let final_val: u64 = unsafe {
atomic::atomic_load_u64(counter, atomic::Ordering::SeqCst)
};
if final_val != (200000 as u64) { return 1; }
return 0;
Reproduce
From docs/examples/recipes/parallel_sum:
cpc build
./target/debug/parallel_sum
From docs/examples/recipes/concurrent_counter:
cpc build
./target/debug/concurrent_counter
Expected result: both programs exit with code 0.
The credibility point is the contrast: ordinary parallel work stays in the safe
partition-and-join shape, while shared mutable state is made explicit with raw
pointers, unsafe, and atomic ordering.
‹ Back to all examples