hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit ddf8e23ffc25bb96791e037efa3f4c6fcf8916d4
parent 043ba68601ec7884f745b72b052ab0a1d0030e3d
Author: Vincent Dagonneau <v@vda.io>
Date:   Mon, 25 Apr 2022 16:24:33 -0400

linux/timerfd: interface for Linux's timerfd

Diffstat:
Alinux/timerfd/README | 4++++
Alinux/timerfd/timerfd.ha | 131+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mscripts/gen-stdlib | 15+++++++++++----
Mstdlib.mk | 28++++++++++++++++++++++++++++
Mtime/+linux/functions.ha | 8++++++++
5 files changed, 182 insertions(+), 4 deletions(-)

diff --git a/linux/timerfd/README b/linux/timerfd/README @@ -0,0 +1,4 @@ +This module provides support for Linux's timerfd interface. For details, +consult the Linux man page timer_create(2). + + diff --git a/linux/timerfd/timerfd.ha b/linux/timerfd/timerfd.ha @@ -0,0 +1,131 @@ +// License: MPL-2.0 +// (c) 2021 Vincent Dagonneau <v@vda.io> + +use errors; +use rt; +use time; +use io; +use endian; + +// The timer will trigger only once, after the set duration and never after. +export type oneshot = time::duration; + +// The timer will trigger once after a configured delay, then periodically at +// the given interval. +export type interval_delayed = (time::duration, time::duration); + +// The timer will trigger periodically at the given interval. +export type interval = time::duration; + +// The expiration configuration for the timer. +export type expiration = (oneshot | interval | interval_delayed); + +const empty_timerspec: rt::itimerspec = rt::itimerspec { + it_interval = rt::timespec { tv_sec = 0, tv_nsec = 0 }, + it_value = rt::timespec { tv_sec = 0, tv_nsec = 0 }, +}; + +// Creates a new timerfd. The timer is initially configured without an +// expiration; see [[set]] to configure it. +export fn new( + clockid: time::clock, + flags: int +) (io::file | errors::error) = { + match (rt::timerfd_create(clockid, flags)) { + case let fd: int => + return fd; + case let err: rt::errno => + return errors::errno(err); + }; +}; + +// Sets the expiration configuration for a timerfd, overwriting any +// previously set expiration. +export fn set( + t: io::file, + exp: expiration, + flags: int +) (void | errors::error) = { + let interval_timespec = rt::timespec { ... }; + const timerspec = match (exp) { + case let o: oneshot => + time::duration_to_timespec(o, &interval_timespec); + yield rt::itimerspec { + it_interval = rt::timespec { tv_sec = 0, tv_nsec = 0 }, + it_value = interval_timespec, + }; + case let i: interval => + time::duration_to_timespec(i, &interval_timespec); + yield rt::itimerspec { + it_interval = interval_timespec, + it_value = interval_timespec, + }; + case let id: interval_delayed => + let delay_timespec = rt::timespec { ... }; + time::duration_to_timespec(id.0, &interval_timespec); + time::duration_to_timespec(id.1, &delay_timespec); + yield rt::itimerspec { + it_interval = interval_timespec, + it_value = delay_timespec, + }; + }; + + match (rt::timerfd_settime(t, flags, &timerspec, null)) { + case let ok: int => + return; + case let err: rt::errno => + return errors::errno(err); + }; +}; + +// Unsets any expiration that was previously set on the given timer. +export fn unset( + t: io::file, +) (void | errors::error) = { + match (rt::timerfd_settime(t, 0, &empty_timerspec, null)) { + case int => + return; + case let err: rt::errno => + return errors::errno(err); + }; +}; + +// Reading from the timerfd returns the number of time the timer expired. This +// call can be blocking or not depending on the flags passed to [[timerfd_create]]. +// Reading from a blocking unset timerfd will block forever. +export fn read( + t: io::file +) (u64 | errors::error) = { + let expirations: [8]u8 = [0...]; + io::read(t, expirations)!; + return endian::host.getu64(expirations); +}; + +@test fn timerfd() void = { + let blocking_fd = new(time::clock::MONOTONIC, 0)!; + + // one-shot blocking + // the first read should block and eventually return 1 + // subsequent reads will block indefinitely + set(blocking_fd, 100: oneshot, 0)!; + let one = read(blocking_fd)!; + assert(one == 1); + + // interval blocking + // the first read should block and eventually return the number of time + // the timer expired + // subsequent reads should return instantly with the number of time the + // timer expired since the last read + set(blocking_fd, 100: interval, 0)!; + let first = read(blocking_fd)!; + let second = read(blocking_fd)!; + + assert(first > 0); + assert(second > 0); + + // unset blocking timer + // reading here would block us forever + unset(blocking_fd)!; + + io::close(blocking_fd); +}; diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib @@ -572,7 +572,7 @@ glob() { else gen_srcs glob glob.ha +test.ha fi - gen_ssa glob fnmatch fs io os sort strings strio + gen_ssa glob fnmatch fs io os sort strings strio } hare_ast() { @@ -801,6 +801,12 @@ linux_keyctl() { gen_ssa -plinux linux::keyctl rt errors strings bytes } +linux_timerfd() { + gen_srcs -plinux linux::timerfd \ + timerfd.ha + gen_ssa -plinux linux::timerfd errors +} + linux_vdso() { gen_srcs -plinux linux::vdso \ vdso.ha @@ -1048,7 +1054,7 @@ gensrcs_strconv() { numeric.ha \ ftos.ha \ stof.ha \ - stof_data.ha \ + stof_data.ha \ $* } @@ -1261,7 +1267,7 @@ unix_signal() { unix_tty() { gen_srcs -plinux unix::tty \ - types.ha \ + types.ha \ +linux/isatty.ha \ +linux/open.ha \ +linux/termios.ha \ @@ -1269,7 +1275,7 @@ unix_tty() { gen_ssa -plinux unix::tty rt fs io os gen_srcs -pfreebsd unix::tty \ - types.ha \ + types.ha \ +freebsd/isatty.ha \ +freebsd/open.ha \ +freebsd/winsize.ha @@ -1338,6 +1344,7 @@ hash::fnv io linux freebsd linux linux linux::keyctl linux +linux::timerfd linux linux::vdso linux log linux freebsd math diff --git a/stdlib.mk b/stdlib.mk @@ -450,6 +450,10 @@ stdlib_deps_linux += $(stdlib_linux_linux) stdlib_linux_keyctl_linux = $(HARECACHE)/linux/keyctl/linux_keyctl-linux.o stdlib_deps_linux += $(stdlib_linux_keyctl_linux) +# gen_lib linux::timerfd (linux) +stdlib_linux_timerfd_linux=$(HARECACHE)/linux/timerfd/linux_timerfd-linux.o +stdlib_deps_linux+=$(stdlib_linux_timerfd_linux) + # gen_lib linux::vdso (linux) stdlib_linux_vdso_linux = $(HARECACHE)/linux/vdso/linux_vdso-linux.o stdlib_deps_linux += $(stdlib_linux_vdso_linux) @@ -1353,6 +1357,16 @@ $(HARECACHE)/linux/keyctl/linux_keyctl-linux.ssa: $(stdlib_linux_keyctl_linux_sr @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nlinux::keyctl \ -t$(HARECACHE)/linux/keyctl/linux_keyctl.td $(stdlib_linux_keyctl_linux_srcs) +# linux::timerfd (+linux) +stdlib_linux_timerfd_linux_srcs= \ + $(STDLIB)/linux/timerfd/timerfd.ha + +$(HARECACHE)/linux/timerfd/linux_timerfd-linux.ssa: $(stdlib_linux_timerfd_linux_srcs) $(stdlib_rt) $(stdlib_errors_$(PLATFORM)) + @printf 'HAREC \t$@\n' + @mkdir -p $(HARECACHE)/linux/timerfd + @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nlinux::timerfd \ + -t$(HARECACHE)/linux/timerfd/linux_timerfd.td $(stdlib_linux_timerfd_linux_srcs) + # linux::vdso (+linux) stdlib_linux_vdso_linux_srcs = \ $(STDLIB)/linux/vdso/vdso.ha @@ -2446,6 +2460,10 @@ testlib_deps_linux += $(testlib_linux_linux) testlib_linux_keyctl_linux = $(TESTCACHE)/linux/keyctl/linux_keyctl-linux.o testlib_deps_linux += $(testlib_linux_keyctl_linux) +# gen_lib linux::timerfd (linux) +testlib_linux_timerfd_linux=$(TESTCACHE)/linux/timerfd/linux_timerfd-linux.o +testlib_deps_linux+=$(testlib_linux_timerfd_linux) + # gen_lib linux::vdso (linux) testlib_linux_vdso_linux = $(TESTCACHE)/linux/vdso/linux_vdso-linux.o testlib_deps_linux += $(testlib_linux_vdso_linux) @@ -3383,6 +3401,16 @@ $(TESTCACHE)/linux/keyctl/linux_keyctl-linux.ssa: $(testlib_linux_keyctl_linux_s @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nlinux::keyctl \ -t$(TESTCACHE)/linux/keyctl/linux_keyctl.td $(testlib_linux_keyctl_linux_srcs) +# linux::timerfd (+linux) +testlib_linux_timerfd_linux_srcs= \ + $(STDLIB)/linux/timerfd/timerfd.ha + +$(TESTCACHE)/linux/timerfd/linux_timerfd-linux.ssa: $(testlib_linux_timerfd_linux_srcs) $(testlib_rt) $(testlib_errors_$(PLATFORM)) + @printf 'HAREC \t$@\n' + @mkdir -p $(TESTCACHE)/linux/timerfd + @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nlinux::timerfd \ + -t$(TESTCACHE)/linux/timerfd/linux_timerfd.td $(testlib_linux_timerfd_linux_srcs) + # linux::vdso (+linux) testlib_linux_vdso_linux_srcs = \ $(STDLIB)/linux/vdso/vdso.ha diff --git a/time/+linux/functions.ha b/time/+linux/functions.ha @@ -70,6 +70,14 @@ export type clock = enum { // Time since the system was booted. Increases monotonically and, unlike // [[MONOTONIC]], continues to tick while the system is suspended. BOOT = 7, + + // This clock is like [[REALTIME]], but will wake the system if it is suspended. + REALTIME_ALARM = 8, + // This clock is like [[BOOT]], but will wake the system if it is suspended. + BOOT_ALARM = 9, + + // A system-wide clock derived from wall-clock time but ignoring leap seconds. + TAI = 11, }; fn cgt_vdso() nullable *fn(_: int, _: *rt::timespec) int = {