-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlazy.zig
96 lines (76 loc) · 2.43 KB
/
lazy.zig
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
const std = @import("std");
//
pub fn fnPtrAsInit(comptime T: type, comptime f: fn () T) type {
return struct {
fn init() void {
f();
}
};
}
pub fn Lazy(comptime T: type) type {
return struct {
val: T = undefined,
initialized: std.atomic.Value(bool) = std.atomic.Value(bool).init(false),
initializing: std.atomic.Value(bool) = std.atomic.Value(bool).init(false),
const Self = @This();
pub fn new() Self {
return .{};
}
pub fn initNow(self: *Self, val: T) void {
_ = self.getOrInit(struct {
val: T,
fn init(s: *const @This()) T {
return s.val;
}
}{
.val = val,
});
}
pub fn waitOrInit(self: *Self, init: anytype) *T {
if (self.getOrInit(init)) |v| {
return v;
}
@setCold(true);
self.wait();
return &self.val;
}
pub fn get(self: *Self) ?*T {
if (!self.isInitialized()) {
@setCold(true);
return null;
}
return &self.val;
}
pub fn getOrInit(self: *Self, init: anytype) ?*T {
if (!self.isInitialized()) {
// very low chance to not be initialized (only the first time)
@setCold(true);
self.startInit() catch {
// super low chance to not be initialized and currently initializing
// (only when one thread accesses it for the first time and the current thread just a short time later)
@setCold(true);
return null;
};
self.val = init.init();
self.finishInit();
}
return &self.val;
}
pub fn isInitialized(self: *Self) bool {
return self.initialized.load(.acquire);
}
pub fn wait(self: *Self) void {
while (!self.isInitialized()) {
std.atomic.spinLoopHint();
}
}
pub fn startInit(self: *Self) !void {
if (self.initializing.swap(true, .acquire)) {
return error.AlreadyInitializing;
}
}
pub fn finishInit(self: *Self) void {
self.initialized.store(true, .release);
}
};
}