Lindenii Project Forge
Login

hare-ev

Temporary fork of hare-ev for... reasons

Warning: Due to various recent migrations, viewing non-HEAD refs may be broken.

/ev/+linux/timers.ha (raw)

use errors;
use io;
use rt;
use time;

// A callback which executes when a timer expires.
export type timercb = fn(file: *file) (void | nomem);

// 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 | nomem) = {
	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 | nomem) = {
	assert(timer.op == op::TIMER);
	let buf: [8]u8 = [0...];
	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)?;
};