Merge branch 'main' into 95-bug4

This commit is contained in:
Chris Boesch
2026-04-14 12:43:29 +02:00
5 changed files with 80 additions and 39 deletions

View File

@@ -1179,12 +1179,7 @@ const exercises = [_]Exercise{
}, },
.{ .{
.main_file = "093_async9.zig", .main_file = "093_async9.zig",
.output = .output = "Worker 1 found signal start over threshold at index 12!",
\\Computing concurrently!
\\Main continues...
\\Main done waiting.
\\Result: 123
, // pay attention to the comma
}, },
.{ .{
.main_file = "094_async10.zig", .main_file = "094_async10.zig",

View File

@@ -30,40 +30,74 @@
// defer _ = future.cancel(io); // defer _ = future.cancel(io);
// const result = future.await(io); // const result = future.await(io);
// //
// Notice the 'try' — that's the key difference in usage! // Let's try a slightly simplified example from signal processing:
// Suppose we're looking for the beginning of a signal above the noise
// level. To do this, we compare each entry from beginning to end with
// the threshold.To speed things up a bit, we split the signal into
// two halves and have two parallel workers search for them.
// Who finds the beginning first "wins" and thus ends the other one.
// //
// Fix this program to launch the computation concurrently. // As I said, this is a simplified explanation,
// but in practice it's done more or less like this.
// //
const std = @import("std"); const std = @import("std");
const Io = std.Io;
const print = std.debug.print; const print = std.debug.print;
const SearchResult = struct {
worker_id: u8,
index: usize,
};
pub fn main(init: std.process.Init) !void { pub fn main(init: std.process.Init) !void {
const io = init.io; const io = init.io;
// Launch with a guaranteed separate unit of concurrency. const data = [_]u32{ 10, 23, 45, 67, 12, 69, 3, 54, 69, 42, 68, 56, 71, 79, 79, 75, 70, 77 };
// Which Io method guarantees this? const threshold = 70;
// (Hint: unlike io.async, this one can fail!) const mid = data.len / 2;
var future = try io.???(compute, .{io});
defer _ = future.cancel(io);
// Note: All breaks in this exercise (using sleep) // A queue with space for one result.
// are only necessary for a deterministic result. var buf: [1]SearchResult = undefined;
io.sleep(std.Io.Duration.fromMilliseconds(100), .awake) catch {}; var queue = Io.Queue(SearchResult).init(&buf);
print("Main continues...\n", .{}); // Launch two workers, each searching half the array.
var f1 = ???(searchRange, .{ data[0..mid], target, 0, 0, &queue, io });
defer _ = f1.cancel(io);
// Wait 1 second for the output order. var f2 = ???(searchRange, .{ data[mid..], target, mid, 1, &queue, io });
io.sleep(std.Io.Duration.fromMilliseconds(200), .awake) catch {}; defer _ = f2.cancel(io);
print("Main done waiting.\n", .{}); // Wait for the first result.
const result = try queue.getOne(io);
const result = future.await(io); print("Worker {} found signal start over threshold at index {}!\n", .{ result.worker_id, result.index });
print("Result: {}\n", .{result});
} }
fn compute(io: std.Io) u32 { fn searchThreshold(
print("Computing concurrently!\n", .{}); io: Io,
// Simulate some work. slice: []const u32,
io.sleep(std.Io.Duration.fromMilliseconds(400), .awake) catch return 0; threshold: u32,
return 123; base_offset: usize,
worker_id: u8,
queue: *Io.Queue(SearchResult),
) void {
for (slice, 0..) |val, i| {
// This pause is necessary so that the process can be canceled
// if another one has already finished. Without this pause,
// all workers would continue until the end.
io.sleep(Io.Duration.fromMilliseconds(1), .awake) catch return;
// To test this, you can view the work of the workers
// and then comment out the pause.
// print("id: {} - val: {}\n", .{ worker_id, val });
if (val >= threshold) {
queue.putOne(io, .{
.worker_id = worker_id,
.index = base_offset + i,
}) catch return;
return;
}
}
} }

View File

@@ -105,3 +105,6 @@ fn thread_pi(pi: *f64, begin: u64, end: u64) !void {
// //
// And you should remove the formatting restriction in "print", // And you should remove the formatting restriction in "print",
// otherwise you will not be able to see the additional digits. // otherwise you will not be able to see the additional digits.
//
// If count = 10_000_000_000_000 you should see the following:
// 3.141592653589

View File

@@ -1,11 +1,20 @@
--- exercises/093_async9.zig 2026-04-13 17:55:35.567204530 +0200 --- exercises/093_async9.zig 2026-04-14 09:50:05.694073287 +0200
+++ answers/093_async9.zig 2026-04-13 18:05:05.636355044 +0200 +++ answers/093_async9.zig 2026-04-14 09:49:58.604934765 +0200
@@ -43,7 +43,7 @@ @@ -61,10 +61,10 @@
// Launch with a guaranteed separate unit of concurrency. var queue = Io.Queue(SearchResult).init(&buf);
// Which Io method guarantees this?
// (Hint: unlike io.async, this one can fail!)
- var future = try io.???(compute, .{io});
+ var future = try io.concurrent(compute, .{io});
defer _ = future.cancel(io);
// Note: All breaks in this exercise (using sleep) // Launch two workers, each searching half the array.
- var f1 = ???(searchRange, .{ data[0..mid], target, 0, 0, &queue, io });
+ var f1 = try io.concurrent(searchThreshold, .{ io, data[0..mid], threshold, 0, 0, &queue });
defer _ = f1.cancel(io);
- var f2 = ???(searchRange, .{ data[mid..], target, mid, 1, &queue, io });
+ var f2 = try io.concurrent(searchThreshold, .{ io, data[mid..], threshold, mid, 1, &queue });
defer _ = f2.cancel(io);
// Wait for the first result.
@@ -100,4 +100,3 @@
}
}
}
-

View File

@@ -1,5 +1,5 @@
--- exercises/108_threading2.zig 2025-08-15 15:17:57.839348063 +0200 --- exercises/108_threading2.zig 2026-04-14 06:44:18.848246237 +0200
+++ answers/108_threading2.zig 2026-04-02 10:51:15.811831656 +0200 +++ answers/108_threading2.zig 2026-04-14 08:15:30.894485037 +0200
@@ -81,8 +81,8 @@ @@ -81,8 +81,8 @@
defer handle1.join(); defer handle1.join();