mirror of
https://github.com/rdmsr/zag.git
synced 2026-10-27 13:58:20 +00:00
ke: rework timer cancellation
This commit is contained in:
+59
-66
@@ -1,13 +1,15 @@
|
|||||||
//! Timer object implementation.
|
//! Timer object implementation.
|
||||||
//! Timer objects are useful when one wants to wait a given amount of time for
|
//! Timer objects are useful when one wants to wait a given amount of time for
|
||||||
//! an event to occur.
|
//! an event to occur.
|
||||||
const std = @import("std");
|
|
||||||
const rtl = @import("rtl");
|
const rtl = @import("rtl");
|
||||||
const r = @import("root");
|
const r = @import("root");
|
||||||
const ke = r.ke;
|
const ke = r.ke;
|
||||||
const kep = ke.private;
|
const kep = ke.private;
|
||||||
const pl = r.pl;
|
const pl = r.pl;
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
|
||||||
const PerCpu = struct {
|
const PerCpu = struct {
|
||||||
/// Heap of pending timers on this CPU.
|
/// Heap of pending timers on this CPU.
|
||||||
timers: rtl.PairingHeap(.min, kep.timer.cmp_timer),
|
timers: rtl.PairingHeap(.min, kep.timer.cmp_timer),
|
||||||
@@ -22,7 +24,7 @@ pub const Timer = struct {
|
|||||||
Running,
|
Running,
|
||||||
/// The timer is currently enqueued.
|
/// The timer is currently enqueued.
|
||||||
Pending,
|
Pending,
|
||||||
/// The timer was stopped.
|
/// The timer is no longer enqueued or being signaled.
|
||||||
Stopped,
|
Stopped,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -79,15 +81,10 @@ pub fn set(timer: *Timer, time: r.Nanoseconds, opts: Options) void {
|
|||||||
|
|
||||||
const cpu = percpu.local();
|
const cpu = percpu.local();
|
||||||
|
|
||||||
if (timer.state.cmpxchgStrong(
|
if (timer.state.load(.monotonic) != .Stopped) {
|
||||||
.Stopped,
|
|
||||||
.Pending,
|
|
||||||
.acquire,
|
|
||||||
.monotonic,
|
|
||||||
) != null) {
|
|
||||||
// Could not set the timer.
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
timer.state.store(.Pending, .monotonic);
|
||||||
|
|
||||||
cpu.lock.acquire_no_ipl();
|
cpu.lock.acquire_no_ipl();
|
||||||
defer cpu.lock.release_no_ipl();
|
defer cpu.lock.release_no_ipl();
|
||||||
@@ -111,41 +108,51 @@ pub fn set(timer: *Timer, time: r.Nanoseconds, opts: Options) void {
|
|||||||
|
|
||||||
/// Cancel a timer.
|
/// Cancel a timer.
|
||||||
pub fn cancel(timer: *Timer) void {
|
pub fn cancel(timer: *Timer) void {
|
||||||
const old_state = timer.state.load(.monotonic);
|
const ipl = ke.ipl.raise(.Dispatch);
|
||||||
|
defer ke.ipl.lower(ipl);
|
||||||
|
|
||||||
if (old_state == .Stopped or old_state == .Running) {
|
while (true) {
|
||||||
// Already stopped or currently executing.
|
timer.hdr.lock.acquire_no_ipl();
|
||||||
return;
|
|
||||||
|
switch (timer.state.load(.acquire)) {
|
||||||
|
.Stopped => {
|
||||||
|
timer.hdr.lock.release_no_ipl();
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
.Running => {
|
||||||
|
// Wait until the timer finishes running.
|
||||||
|
timer.hdr.lock.release_no_ipl();
|
||||||
|
while (timer.state.load(.acquire) == .Running) {
|
||||||
|
std.atomic.spinLoopHint();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.Pending => {
|
||||||
|
const cpu = timer.cpu.?;
|
||||||
|
cpu.lock.acquire_no_ipl();
|
||||||
|
|
||||||
|
// Re-check under the lock.
|
||||||
|
if (timer.state.load(.acquire) == .Pending) {
|
||||||
|
cpu.timers.remove(&timer.node);
|
||||||
|
timer.cpu = null;
|
||||||
|
timer.state.store(.Stopped, .release);
|
||||||
|
|
||||||
|
cpu.lock.release_no_ipl();
|
||||||
|
timer.hdr.lock.release_no_ipl();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu.lock.release_no_ipl();
|
||||||
|
timer.hdr.lock.release_no_ipl();
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ipl = timer.hdr.lock.acquire();
|
|
||||||
defer timer.hdr.lock.release(ipl);
|
|
||||||
|
|
||||||
if (timer.state.cmpxchgStrong(
|
|
||||||
.Pending,
|
|
||||||
.Stopped,
|
|
||||||
.acquire,
|
|
||||||
.monotonic,
|
|
||||||
) != null) {
|
|
||||||
// Something changed, it probably expired.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (timer.cpu) |cpu| {
|
|
||||||
// Dequeue the timer.
|
|
||||||
cpu.lock.acquire_no_ipl();
|
|
||||||
|
|
||||||
cpu.timers.remove(&timer.node);
|
|
||||||
timer.cpu = null;
|
|
||||||
|
|
||||||
cpu.lock.release_no_ipl();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lock dropped
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compare two timers.
|
/// Compare two timers.
|
||||||
pub fn cmp_timer(a: *rtl.pairing_heap.Node, b_: *rtl.pairing_heap.Node) std.math.Order {
|
pub fn cmp_timer(
|
||||||
|
a: *rtl.pairing_heap.Node,
|
||||||
|
b_: *rtl.pairing_heap.Node,
|
||||||
|
) std.math.Order {
|
||||||
const timer_a: *Timer = @fieldParentPtr("node", a);
|
const timer_a: *Timer = @fieldParentPtr("node", a);
|
||||||
const timer_b: *Timer = @fieldParentPtr("node", b_);
|
const timer_b: *Timer = @fieldParentPtr("node", b_);
|
||||||
|
|
||||||
@@ -179,37 +186,24 @@ fn handle_expiry(_: *ke.Dpc, _: ?*anyopaque) void {
|
|||||||
|
|
||||||
const timer: *Timer = @fieldParentPtr("node", timer_node);
|
const timer: *Timer = @fieldParentPtr("node", timer_node);
|
||||||
|
|
||||||
// If the timer expires more than 1ms in the future, consider it not yet due.
|
// If the timer expires more than 1ms in the future, consider it not
|
||||||
// Sub-millisecond differences are close enough to expire immediately.
|
// yet due. Sub-millisecond differences are close enough to expire
|
||||||
|
// immediately.
|
||||||
if (timer.deadline.value > curtime.value and
|
if (timer.deadline.value > curtime.value and
|
||||||
timer.deadline.value - curtime.value > std.time.ns_per_ms)
|
timer.deadline.value - curtime.value > std.time.ns_per_ms)
|
||||||
{
|
{
|
||||||
const next = timer;
|
|
||||||
cpu.lock.release_no_ipl();
|
|
||||||
|
|
||||||
// Re-check in case the timer expired while we were here.
|
|
||||||
const now = ke.time.read_time();
|
const now = ke.time.read_time();
|
||||||
if (next.deadline.value <= now.value or
|
if (timer.deadline.value > now.value and
|
||||||
next.deadline.value - now.value <= std.time.ns_per_ms)
|
timer.deadline.value - now.value > std.time.ns_per_ms)
|
||||||
{
|
{
|
||||||
continue; // expired in the meantime, loop again
|
pl.arm_timer(.init(timer.deadline.value - now.value));
|
||||||
|
cpu.lock.release_no_ipl();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std.debug.assert(next.deadline.value > now.value);
|
|
||||||
pl.arm_timer(.init(next.deadline.value - now.value));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timer.state.cmpxchgStrong(
|
assert(timer.state.load(.monotonic) == .Pending);
|
||||||
.Pending,
|
timer.state.store(.Running, .monotonic);
|
||||||
.Running,
|
|
||||||
.acquire,
|
|
||||||
.monotonic,
|
|
||||||
) != null) {
|
|
||||||
// Timer must've been canceled.
|
|
||||||
cpu.lock.release_no_ipl();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = cpu.timers.pop();
|
_ = cpu.timers.pop();
|
||||||
cpu.lock.release_no_ipl();
|
cpu.lock.release_no_ipl();
|
||||||
@@ -222,12 +216,11 @@ fn handle_expiry(_: *ke.Dpc, _: ?*anyopaque) void {
|
|||||||
timer.hdr.signaled = 1;
|
timer.hdr.signaled = 1;
|
||||||
timer.cpu = null;
|
timer.cpu = null;
|
||||||
|
|
||||||
timer.state.store(.Stopped, .release);
|
|
||||||
|
|
||||||
// Wake whomever was waiting on the timer.
|
// Wake whomever was waiting on the timer.
|
||||||
kep.wait.satisfy_wait(&timer.hdr);
|
kep.wait.satisfy_wait(&timer.hdr);
|
||||||
timer.hdr.lock.release_no_ipl();
|
|
||||||
|
|
||||||
if (maybe_dpc) |dpc| ke.dpc.enqueue(dpc, null);
|
if (maybe_dpc) |dpc| ke.dpc.enqueue(dpc, null);
|
||||||
|
|
||||||
|
timer.state.store(.Stopped, .release);
|
||||||
|
timer.hdr.lock.release_no_ipl();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user