I’m trying to make a mini scheduler where processes can send a message over a ringbuffer, then call ring() to wake up the receiving core if it was asleep, then that core services whatever it needs in it’s state.update() function. I’m using x86 umonitor and umwait as light sleep is what I’m going for. And I also added the if load on the ring, cause if I go with like fetchAdd generation u32, then it’d be a bunch of cacheline bouncing because ring() can be called a few million times/s from other cores as they submit messages to the main core. I’m pretty much completely lost in terms of properly implementing this with atomics. I think this is on the right track because I check update one more time before truly sleeping which is kinda Dekker’s algorithm, but I think its still wrong. Any help would be super appreciated, thanks.
const DoorbellState = enum(u8) {
awake,
asleep,
};
pub const Doorbell = struct {
const Self = @This();
state: mem.CachePadded(atomic.Value(DoorbellState)),
pub fn init() Self {
return .{ .state = .init(.awake) };
}
pub fn ring(self: *Self) void {
if (self.state.load(.seq_cst) == .awake) {
@branchHint(.likely);
return;
}
self.state.store(.awake, .release);
}
pub fn prepare(self: *Self) void {
self.state.store(.asleep, .seq_cst);
hw.time.monitor(&self.state); // calls umonitor
}
pub fn cancel(self: *Self) void {
self.state.store(.awake, .release);
}
pub fn commit(self: *Self, deadline: unit.Tick(u64, .n4)) void {
if (self.state.load(.acquire) == .awake) return;
hw.time.wait(deadline); // calls umwait
self.state.store(.awake, .release);
}
};
var last_time = hw.time.now().to(Tick(u32, .n4));
while (true) {
const now = hw.time.now(); // Tick(u64, .n4)
const now_short = now.to(Tick(u32, .n4));
last_time = now_short;
const delay = state.update();
if (delay.raw > min_sleep) {
sys.doorbells[cpu].prepare();
const delay2 = state.update();
if (delay2.raw <= min_sleep) {
sys.doorbells[cpu].cancel();
continue;
}
const sleep_now = hw.time.now();
sys.doorbells[cpu].commit(sleep_now.add(delay2.to(Tick(u64, .n4))));
} else {
core.atomic.spinLoop();
}
}