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 raw-pointer 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 {
var total: i64 = 0 as i64;
var 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 {
var i: i32 = 0 as i32;
while i < (100000 as i32) {
let _prev: u64 =
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 =
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 and atomic ordering.
‹ Back to all examples