// 982: Join Parallel Async
// Rust: std::thread::spawn + join() โ like OCaml's Lwt.both
use std::thread;
// --- Approach 1: Join two threads (Lwt.both analogue) ---
fn parallel_both<A, B, F1, F2>(f1: F1, f2: F2) -> (A, B)
where
A: Send + 'static,
B: Send + 'static,
F1: FnOnce() -> A + Send + 'static,
F2: FnOnce() -> B + Send + 'static,
{
let h1 = thread::spawn(f1);
let h2 = thread::spawn(f2);
// Both run concurrently; join waits for both
let a = h1.join().expect("thread 1 panicked");
let b = h2.join().expect("thread 2 panicked");
(a, b)
}
// --- Approach 2: Join N tasks and collect results ---
fn parallel_map<T, F>(tasks: Vec<F>) -> Vec<T>
where
T: Send + 'static,
F: FnOnce() -> T + Send + 'static,
{
let handles: Vec<_> = tasks.into_iter().map(thread::spawn).collect();
handles.into_iter().map(|h| h.join().expect("task panicked")).collect()
}
// --- Approach 3: Parallel sum ---
fn parallel_sum(ns: Vec<i32>) -> i32 {
let handles: Vec<_> = ns.into_iter()
.map(|n| thread::spawn(move || n * n))
.collect();
handles.into_iter()
.map(|h| h.join().unwrap())
.sum()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parallel_both() {
let (a, b) = parallel_both(|| 6 * 7, || 10 + 20);
assert_eq!(a, 42);
assert_eq!(b, 30);
}
#[test]
fn test_parallel_map() {
let mut results = parallel_map(vec![
Box::new(|| 2 + 2) as Box<dyn FnOnce() -> i32 + Send>,
Box::new(|| 3 * 3),
Box::new(|| 10 - 1),
]);
results.sort(); // order may vary
assert_eq!(results, vec![4, 9, 9]);
}
#[test]
fn test_parallel_sum() {
// 1+4+9+16 = 30
assert_eq!(parallel_sum(vec![1, 2, 3, 4]), 30);
}
#[test]
fn test_both_independent() {
// Results don't depend on order
let (x, y) = parallel_both(|| "hello", || 42u32);
assert_eq!(x, "hello");
assert_eq!(y, 42);
}
#[test]
fn test_empty_parallel_map() {
let results: Vec<i32> = parallel_map::<i32, fn() -> i32>(vec![]);
assert!(results.is_empty());
}
}
(* 982: Join Parallel Async *)
(* OCaml: Lwt.both p1 p2 runs them "concurrently" and waits for both *)
(* --- Approach 1: Simulate Lwt.both with threads --- *)
let parallel_both f1 f2 =
let t1 = Thread.create f1 () in
let t2 = Thread.create f2 () in
(* In Lwt: Lwt.both returns (v1, v2) when both resolve *)
(* With threads, we join both *)
Thread.join t1;
Thread.join t2
let result1 = ref 0
let result2 = ref 0
let () =
parallel_both
(fun () -> result1 := 6 * 7)
(fun () -> result2 := 10 + 20);
assert (!result1 = 42);
assert (!result2 = 30);
Printf.printf "Approach 1 (parallel threads): %d, %d\n" !result1 !result2
(* --- Approach 2: Lwt.both concept โ collect results via mutex --- *)
let compute_parallel tasks =
let m = Mutex.create () in
let results = Array.make (List.length tasks) 0 in
let threads = List.mapi (fun i f ->
Thread.create (fun () ->
let v = f () in
Mutex.lock m;
results.(i) <- v;
Mutex.unlock m
) ()
) tasks in
List.iter Thread.join threads;
Array.to_list results
let () =
let tasks = [
(fun () -> 2 + 2);
(fun () -> 3 * 3);
(fun () -> 10 - 1);
] in
let results = compute_parallel tasks in
assert (results = [4; 9; 9]);
Printf.printf "Approach 2 (parallel collect): [%s]\n"
(String.concat "; " (List.map string_of_int results))
(* --- Approach 3: Join all, sum results --- *)
let parallel_sum ns =
let total = ref 0 in
let m = Mutex.create () in
let threads = List.map (fun n ->
Thread.create (fun () ->
(* simulate work: n * n *)
let v = n * n in
Mutex.lock m;
total := !total + v;
Mutex.unlock m
) ()
) ns in
List.iter Thread.join threads;
!total
let () =
(* 1^2 + 2^2 + 3^2 + 4^2 = 1+4+9+16 = 30 *)
let s = parallel_sum [1;2;3;4] in
assert (s = 30);
Printf.printf "Approach 3 (parallel sum): %d\n" s
let () = Printf.printf "โ All tests passed\n"