···11791179 },
11801180 .{
11811181 .main_file = "093_async9.zig",
11821182- .output =
11831183- \\Computing concurrently!
11841184- \\Main continues...
11851185- \\Main done waiting.
11861186- \\Result: 123
11871187- , // pay attention to the comma
11821182+ .output = "Worker 1 found signal start over threshold at index 12!",
11881183 },
11891184 .{
11901185 .main_file = "094_async10.zig",
+55-21
exercises/093_async9.zig
···3030// defer _ = future.cancel(io);
3131// const result = future.await(io);
3232//
3333-// Notice the 'try' — that's the key difference in usage!
3333+// Let's try a slightly simplified example from signal processing:
3434+// Suppose we're looking for the beginning of a signal above the noise
3535+// level. To do this, we compare each entry from beginning to end with
3636+// the threshold.To speed things up a bit, we split the signal into
3737+// two halves and have two parallel workers search for them.
3838+// Who finds the beginning first "wins" and thus ends the other one.
3439//
3535-// Fix this program to launch the computation concurrently.
4040+// As I said, this is a simplified explanation,
4141+// but in practice it's done more or less like this.
3642//
3743const std = @import("std");
4444+const Io = std.Io;
3845const print = std.debug.print;
4646+4747+const SearchResult = struct {
4848+ worker_id: u8,
4949+ index: usize,
5050+};
39514052pub fn main(init: std.process.Init) !void {
4153 const io = init.io;
42544343- // Launch with a guaranteed separate unit of concurrency.
4444- // Which Io method guarantees this?
4545- // (Hint: unlike io.async, this one can fail!)
4646- var future = try io.???(compute, .{io});
4747- defer _ = future.cancel(io);
5555+ const data = [_]u32{ 10, 23, 45, 67, 12, 69, 3, 54, 69, 42, 68, 56, 71, 79, 79, 75, 70, 77 };
5656+ const threshold = 70;
5757+ const mid = data.len / 2;
48584949- // Note: All breaks in this exercise (using sleep)
5050- // are only necessary for a deterministic result.
5151- io.sleep(std.Io.Duration.fromMilliseconds(100), .awake) catch {};
5959+ // A queue with space for one result.
6060+ var buf: [1]SearchResult = undefined;
6161+ var queue = Io.Queue(SearchResult).init(&buf);
52625353- print("Main continues...\n", .{});
6363+ // Launch two workers, each searching half the array.
6464+ var f1 = ???(searchRange, .{ data[0..mid], target, 0, 0, &queue, io });
6565+ defer _ = f1.cancel(io);
54665555- // Wait 1 second for the output order.
5656- io.sleep(std.Io.Duration.fromMilliseconds(200), .awake) catch {};
6767+ var f2 = ???(searchRange, .{ data[mid..], target, mid, 1, &queue, io });
6868+ defer _ = f2.cancel(io);
57695858- print("Main done waiting.\n", .{});
7070+ // Wait for the first result.
7171+ const result = try queue.getOne(io);
59726060- const result = future.await(io);
6161- print("Result: {}\n", .{result});
7373+ print("Worker {} found signal start over threshold at index {}!\n", .{ result.worker_id, result.index });
6274}
63756464-fn compute(io: std.Io) u32 {
6565- print("Computing concurrently!\n", .{});
6666- // Simulate some work.
6767- io.sleep(std.Io.Duration.fromMilliseconds(400), .awake) catch return 0;
6868- return 123;
7676+fn searchThreshold(
7777+ io: Io,
7878+ slice: []const u32,
7979+ threshold: u32,
8080+ base_offset: usize,
8181+ worker_id: u8,
8282+ queue: *Io.Queue(SearchResult),
8383+) void {
8484+ for (slice, 0..) |val, i| {
8585+ // This pause is necessary so that the process can be canceled
8686+ // if another one has already finished. Without this pause,
8787+ // all workers would continue until the end.
8888+ io.sleep(Io.Duration.fromMilliseconds(1), .awake) catch return;
8989+9090+ // To test this, you can view the work of the workers
9191+ // and then comment out the pause.
9292+ // print("id: {} - val: {}\n", .{ worker_id, val });
9393+9494+ if (val >= threshold) {
9595+ queue.putOne(io, .{
9696+ .worker_id = worker_id,
9797+ .index = base_offset + i,
9898+ }) catch return;
9999+ return;
100100+ }
101101+ }
69102}
103103+
+3
exercises/108_threading2.zig
···105105//
106106// And you should remove the formatting restriction in "print",
107107// otherwise you will not be able to see the additional digits.
108108+//
109109+// If count = 10_000_000_000_000 you should see the following:
110110+// 3.141592653589
+19-10
patches/patches/093_async9.patch
···11---- exercises/093_async9.zig 2026-04-13 17:55:35.567204530 +0200
22-+++ answers/093_async9.zig 2026-04-13 18:05:05.636355044 +0200
33-@@ -43,7 +43,7 @@
44- // Launch with a guaranteed separate unit of concurrency.
55- // Which Io method guarantees this?
66- // (Hint: unlike io.async, this one can fail!)
77-- var future = try io.???(compute, .{io});
88-+ var future = try io.concurrent(compute, .{io});
99- defer _ = future.cancel(io);
11+--- exercises/093_async9.zig 2026-04-14 09:50:05.694073287 +0200
22++++ answers/093_async9.zig 2026-04-14 09:49:58.604934765 +0200
33+@@ -61,10 +61,10 @@
44+ var queue = Io.Queue(SearchResult).init(&buf);
1051111- // Note: All breaks in this exercise (using sleep)
66+ // Launch two workers, each searching half the array.
77+- var f1 = ???(searchRange, .{ data[0..mid], target, 0, 0, &queue, io });
88++ var f1 = try io.concurrent(searchThreshold, .{ io, data[0..mid], threshold, 0, 0, &queue });
99+ defer _ = f1.cancel(io);
1010+1111+- var f2 = ???(searchRange, .{ data[mid..], target, mid, 1, &queue, io });
1212++ var f2 = try io.concurrent(searchThreshold, .{ io, data[mid..], threshold, mid, 1, &queue });
1313+ defer _ = f2.cancel(io);
1414+1515+ // Wait for the first result.
1616+@@ -100,4 +100,3 @@
1717+ }
1818+ }
1919+ }
2020+-