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:
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.