Lindenii Project Forge
timer: make timers NONBLOCK to prevent races Signed-off-by: Drew DeVault <sir@cmpwn.com>
use errors; use io; use rt; use time; // A callback which executes when a timer expires. export type timercb = fn(file: *file) void; // Creates a new timer. By default, this timer never expires; configure it with // [[timer_configure]]. export fn newtimer( loop: *loop, cb: *timercb, clock: time::clock, ) (*file | errors::error) = {
const fd = match (rt::timerfd_create(clock, rt::TFD_CLOEXEC)) {
const fd = match (rt::timerfd_create(clock, rt::TFD_NONBLOCK | rt::TFD_CLOEXEC)) {
case let fd: int => yield fd: io::file; case let errno: rt::errno => return errors::errno(errno); }; const file = register(loop, fd)?; file.op = op::TIMER; file.cb = cb; file_epoll_ctl(file); return file; }; // Starts a timer created with [[newtimer]] to expire after the given "delay" // and indefinitely thereafter following each interval of "repeat". Setting both // values to zero disarms the timer; setting either value non-zero arms the // timer. export fn timer_configure( timer: *file, delay: time::duration, repeat: time::duration, ) void = { assert(timer.op == op::TIMER); let spec = rt::itimerspec { it_value = time::duration_to_timespec(delay), it_interval = time::duration_to_timespec(repeat), ... }; rt::timerfd_settime(timer.fd, 0, &spec, null)!; }; fn timer_ready(timer: *file, ev: *rt::epoll_event) void = { assert(timer.op == op::TIMER); let buf: [8]u8 = [0...];
io::read(timer.fd, buf)!;
match (io::read(timer.fd, buf)) { case errors::again => // This can occur if the timer was reconfigured in an event // handler while the expiration event was pending in the same // dispatch of the event loop. Just discard the event in this // case. return; case (io::EOF | io::error) => abort(); case => void; };
assert(timer.cb != null); const cb = timer.cb: *timercb; cb(timer); };