hare

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

commit d12fca83c4680da1cd29ddc0341499f5c0a0d9ca
parent 1b10148532950e0fa9684a153966e18148c8f557
Author: Drew DeVault <sir@cmpwn.com>
Date:   Wed, 13 Mar 2024 12:08:12 +0100

unix::signal: add sigwait(2) family of functions

Signed-off-by: Drew DeVault <sir@cmpwn.com>

Diffstat:
Mrt/+freebsd/signal.ha | 26++++++++++++++++++++++++++
Mrt/+linux/signal.ha | 23+++++++++++++++++++++++
Mrt/+openbsd/signal.ha | 15+++++++++++++++
Mtime/+freebsd/functions.ha | 7+++++++
Mtime/+linux/functions.ha | 7+++++++
Mtime/+openbsd/functions.ha | 7+++++++
Munix/signal/+freebsd.ha | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Munix/signal/+linux.ha | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Munix/signal/+openbsd.ha | 19+++++++++++++++++++
9 files changed, 265 insertions(+), 0 deletions(-)

diff --git a/rt/+freebsd/signal.ha b/rt/+freebsd/signal.ha @@ -41,6 +41,32 @@ export fn setitimer( oldval: uintptr: u64))?; }; +export fn sigwait(set: *sigset, sig: *int) (void | errno) = { + *sig = sigwaitinfo(set, null)?; +}; + +export fn sigwaitinfo( + set: *sigset, + info: nullable *siginfo, +) (int | errno) = { + return wrap_return(syscall2(SYS_sigwaitinfo, + set: uintptr: u64, + info: uintptr: u64, + ))?: int; +}; + +export fn sigtimedwait( + set: *sigset, + info: nullable *siginfo, + timeout: nullable *timespec, +) (int | errno) = { + return wrap_return(syscall3(SYS_sigtimedwait, + set: uintptr: u64, + info: uintptr: u64, + timeout: uintptr: u64, + ))?: int; +}; + export fn sigemptyset(set: *sigset) void = { for (let i = 0z; i < len(set.__bits); i += 1) { set.__bits[i] = 0; diff --git a/rt/+linux/signal.ha b/rt/+linux/signal.ha @@ -34,6 +34,29 @@ export fn setitimer( oldval: uintptr: u64))?; }; +export fn sigwait(set: *sigset, sig: *int) (void | errno) = { + *sig = sigtimedwait(set, null, null)?; +}; + +export fn sigwaitinfo( + set: *sigset, + info: nullable *siginfo, +) (int | errno) = { + return sigtimedwait(set, info, null); +}; + +export fn sigtimedwait( + set: *sigset, + info: nullable *siginfo, + timeout: nullable *timespec, +) (int | errno) = { + return wrap_return(syscall3(SYS_rt_sigtimedwait, + set: uintptr: u64, + info: uintptr: u64, + timeout: uintptr: u64, + ))?: int; +}; + export fn sigemptyset(set: *sigset) void = { set.__val[0] = 0; }; diff --git a/rt/+openbsd/signal.ha b/rt/+openbsd/signal.ha @@ -1,6 +1,21 @@ // SPDX-License-Identifier: MPL-2.0 // (c) Hare authors <https://harelang.org> +// XXX: sigwaitinfo and sigtimedwait are not yet available in OpenBSD. Quoting +// from OpenBSD's lib/libc/gen/sigwait.c: "need kernel to fill in more siginfo_t +// bits first" + +// sigwait + +@symbol("sigwait") fn libc_sigwait(set: *sigset, sig: *int) int; + +export fn sigwait(set: *sigset, sig: *int) (void | errno) = { + let res = libc_sigwait(set, sig); + if (res != -1) { + return *__errno(): errno; + }; +}; + export fn alarm(sec: uint) uint = { let nval = itimerval { ... }; let oval = itimerval { ... }; diff --git a/time/+freebsd/functions.ha b/time/+freebsd/functions.ha @@ -10,6 +10,13 @@ export fn duration_to_timespec(n: duration) rt::timespec = rt::timespec { tv_nsec = n % SECOND, }; +// Converts a [[duration]] to an [[rt::timeval]]. This function is +// non-portable. +export fn duration_to_timeval(d: duration) rt::timeval = rt::timeval { + tv_sec = d / SECOND, + tv_usec = d % SECOND / 1000, +}; + // Converts an [[instant]] to an [[rt::timespec]]. This function is // non-portable. export fn instant_to_timespec(t: instant) rt::timespec = rt::timespec { diff --git a/time/+linux/functions.ha b/time/+linux/functions.ha @@ -11,6 +11,13 @@ export fn duration_to_timespec(d: duration) rt::timespec = rt::timespec { tv_nsec = d % SECOND, }; +// Converts a [[duration]] to an [[rt::timeval]]. This function is +// non-portable. +export fn duration_to_timeval(d: duration) rt::timeval = rt::timeval { + tv_sec = d / SECOND, + tv_usec = d % SECOND / 1000, +}; + // Converts an [[instant]] to an [[rt::timespec]]. This function is // non-portable. export fn instant_to_timespec(t: instant) rt::timespec = rt::timespec { diff --git a/time/+openbsd/functions.ha b/time/+openbsd/functions.ha @@ -10,6 +10,13 @@ export fn duration_to_timespec(n: duration) rt::timespec = rt::timespec { tv_nsec = n % SECOND, }; +// Converts a [[duration]] to an [[rt::timeval]]. This function is +// non-portable. +export fn duration_to_timeval(d: duration) rt::timeval = rt::timeval { + tv_sec = d / SECOND, + tv_usec = d % SECOND / 1000, +}; + // Converts an [[instant]] to an [[rt::timespec]]. This function is // non-portable. export fn instant_to_timespec(t: instant) rt::timespec = rt::timespec { diff --git a/unix/signal/+freebsd.ha b/unix/signal/+freebsd.ha @@ -1,8 +1,10 @@ // SPDX-License-Identifier: MPL-2.0 // (c) Hare authors <https://harelang.org> +use errors; use io; use rt; +use time; use unix; // Requests that [[sig::ALRM]] is delivered to the calling process in (about) @@ -194,6 +196,89 @@ export fn sigset_member(set: *sigset, item: sig) bool = { return rt::sigismember(set: *rt::sigset, item)!; }; +// Waits for a signal among the given [[sigset]] to be delivered, then returns +// the signal number. +// +// If a signal is received while waiting, [[errors::interrupted]] is returned. +// Most consumers of this function will likely wish to block all signals and +// handle them exclusively through [[wait]] et al, in which case this error +// cannot occur. +// +// See also [[waitinfo]] and [[timedwait]]. +export fn wait(set: *sigset) (sig | errors::interrupted) = { + let signal = 0i; + match (rt::sigwait(set: *rt::sigset, &signal)) { + case let err: rt::errno => + assert(err == rt::EINTR); + return errors::interrupted; + case void => + return signal: sig; + }; +}; + +// Waits for a signal among the given [[sigset]] to be delivered, then returns +// the corresponding [[siginfo]] data. +// +// See notes on [[wait]] regarding the [[errors::interrupted]] case. +// +// This function is designed to provide the portable subset of the semantics of +// sigwaitinfo(3) as defined by POSIX.1-2008. To access the complete siginfo_t +// structure provided by the underlying platform, use [[rt::sigwaitinfo]] and +// [[rt::siginfo_t]] directly. +// +// Note that this function is not supported on OpenBSD. +export fn waitinfo(set: *sigset) (siginfo | errors::interrupted) = { + let info = rt::siginfo { + si_addr = null: *opaque, + si_value = rt::sigval { ... }, + ... + }; + match (rt::sigwaitinfo(set: *rt::sigset, &info)) { + case let err: rt::errno => + assert(err == rt::EINTR); + return errors::interrupted; + case int => + return *(&info: *siginfo); + }; +}; + +// Waits for a signal among the given [[sigset]] to be delivered, then returns +// the corresponding [[siginfo]] data. +// +// Returns a [[siginfo]] if a signal is successfully processed through this +// function, or [[errors::again]] if the timeout expired. See notes on [[wait]] +// regarding the [[errors::interrupted]] case. +// +// This function is designed to provide the portable subset of the semantics of +// sigtimedwait(3) as defined by POSIX.1-2008. To access the complete siginfo_t +// structure provided by the underlying platform, use [[rt::sigtimedwait]] and +// [[rt::siginfo_t]] directly. +// +// Note that this function is not supported on OpenBSD. +export fn timedwait( + set: *sigset, + timeout: time::duration, +) (siginfo | errors::interrupted | errors::again) = { + let info = rt::siginfo { + si_addr = null: *opaque, + si_value = rt::sigval { ... }, + ... + }; + let to = time::duration_to_timeval(timeout); + match (rt::sigwaitinfo(set: *rt::sigset, &info)) { + case let err: rt::errno => + switch (err) { + case rt::EINTR => + return errors::interrupted; + case rt::EAGAIN => + return errors::again; + case => abort(); + }; + case int => + return *(&info: *siginfo); + }; +}; + // Provides additional information about signal deliveries. Only the members // defined by POSIX are available here; cast to [[rt::siginfo]] to access // non-portable members. diff --git a/unix/signal/+linux.ha b/unix/signal/+linux.ha @@ -4,6 +4,7 @@ use errors; use io; use rt; +use time; use unix; // Requests that [[sig::ALRM]] is delivered to the calling process in (about) @@ -196,6 +197,81 @@ export fn sigset_member(set: *sigset, item: sig) bool = { return rt::sigismember(set: *rt::sigset, item)!; }; +// Waits for a signal among the given [[sigset]] to be delivered, then returns +// the signal number. +// +// If a signal is received while waiting, [[errors::interrupted]] is returned. +// Most consumers of this function will likely wish to block all signals and +// handle them exclusively through [[wait]] et al, in which case this error +// cannot occur. +// +// See also [[waitinfo]] and [[timedwait]]. +export fn wait(set: *sigset) (sig | errors::interrupted) = { + let signal = 0i; + match (rt::sigwait(set: *rt::sigset, &signal)) { + case let err: rt::errno => + assert(err == rt::EINTR); + return errors::interrupted; + case void => + return signal: sig; + }; +}; + +// Waits for a signal among the given [[sigset]] to be delivered, then returns +// the corresponding [[siginfo]] data. +// +// See notes on [[wait]] regarding the [[errors::interrupted]] case. +// +// This function is designed to provide the portable subset of the semantics of +// sigwaitinfo(3) as defined by POSIX.1-2008. To access the complete siginfo_t +// structure provided by the underlying platform, use [[rt::sigwaitinfo]] and +// [[rt::siginfo_t]] directly. +// +// Note that this function is not supported on OpenBSD. +export fn waitinfo(set: *sigset) (siginfo | errors::interrupted) = { + let info = rt::siginfo { ... }; + match (rt::sigwaitinfo(set: *rt::sigset, &info)) { + case let err: rt::errno => + assert(err == rt::EINTR); + return errors::interrupted; + case int => + return *(&info: *siginfo); + }; +}; + +// Waits for a signal among the given [[sigset]] to be delivered, then returns +// the corresponding [[siginfo]] data. +// +// Returns a [[siginfo]] if a signal is successfully processed through this +// function, or [[errors::again]] if the timeout expired. See notes on [[wait]] +// regarding the [[errors::interrupted]] case. +// +// This function is designed to provide the portable subset of the semantics of +// sigtimedwait(3) as defined by POSIX.1-2008. To access the complete siginfo_t +// structure provided by the underlying platform, use [[rt::sigtimedwait]] and +// [[rt::siginfo_t]] directly. +// +// Note that this function is not supported on OpenBSD. +export fn timedwait( + set: *sigset, + timeout: time::duration, +) (siginfo | errors::interrupted | errors::again) = { + let info = rt::siginfo { ... }; + let to = time::duration_to_timeval(timeout); + match (rt::sigwaitinfo(set: *rt::sigset, &info)) { + case let err: rt::errno => + switch (err) { + case rt::EINTR => + return errors::interrupted; + case rt::EAGAIN => + return errors::again; + case => abort(); + }; + case int => + return *(&info: *siginfo); + }; +}; + // Provides additional information about signal deliveries. Only the members // defined by POSIX are available here; cast to [[rt::siginfo]] to access // non-portable members. diff --git a/unix/signal/+openbsd.ha b/unix/signal/+openbsd.ha @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MPL-2.0 // (c) Hare authors <https://harelang.org> +use errors; use io; use rt; use unix; @@ -193,6 +194,24 @@ export fn sigset_member(set: *sigset, item: sig) bool = { return rt::sigismember(set: *rt::sigset, item)!; }; +// Waits for a signal among the given [[sigset]] to be delivered, then returns +// the signal number. +// +// If a signal is received while waiting, [[errors::interrupted]] is returned. +// Most consumers of this function will likely wish to block all signals and +// handle them exclusively through [[wait]] et al, in which case this error +// cannot occur. +export fn wait(set: *sigset) (sig | errors::interrupted) = { + let signal = 0i; + match (rt::sigwait(set: *rt::sigset, &signal)) { + case let err: rt::errno => + assert(err == rt::EINTR); + return errors::interrupted; + case void => + return signal: sig; + }; +}; + // Provides additional information about signal deliveries. Only the members // defined by POSIX are available here; cast to [[rt::siginfo]] to access // non-portable members.