summaryrefslogtreecommitdiff
path: root/exercises/094_async10.zig
blob: 6ed229d2b458b198cd99ef4d37922d6e51b1dbca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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.";
}