lib/alloc/src/boundary.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2
3 const policy = @import("policy.zig");
4 const process = @import("process.zig");
5
6 pub const PhaseBoundaryState = struct {
7 phase: Phase = .initialization,
8 sealed: bool = false,
9
10 pub const Phase = enum { initialization, steady, teardown };
11 };
12
13 pub const PhaseBoundary = struct {
14 state: ?*PhaseBoundaryState = null,
15
16 pub fn seal(self: PhaseBoundary) void {
17 const state = self.state orelse return;
18 std.debug.assert(state.phase == .initialization);
19 policy.sealProcessAllocatorPhase();
20 state.phase = .steady;
21 state.sealed = true;
22 }
23
24 pub fn beginTeardown(self: PhaseBoundary) void {
25 const state = self.state orelse return;
26 if (state.phase != .steady) return;
27 policy.beginProcessAllocatorTeardown();
28 state.phase = .teardown;
29 }
30
31 pub fn sealed(self: PhaseBoundary) bool {
32 const state = self.state orelse return false;
33 return state.sealed;
34 }
35 };
36
37 pub fn writePhaseReport(writer: *std.Io.Writer, tool: []const u8, was_sealed: bool) !bool {
38 const violations = process.violations() orelse return false;
39 try writer.print(
40 "{s} allocator phase steady violations: sealed={} total={d} alloc={d} " ++
41 "resize={d} remap={d} free={d}",
42 .{
43 tool,
44 was_sealed,
45 violations.total(),
46 violations.allocations,
47 violations.resizes,
48 violations.remaps,
49 violations.frees,
50 },
51 );
52 if (process.violationSites()) |sites| {
53 for (sites.slice()) |address| try writer.print(" site=0x{x}", .{address});
54 }
55 try writer.writeByte('\n');
56 return true;
57 }
58
59 test "phase boundary without state is inert" {
60 const boundary = PhaseBoundary{};
61 boundary.seal();
62 boundary.beginTeardown();
63 try std.testing.expect(!boundary.sealed());
64 }
65
66 test "phase report is silent when the phase machinery is off" {
67 if (process.mode != .off) return;
68 var buffer: [256]u8 = undefined;
69 var writer = std.Io.Writer.fixed(&buffer);
70 try std.testing.expect(!(try writePhaseReport(&writer, "test", false)));
71 try std.testing.expectEqual(@as(usize, 0), writer.buffered().len);
72 }
73
74 test "phase boundary transitions once through steady into teardown" {
75 if (process.mode == .off) return;
76 var state = PhaseBoundaryState{};
77 const boundary = PhaseBoundary{ .state = &state };
78 if (process.currentPhase()) |phase| {
79 if (phase != .initialization) return;
80 }
81 boundary.seal();
82 try std.testing.expect(boundary.sealed());
83 try std.testing.expectEqual(PhaseBoundaryState.Phase.steady, state.phase);
84 boundary.beginTeardown();
85 try std.testing.expectEqual(PhaseBoundaryState.Phase.teardown, state.phase);
86 }