diff --git a/build.zig b/build.zig index 5e5d3de..b9aaac3 100644 --- a/build.zig +++ b/build.zig @@ -1161,13 +1161,20 @@ const exercises = [_]Exercise{ }, .{ .main_file = "092_async9.zig", - .output = "", - .skip = true, + .output = + \\Main thread continues... + \\Computing on a separate thread! + \\Main thread done waiting. + \\Result: 123 + , // pay attention to the comma }, .{ .main_file = "093_async10.zig", - .output = "", - .skip = true, + .output = + \\Starting critical section... + \\Critical section completed safely. + \\Task result: All data saved. + , // pay attention to the comma }, .{ .main_file = "094_async_quiz.zig", diff --git a/exercises/093_async10.zig b/exercises/093_async10.zig new file mode 100644 index 0000000..6ed229d --- /dev/null +++ b/exercises/093_async10.zig @@ -0,0 +1,67 @@ +// +// In exercise 088, we learned that cancellation happens at +// "cancellation points" — any Io function that can return +// error.Canceled. +// +// But sometimes a task has a critical section that MUST NOT +// be interrupted — for example, writing a consistent state +// to disk, or completing a transaction. +// +// Io provides CancelProtection for this: +// +// const old = io.swapCancelProtection(.blocked); +// defer _ = io.swapCancelProtection(old); +// +// // In this block, NO Io function will return error.Canceled. +// // The cancel request is held until protection is restored. +// +// There are two states: +// .unblocked — normal: cancellation points can fire (default) +// .blocked — protected: error.Canceled is never returned +// +// There's also io.checkCancel() — a pure cancellation point +// that does nothing except return error.Canceled if a cancel +// request is pending. Useful in long CPU-bound loops. +// +// And io.recancel() — re-arms a consumed cancel request so +// the NEXT cancellation point will fire again. +// +// Fix this program so the critical section completes even +// when the task is canceled. +// +const std = @import("std"); +const print = std.debug.print; + +pub fn main(init: std.process.Init) !void { + const io = init.io; + + var future = io.async(importantTask, .{io}); + + // Give the task time to start and enter its critical section. + io.sleep(std.Io.Duration.fromMilliseconds(300), .awake) catch {}; + + // Cancel while the task is in its protected section. + const result = future.cancel(io); + print("Task result: {s}\n", .{result}); +} + +fn importantTask(io: std.Io) []const u8 { + print("Starting critical section...\n", .{}); + + // Protect this section from cancellation. + // What method swaps the cancel protection state? + const old = io.???(. blocked); + defer _ = io.???(old); + + // This sleep will NOT return error.Canceled even though + // we get canceled during it — protection is active! + io.sleep(std.Io.Duration.fromMilliseconds(600), .awake) catch |err| switch (err) { + error.Canceled => { + // This should never happen while protected! + return "ERROR: canceled during critical section!"; + }, + }; + + print("Critical section completed safely.\n", .{}); + return "All data saved."; +} diff --git a/patches/patches/093_async10.patch b/patches/patches/093_async10.patch new file mode 100644 index 0000000..69f7518 --- /dev/null +++ b/patches/patches/093_async10.patch @@ -0,0 +1,13 @@ +--- exercises/093_async10.zig 2026-04-03 14:25:16.600025924 +0200 ++++ answers/093_async10.zig 2026-04-03 14:24:56.192615893 +0200 +@@ -50,8 +50,8 @@ + + // Protect this section from cancellation. + // What method swaps the cancel protection state? +- const old = io.???(. blocked); +- defer _ = io.???(old); ++ const old = io.swapCancelProtection(.blocked); ++ defer _ = io.swapCancelProtection(old); + + // This sleep will NOT return error.Canceled even though + // we get canceled during it — protection is active!