mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-06 03:08:31 +00:00
Introduce cosmo_futex_wait and cosmo_futex_wake
Cosmopolitan Futexes are now exposed as a public API.
This commit is contained in:
parent
729f7045e3
commit
9ddbfd921e
66 changed files with 886 additions and 917 deletions
|
@ -30,9 +30,11 @@ LIBC_INTRIN_A_CHECKS = \
|
|||
LIBC_INTRIN_A_DIRECTDEPS = \
|
||||
LIBC_NEXGEN32E \
|
||||
LIBC_NT_KERNEL32 \
|
||||
LIBC_NT_REALTIME \
|
||||
LIBC_NT_SYNCHRONIZATION \
|
||||
LIBC_NT_WS2_32 \
|
||||
LIBC_SYSV \
|
||||
LIBC_SYSV_CALLS
|
||||
LIBC_SYSV_CALLS \
|
||||
|
||||
LIBC_INTRIN_A_DEPS := \
|
||||
$(call uniq,$(foreach x,$(LIBC_INTRIN_A_DIRECTDEPS),$($(x))))
|
||||
|
@ -106,6 +108,16 @@ o//libc/intrin/demangle.o: private \
|
|||
CFLAGS += \
|
||||
-mgeneral-regs-only
|
||||
|
||||
# ensure that division is optimized
|
||||
o/$(MODE)/libc/intrin/windowsdurationtotimeval.o \
|
||||
o/$(MODE)/libc/intrin/windowsdurationtotimespec.o \
|
||||
o/$(MODE)/libc/intrin/timevaltowindowstime.o \
|
||||
o/$(MODE)/libc/intrin/timespectowindowstime.o \
|
||||
o/$(MODE)/libc/intrin/windowstimetotimeval.o \
|
||||
o/$(MODE)/libc/intrin/windowstimetotimespec.o: private \
|
||||
CFLAGS += \
|
||||
-O2
|
||||
|
||||
# these assembly files are safe to build on aarch64
|
||||
o/$(MODE)/libc/intrin/aarch64/%.o: libc/intrin/aarch64/%.S
|
||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||
|
|
36
libc/intrin/checkcancel.c
Normal file
36
libc/intrin/checkcancel.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
|
||||
textwindows bool _is_canceled(void) {
|
||||
struct PosixThread *pt;
|
||||
return _weaken(_pthread_cancel_ack) && (pt = _pthread_self()) &&
|
||||
atomic_load_explicit(&pt->pt_canceled, memory_order_acquire) &&
|
||||
!(pt->pt_flags & PT_NOCANCEL);
|
||||
}
|
||||
|
||||
textwindows int _check_cancel(void) {
|
||||
if (_is_canceled())
|
||||
// once acknowledged _is_canceled() will return false
|
||||
return _weaken(_pthread_cancel_ack)();
|
||||
return 0;
|
||||
}
|
80
libc/intrin/clock_gettime-mono.c
Normal file
80
libc/intrin/clock_gettime-mono.c
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/nexgen32e/rdtsc.h"
|
||||
|
||||
/**
|
||||
* @fileoverview Monotonic clock polyfill.
|
||||
*
|
||||
* This isn't quite `CLOCK_MONOTONIC` and isn't quite `CLOCK_BOOTTIME`
|
||||
* either; however it is fast and almost always goes in one direction.
|
||||
*
|
||||
* Intel architecture guarantees that a mapping exists between rdtsc &
|
||||
* nanoseconds only if the cpu advertises invariant timestamps support
|
||||
* however this shouldn't matter for a monotonic clock since we really
|
||||
* don't want to have it tick while suspended. Sadly that shall happen
|
||||
* since nearly all x86 microprocessors support invariant tsc which is
|
||||
* why we try to avoid this fallback when possible.
|
||||
*/
|
||||
|
||||
int sys_sysctl(int *, unsigned, void *, size_t *, void *, size_t) libcesque;
|
||||
|
||||
static struct {
|
||||
atomic_uint once;
|
||||
unsigned long base;
|
||||
struct timespec boot;
|
||||
} g_mono;
|
||||
|
||||
static struct timespec get_boot_time_xnu(void) {
|
||||
struct timeval t;
|
||||
size_t n = sizeof(t);
|
||||
int mib[] = {1 /* CTL_KERN */, 21 /* KERN_BOOTTIME */};
|
||||
if (sys_sysctl(mib, 2, &t, &n, 0, 0) == -1)
|
||||
__builtin_trap();
|
||||
return timeval_totimespec(t);
|
||||
}
|
||||
|
||||
static void sys_clock_gettime_mono_init(void) {
|
||||
g_mono.base = rdtsc();
|
||||
if (IsXnu()) {
|
||||
g_mono.boot = get_boot_time_xnu();
|
||||
} else {
|
||||
__builtin_trap();
|
||||
}
|
||||
}
|
||||
|
||||
int sys_clock_gettime_mono(struct timespec *time) {
|
||||
uint64_t nanos;
|
||||
uint64_t cycles;
|
||||
cosmo_once(&g_mono.once, sys_clock_gettime_mono_init);
|
||||
// ensure we get the full 64 bits of counting, which avoids wraparound
|
||||
cycles = rdtsc() - g_mono.base;
|
||||
// this is a crude approximation, that's worked reasonably well so far
|
||||
// only the kernel knows the actual mapping between rdtsc and nanosecs
|
||||
// which we could attempt to measure ourselves using clock_gettime but
|
||||
// we'd need to impose 100 ms of startup latency for a guess this good
|
||||
nanos = cycles / 3;
|
||||
*time = timespec_add(g_mono.boot, timespec_fromnanos(nanos));
|
||||
return 0;
|
||||
}
|
117
libc/intrin/clock_gettime-nt.c
Normal file
117
libc/intrin/clock_gettime-nt.c
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/wintime.internal.h"
|
||||
#include "libc/nt/accounting.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/nt/time.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
#define _CLOCK_REALTIME 0
|
||||
#define _CLOCK_MONOTONIC 1
|
||||
#define _CLOCK_REALTIME_COARSE 2
|
||||
#define _CLOCK_BOOTTIME 3
|
||||
#define _CLOCK_PROCESS_CPUTIME_ID 4
|
||||
#define _CLOCK_THREAD_CPUTIME_ID 5
|
||||
#define _CLOCK_MONOTONIC_COARSE 6
|
||||
|
||||
textwindows int sys_clock_gettime_nt(int clock, struct timespec *ts) {
|
||||
uint64_t hectons;
|
||||
struct NtFileTime ft, ftExit, ftUser, ftKernel, ftCreation;
|
||||
switch (clock) {
|
||||
case _CLOCK_REALTIME:
|
||||
GetSystemTimePreciseAsFileTime(&ft);
|
||||
*ts = FileTimeToTimeSpec(ft);
|
||||
return 0;
|
||||
case _CLOCK_REALTIME_COARSE:
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
*ts = FileTimeToTimeSpec(ft);
|
||||
return 0;
|
||||
case _CLOCK_MONOTONIC:
|
||||
//
|
||||
// "If you need a higher resolution timer, use the
|
||||
// QueryUnbiasedInterruptTime function, a multimedia timer, or a
|
||||
// high-resolution timer. The elapsed time retrieved by the
|
||||
// QueryUnbiasedInterruptTime function includes only time that
|
||||
// the system spends in the working state."
|
||||
//
|
||||
// —Quoth MSDN § Windows Time
|
||||
//
|
||||
QueryUnbiasedInterruptTimePrecise(&hectons);
|
||||
*ts = timespec_fromnanos(hectons * 100);
|
||||
return 0;
|
||||
case _CLOCK_MONOTONIC_COARSE:
|
||||
//
|
||||
// "QueryUnbiasedInterruptTimePrecise is similar to the
|
||||
// QueryUnbiasedInterruptTime routine, but is more precise. The
|
||||
// interrupt time reported by QueryUnbiasedInterruptTime is based
|
||||
// on the latest tick of the system clock timer. The system clock
|
||||
// timer is the hardware timer that periodically generates
|
||||
// interrupts for the system clock. The uniform period between
|
||||
// system clock timer interrupts is referred to as a system clock
|
||||
// tick, and is typically in the range of 0.5 milliseconds to
|
||||
// 15.625 milliseconds, depending on the hardware platform. The
|
||||
// interrupt time value retrieved by QueryUnbiasedInterruptTime
|
||||
// is accurate within a system clock tick. ¶To provide a system
|
||||
// time value that is more precise than that of
|
||||
// QueryUnbiasedInterruptTime, QueryUnbiasedInterruptTimePrecise
|
||||
// reads the timer hardware directly, therefore a
|
||||
// QueryUnbiasedInterruptTimePrecise call can be slower than a
|
||||
// QueryUnbiasedInterruptTime call."
|
||||
//
|
||||
// —Quoth MSDN § QueryUnbiasedInterruptTimePrecise
|
||||
//
|
||||
QueryUnbiasedInterruptTime(&hectons);
|
||||
*ts = timespec_fromnanos(hectons * 100);
|
||||
return 0;
|
||||
case _CLOCK_BOOTTIME:
|
||||
//
|
||||
// "Unbiased interrupt-time means that only time that the system
|
||||
// is in the working state is counted; therefore, the interrupt
|
||||
// time count is not "biased" by time the system spends in sleep
|
||||
// or hibernation."
|
||||
//
|
||||
// —Quoth MSDN § Interrupt Time
|
||||
//
|
||||
QueryInterruptTimePrecise(&hectons);
|
||||
*ts = timespec_fromnanos(hectons * 100);
|
||||
return 0;
|
||||
case _CLOCK_PROCESS_CPUTIME_ID:
|
||||
GetProcessTimes(GetCurrentProcess(), &ftCreation, &ftExit, &ftKernel,
|
||||
&ftUser);
|
||||
*ts = WindowsDurationToTimeSpec(ReadFileTime(ftUser) +
|
||||
ReadFileTime(ftKernel));
|
||||
return 0;
|
||||
case _CLOCK_THREAD_CPUTIME_ID:
|
||||
GetThreadTimes(GetCurrentThread(), &ftCreation, &ftExit, &ftKernel,
|
||||
&ftUser);
|
||||
*ts = WindowsDurationToTimeSpec(ReadFileTime(ftUser) +
|
||||
ReadFileTime(ftKernel));
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // __x86_64__
|
25
libc/intrin/clock_gettime-sysv.c
Normal file
25
libc/intrin/clock_gettime-sysv.c
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/calls/syscall_support-sysv.internal.h"
|
||||
#include "libc/sysv/consts/nr.h"
|
||||
|
||||
int sys_clock_gettime(int clock, struct timespec *ts) {
|
||||
return __syscall2i(clock, (long)ts, __NR_clock_gettime);
|
||||
}
|
66
libc/intrin/clock_gettime-xnu.c
Normal file
66
libc/intrin/clock_gettime-xnu.c
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/calls/struct/timeval.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/macros.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
int sys_clock_gettime_xnu(int clock, struct timespec *ts) {
|
||||
long ax, dx;
|
||||
if (clock == CLOCK_REALTIME) {
|
||||
// invoke the system call
|
||||
//
|
||||
// int gettimeofday(struct timeval *tp,
|
||||
// struct timezone *tzp,
|
||||
// uint64_t *mach_absolute_time);
|
||||
//
|
||||
// as follows
|
||||
//
|
||||
// ax, dx = gettimeofday(&ts, 0, 0);
|
||||
//
|
||||
// to support multiple calling conventions
|
||||
//
|
||||
// 1. new xnu returns *ts in memory via rdi
|
||||
// 2. old xnu returns *ts in rax:rdx regs
|
||||
//
|
||||
// we assume this system call always succeeds
|
||||
asm volatile("syscall"
|
||||
: "=a"(ax), "=d"(dx)
|
||||
: "0"(0x2000000 | 116), "D"(ts), "S"(0), "1"(0)
|
||||
: "rcx", "r8", "r9", "r10", "r11", "memory");
|
||||
if (ax) {
|
||||
ts->tv_sec = ax;
|
||||
ts->tv_nsec = dx;
|
||||
}
|
||||
ts->tv_nsec *= 1000;
|
||||
return 0;
|
||||
} else if (clock == CLOCK_BOOTTIME || //
|
||||
clock == CLOCK_MONOTONIC || //
|
||||
clock == CLOCK_MONOTONIC_COARSE) {
|
||||
return sys_clock_gettime_mono(ts);
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
165
libc/intrin/clock_gettime.c
Normal file
165
libc/intrin/clock_gettime.c
Normal file
|
@ -0,0 +1,165 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=8 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/calls/syscall_support-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/describeflags.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/runtime/syslib.internal.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
|
||||
#ifdef __aarch64__
|
||||
#define CGT_VDSO __vdsosym("LINUX_2.6.39", "__kernel_clock_gettime")
|
||||
#else
|
||||
#define CGT_VDSO __vdsosym("LINUX_2.6", "__vdso_clock_gettime")
|
||||
#endif
|
||||
|
||||
typedef int clock_gettime_f(int, struct timespec *);
|
||||
|
||||
static clock_gettime_f *__clock_gettime_get(void) {
|
||||
clock_gettime_f *cgt;
|
||||
if (IsLinux() && (cgt = CGT_VDSO)) {
|
||||
return cgt;
|
||||
} else if (__syslib) {
|
||||
return (void *)__syslib->__clock_gettime;
|
||||
#ifdef __x86_64__
|
||||
} else if (IsWindows()) {
|
||||
return sys_clock_gettime_nt;
|
||||
} else if (IsXnu()) {
|
||||
return sys_clock_gettime_xnu;
|
||||
#endif
|
||||
} else {
|
||||
return sys_clock_gettime;
|
||||
}
|
||||
}
|
||||
|
||||
static int __clock_gettime_init(int, struct timespec *);
|
||||
static clock_gettime_f *__clock_gettime = __clock_gettime_init;
|
||||
static int __clock_gettime_init(int clockid, struct timespec *ts) {
|
||||
clock_gettime_f *cgt;
|
||||
__clock_gettime = cgt = __clock_gettime_get();
|
||||
return cgt(clockid, ts);
|
||||
}
|
||||
|
||||
static int clock_gettime_impl(int clock, struct timespec *ts) {
|
||||
// BSDs and sometimes Linux too will crash when `ts` is NULL
|
||||
// it's also nice to not have to check for null in polyfills
|
||||
struct timespec memory;
|
||||
if (!ts)
|
||||
ts = &memory;
|
||||
return __clock_gettime(clock, ts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns nanosecond time.
|
||||
*
|
||||
* The `clock` parameter may bo set to:
|
||||
*
|
||||
* - `CLOCK_REALTIME` returns a wall clock timestamp represented in
|
||||
* nanoseconds since the UNIX epoch (~1970). It'll count time in the
|
||||
* suspend state. This clock is subject to being smeared by various
|
||||
* adjustments made by NTP. These timestamps can have unpredictable
|
||||
* discontinuous jumps when clock_settime() is used. Therefore this
|
||||
* clock is the default clock for everything, even pthread condition
|
||||
* variables. Cosmopoiltan guarantees this clock will never raise
|
||||
* `EINVAL` and also guarantees `CLOCK_REALTIME == 0` will always be
|
||||
* the case. On Windows this maps to GetSystemTimePreciseAsFileTime().
|
||||
* On platforms with vDSOs like Linux, Windows, and MacOS ARM64 this
|
||||
* should take about 20 nanoseconds.
|
||||
*
|
||||
* - `CLOCK_MONOTONIC` returns a timestamp with an unspecified epoch,
|
||||
* that should be when the system was powered on. These timestamps
|
||||
* shouldn't go backwards. Timestamps shouldn't count time spent in
|
||||
* the sleep, suspend, and hibernation states. These timestamps won't
|
||||
* be impacted by clock_settime(). These timestamps may be impacted by
|
||||
* frequency adjustments made by NTP. Cosmopoiltan guarantees this
|
||||
* clock will never raise `EINVAL`. MacOS and BSDs use the word
|
||||
* "uptime" to describe this clock. On Windows this maps to
|
||||
* QueryUnbiasedInterruptTimePrecise().
|
||||
*
|
||||
* - `CLOCK_BOOTTIME` is a monotonic clock returning a timestamp with an
|
||||
* unspecified epoch, that should be relative to when the host system
|
||||
* was powered on. These timestamps shouldn't go backwards. Timestamps
|
||||
* should also include time spent in a sleep, suspend, or hibernation
|
||||
* state. These timestamps aren't impacted by clock_settime(), but
|
||||
* they may be impacted by frequency adjustments made by NTP. This
|
||||
* clock will raise an `EINVAL` error on extremely old Linux distros
|
||||
* like RHEL5. MacOS and BSDs use the word "monotonic" to describe
|
||||
* this clock. On Windows this maps to QueryInterruptTimePrecise().
|
||||
*
|
||||
* - `CLOCK_MONOTONIC_RAW` returns a timestamp from an unspecified
|
||||
* epoch. These timestamps don't count time spent in the sleep,
|
||||
* suspend, and hibernation states. This clock is not impacted by
|
||||
* clock_settime(). Unlike `CLOCK_MONOTONIC` this clock is guaranteed
|
||||
* to not be impacted by frequency adjustments. Providing this level
|
||||
* of assurances may make this clock 10x slower than the monotonic
|
||||
* clock. Furthermore this clock may cause `EINVAL` to be raised if
|
||||
* running on a host system that doesn't provide those guarantees,
|
||||
* e.g. OpenBSD and MacOS on AMD64.
|
||||
*
|
||||
* - `CLOCK_REALTIME_COARSE` is the same as `CLOCK_REALTIME` except
|
||||
* it'll go faster if the host OS provides a cheaper way to read the
|
||||
* wall time. Please be warned that coarse can be really coarse.
|
||||
* Rather than nano precision, you're looking at `CLK_TCK` precision,
|
||||
* which can lag as far as 30 milliseconds behind or possibly more.
|
||||
* Cosmopolitan may fallback to `CLOCK_REALTIME` if a faster less
|
||||
* accurate clock isn't provided by the system. This clock will raise
|
||||
* an `EINVAL` error on extremely old Linux distros like RHEL5. On
|
||||
* platforms with vDSOs like Linux, Windows, and MacOS ARM64 this
|
||||
* should take about 5 nanoseconds.
|
||||
*
|
||||
* - `CLOCK_MONOTONIC_COARSE` is the same as `CLOCK_MONOTONIC` except
|
||||
* it'll go faster if the host OS provides a cheaper way to read the
|
||||
* unbiased time. Please be warned that coarse can be really coarse.
|
||||
* Rather than nano precision, you're looking at `CLK_TCK` precision,
|
||||
* which can lag as far as 30 milliseconds behind or possibly more.
|
||||
* Cosmopolitan may fallback to `CLOCK_REALTIME` if a faster less
|
||||
* accurate clock isn't provided by the system. This clock will raise
|
||||
* an `EINVAL` error on extremely old Linux distros like RHEL5. On
|
||||
* platforms with vDSOs like Linux, Windows, and MacOS ARM64 this
|
||||
* should take about 5 nanoseconds.
|
||||
*
|
||||
* - `CLOCK_PROCESS_CPUTIME_ID` returns the amount of time this process
|
||||
* was actively scheduled. This is similar to getrusage() and clock().
|
||||
*
|
||||
* - `CLOCK_THREAD_CPUTIME_ID` returns the amount of time this thread
|
||||
* was actively scheduled. This is similar to getrusage() and clock().
|
||||
*
|
||||
* @param ts is where the result is stored (or null to do clock check)
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @raise EFAULT if `ts` points to invalid memory
|
||||
* @error EINVAL if `clock` isn't supported on this system
|
||||
* @error EPERM if pledge() is in play without stdio promise
|
||||
* @error ESRCH on NetBSD if PID/TID OR'd into `clock` wasn't found
|
||||
* @see strftime(), gettimeofday()
|
||||
* @asyncsignalsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
int clock_gettime(int clock, struct timespec *ts) {
|
||||
int rc = clock_gettime_impl(clock, ts);
|
||||
if (rc) {
|
||||
errno = -rc;
|
||||
rc = -1;
|
||||
}
|
||||
TIMETRACE("clock_gettime(%s, [%s]) → %d% m", DescribeClockName(clock),
|
||||
DescribeTimespec(rc, ts), rc);
|
||||
return rc;
|
||||
}
|
26
libc/intrin/clock_gettime_monotonic_nt.c
Normal file
26
libc/intrin/clock_gettime_monotonic_nt.c
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2024 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/nt/time.h"
|
||||
|
||||
textwindows struct timespec sys_clock_gettime_monotonic_nt(void) {
|
||||
uint64_t hectons;
|
||||
QueryUnbiasedInterruptTimePrecise(&hectons);
|
||||
return timespec_fromnanos(hectons * 100);
|
||||
}
|
423
libc/intrin/cosmo_futex.c
Normal file
423
libc/intrin/cosmo_futex.c
Normal file
|
@ -0,0 +1,423 @@
|
|||
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/describeflags.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/intrin/ulock.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
#include "libc/sysv/consts/futex.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/freebsd.internal.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
// clang-format off
|
||||
|
||||
#define FUTEX_WAIT_BITS_ FUTEX_BITSET_MATCH_ANY
|
||||
|
||||
errno_t cosmo_futex_thunk (atomic_int *, int, int, const struct timespec *, int *, int);
|
||||
errno_t _futex_wake (atomic_int *, int, int) asm ("cosmo_futex_thunk");
|
||||
int sys_futex_cp (atomic_int *, int, int, const struct timespec *, int *, int);
|
||||
|
||||
static struct CosmoFutex {
|
||||
atomic_uint once;
|
||||
int FUTEX_WAIT_;
|
||||
int FUTEX_PRIVATE_FLAG_;
|
||||
int FUTEX_CLOCK_REALTIME_;
|
||||
bool is_supported;
|
||||
bool timeout_is_relative;
|
||||
} g_cosmo_futex;
|
||||
|
||||
static void cosmo_futex_init (void) {
|
||||
int e;
|
||||
atomic_int x;
|
||||
|
||||
g_cosmo_futex.FUTEX_WAIT_ = FUTEX_WAIT;
|
||||
|
||||
if (IsWindows ()) {
|
||||
g_cosmo_futex.is_supported = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsXnu ()) {
|
||||
g_cosmo_futex.is_supported = true;
|
||||
g_cosmo_futex.timeout_is_relative = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsFreebsd ()) {
|
||||
g_cosmo_futex.is_supported = true;
|
||||
g_cosmo_futex.FUTEX_PRIVATE_FLAG_ = FUTEX_PRIVATE_FLAG;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(g_cosmo_futex.is_supported = IsLinux () || IsOpenbsd ()))
|
||||
return;
|
||||
|
||||
// In our testing, we found that the monotonic clock on various
|
||||
// popular systems (such as Linux, and some BSD variants) was no
|
||||
// better behaved than the realtime clock, and routinely took
|
||||
// large steps backwards, especially on multiprocessors. Given
|
||||
// that "monotonic" doesn't seem to mean what it says,
|
||||
// implementers of cosmo_time might consider retaining the
|
||||
// simplicity of a single epoch within an address space, by
|
||||
// configuring any time synchronization mechanism (like ntp) to
|
||||
// adjust for leap seconds by adjusting the rate, rather than
|
||||
// with a backwards step.
|
||||
e = errno;
|
||||
atomic_store_explicit (&x, 0, memory_order_relaxed);
|
||||
if (IsLinux () &&
|
||||
cosmo_futex_thunk (&x, FUTEX_WAIT_BITSET | FUTEX_CLOCK_REALTIME,
|
||||
1, 0, 0, FUTEX_BITSET_MATCH_ANY) == -EAGAIN) {
|
||||
g_cosmo_futex.FUTEX_WAIT_ = FUTEX_WAIT_BITSET;
|
||||
g_cosmo_futex.FUTEX_PRIVATE_FLAG_ = FUTEX_PRIVATE_FLAG;
|
||||
g_cosmo_futex.FUTEX_CLOCK_REALTIME_ = FUTEX_CLOCK_REALTIME;
|
||||
} else if (IsOpenbsd () ||
|
||||
(IsLinux () &&
|
||||
!_futex_wake (&x, FUTEX_WAKE_PRIVATE, 1))) {
|
||||
g_cosmo_futex.FUTEX_WAIT_ = FUTEX_WAIT;
|
||||
g_cosmo_futex.FUTEX_PRIVATE_FLAG_ = FUTEX_PRIVATE_FLAG;
|
||||
g_cosmo_futex.timeout_is_relative = true;
|
||||
} else {
|
||||
g_cosmo_futex.FUTEX_WAIT_ = FUTEX_WAIT;
|
||||
g_cosmo_futex.timeout_is_relative = true;
|
||||
}
|
||||
errno = e;
|
||||
}
|
||||
|
||||
static uint32_t cosmo_time_64to32u (uint64_t duration) {
|
||||
if (duration <= -1u)
|
||||
return duration;
|
||||
return -1u;
|
||||
}
|
||||
|
||||
static int cosmo_futex_polyfill (atomic_int *w, int expect, int clock,
|
||||
struct timespec *abstime) {
|
||||
for (;;) {
|
||||
if (atomic_load_explicit (w, memory_order_acquire) != expect)
|
||||
return 0;
|
||||
if (_weaken (pthread_testcancel_np) &&
|
||||
_weaken (pthread_testcancel_np) ())
|
||||
return -ECANCELED;
|
||||
struct timespec now;
|
||||
if (clock_gettime (clock, &now))
|
||||
return -EINVAL;
|
||||
if (abstime && timespec_cmp (now, *abstime) >= 0)
|
||||
return -ETIMEDOUT;
|
||||
pthread_yield_np ();
|
||||
}
|
||||
}
|
||||
|
||||
static int cosmo_futex_wait_win32 (atomic_int *w, int expect, char pshare,
|
||||
int clock, const struct timespec *timeout,
|
||||
struct PosixThread *pt,
|
||||
sigset_t waitmask) {
|
||||
#ifdef __x86_64__
|
||||
int sig;
|
||||
bool32 ok;
|
||||
struct timespec deadline, wait, now;
|
||||
|
||||
if (timeout) {
|
||||
deadline = *timeout;
|
||||
} else {
|
||||
deadline = timespec_max;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
if (clock_gettime (clock, &now))
|
||||
return einval ();
|
||||
if (timespec_cmp (now, deadline) >= 0)
|
||||
return etimedout ();
|
||||
wait = timespec_sub (deadline, now);
|
||||
if (atomic_load_explicit (w, memory_order_acquire) != expect)
|
||||
return 0;
|
||||
if (pt) {
|
||||
if (_check_cancel () == -1)
|
||||
return -1; /* ECANCELED */
|
||||
if ((sig = __sig_get (waitmask))) {
|
||||
__sig_relay (sig, SI_KERNEL, waitmask);
|
||||
if (_check_cancel () == -1)
|
||||
return -1; /* ECANCELED */
|
||||
return eintr ();
|
||||
}
|
||||
pt->pt_blkmask = waitmask;
|
||||
atomic_store_explicit (&pt->pt_blocker, w, memory_order_release);
|
||||
}
|
||||
ok = WaitOnAddress (w, &expect, sizeof(int), cosmo_time_64to32u (timespec_tomillis (wait)));
|
||||
if (pt) {
|
||||
/* __sig_wake wakes our futex without changing `w` after enqueing signals */
|
||||
atomic_store_explicit (&pt->pt_blocker, 0, memory_order_release);
|
||||
if (ok && atomic_load_explicit (w, memory_order_acquire) == expect && (sig = __sig_get (waitmask))) {
|
||||
__sig_relay (sig, SI_KERNEL, waitmask);
|
||||
if (_check_cancel () == -1)
|
||||
return -1; /* ECANCELED */
|
||||
return eintr ();
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
return 0;
|
||||
} else {
|
||||
unassert (GetLastError () == ETIMEDOUT);
|
||||
}
|
||||
}
|
||||
#else
|
||||
return 0;
|
||||
#endif /* __x86_64__ */
|
||||
}
|
||||
|
||||
static int cosmo_futex_fix_timeout (struct timespec *memory, int clock,
|
||||
const struct timespec *abstime,
|
||||
struct timespec **result) {
|
||||
struct timespec now;
|
||||
if (!abstime) {
|
||||
*result = 0;
|
||||
return 0;
|
||||
} else if (!g_cosmo_futex.timeout_is_relative) {
|
||||
*memory = *abstime;
|
||||
*result = memory;
|
||||
return 0;
|
||||
} else {
|
||||
if (clock_gettime (clock, &now))
|
||||
return -EINVAL;
|
||||
*memory = timespec_subz (*abstime, now);
|
||||
*result = memory;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits on futex.
|
||||
*
|
||||
* This function may be used to ask the OS to park the calling thread
|
||||
* until cosmo_futex_wake() is called on the memory address `w`.
|
||||
*
|
||||
* @param w is your futex
|
||||
* @param expect is the value `*w` is expected to have on entry
|
||||
* @param pshare is `PTHREAD_PROCESS_PRIVATE` / `PTHREAD_PROCESS_SHARED`
|
||||
* @param clock is `CLOCK_MONOTONIC`, `CLOCK_REALTIME`, etc.
|
||||
* @param abstime is null to wait forever or absolute timestamp to stop
|
||||
* @return 0 on success, or -errno on error
|
||||
* @raise EINVAL on bad parameter
|
||||
* @raise EAGAIN if `*w` wasn't `expect`
|
||||
* @raise EINTR if a signal handler was called while waiting
|
||||
* @raise ECANCELED if calling thread was canceled while waiting
|
||||
*/
|
||||
int cosmo_futex_wait (atomic_int *w, int expect, char pshare,
|
||||
int clock, const struct timespec *abstime) {
|
||||
int e, rc, op;
|
||||
struct CosmoTib *tib;
|
||||
struct PosixThread *pt;
|
||||
struct timespec tsmem;
|
||||
struct timespec *timeout = 0;
|
||||
|
||||
cosmo_once (&g_cosmo_futex.once, cosmo_futex_init);
|
||||
|
||||
op = g_cosmo_futex.FUTEX_WAIT_;
|
||||
if (pshare == PTHREAD_PROCESS_PRIVATE)
|
||||
op |= g_cosmo_futex.FUTEX_PRIVATE_FLAG_;
|
||||
if (clock == CLOCK_REALTIME ||
|
||||
clock == CLOCK_REALTIME_COARSE)
|
||||
op |= g_cosmo_futex.FUTEX_CLOCK_REALTIME_;
|
||||
|
||||
if (abstime && timespec_cmp (*abstime, timespec_zero) <= 0) {
|
||||
rc = -ETIMEDOUT;
|
||||
goto Finished;
|
||||
}
|
||||
|
||||
if (atomic_load_explicit (w, memory_order_acquire) != expect) {
|
||||
rc = -EAGAIN;
|
||||
goto Finished;
|
||||
}
|
||||
|
||||
if ((rc = cosmo_futex_fix_timeout (&tsmem, clock, abstime, &timeout)))
|
||||
goto Finished;
|
||||
|
||||
LOCKTRACE ("futex(%t [%d], %s, %#x, %s) → ...",
|
||||
w, atomic_load_explicit (w, memory_order_relaxed),
|
||||
DescribeFutexOp (op), expect,
|
||||
DescribeTimespec (0, timeout));
|
||||
|
||||
tib = __get_tls();
|
||||
pt = (struct PosixThread *)tib->tib_pthread;
|
||||
|
||||
if (g_cosmo_futex.is_supported) {
|
||||
e = errno;
|
||||
if (IsWindows ()) {
|
||||
// Windows 8 futexes don't support multiple processes :(
|
||||
if (pshare) goto Polyfill;
|
||||
sigset_t m = __sig_block ();
|
||||
rc = cosmo_futex_wait_win32 (w, expect, pshare, clock, timeout, pt, m);
|
||||
__sig_unblock (m);
|
||||
} else if (IsXnu ()) {
|
||||
|
||||
/* XNU ulock (used by cosmo futexes) is an internal API, however:
|
||||
|
||||
1. Unlike GCD it's cancelable i.e. can be EINTR'd by signals
|
||||
2. We have no choice but to use ulock for joining threads
|
||||
3. Grand Central Dispatch requires a busy loop workaround
|
||||
4. ulock makes our mutexes use 20% more system time (meh)
|
||||
5. ulock makes our mutexes use 40% less wall time (good)
|
||||
6. ulock makes our mutexes use 64% less user time (woop)
|
||||
7. GCD uses Mach timestamps D: ulock just uses rel. time
|
||||
|
||||
ulock is an outstanding system call that must be used.
|
||||
gcd is not an acceptable alternative to ulock. */
|
||||
|
||||
uint32_t op, us;
|
||||
if (pshare) {
|
||||
op = UL_COMPARE_AND_WAIT_SHARED;
|
||||
} else {
|
||||
op = UL_COMPARE_AND_WAIT;
|
||||
}
|
||||
if (timeout) {
|
||||
us = cosmo_time_64to32u (timespec_tomicros (*timeout));
|
||||
} else {
|
||||
us = -1u;
|
||||
}
|
||||
rc = ulock_wait (op, w, expect, us);
|
||||
if (rc > 0) rc = 0; // don't care about #waiters
|
||||
} else if (IsFreebsd ()) {
|
||||
rc = sys_umtx_timedwait_uint (w, expect, pshare, clock, timeout);
|
||||
} else {
|
||||
if (IsOpenbsd()) {
|
||||
// OpenBSD 6.8 futex() returns errors as
|
||||
// positive numbers, without setting CF.
|
||||
// This irregularity is fixed in 7.2 but
|
||||
// unfortunately OpenBSD futex() defines
|
||||
// its own ECANCELED condition, and that
|
||||
// overlaps with our system call wrapper
|
||||
if (pt) pt->pt_flags &= ~PT_OPENBSD_KLUDGE;
|
||||
}
|
||||
rc = sys_futex_cp (w, op, expect, timeout, 0, FUTEX_WAIT_BITS_);
|
||||
if (IsOpenbsd()) {
|
||||
// Handle the OpenBSD 6.x irregularity.
|
||||
if (rc > 0) {
|
||||
errno = rc;
|
||||
rc = -1;
|
||||
}
|
||||
// Check if ECANCELED came from the kernel
|
||||
// because a SA_RESTART signal handler was
|
||||
// invoked, such as our SIGTHR callback.
|
||||
if (rc == -1 && errno == ECANCELED &&
|
||||
pt && (~pt->pt_flags & PT_OPENBSD_KLUDGE)) {
|
||||
errno = EINTR;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rc == -1) {
|
||||
rc = -errno;
|
||||
errno = e;
|
||||
}
|
||||
} else {
|
||||
Polyfill:
|
||||
rc = cosmo_futex_polyfill (w, expect, clock, timeout);
|
||||
}
|
||||
|
||||
Finished:
|
||||
STRACE ("futex(%t [%d], %s, %#x, %s) → %s",
|
||||
w, atomic_load_explicit (w, memory_order_relaxed),
|
||||
DescribeFutexOp (op), expect,
|
||||
DescribeTimespec (0, abstime),
|
||||
DescribeErrno (rc));
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wakes futex.
|
||||
*
|
||||
* @param w is your futex
|
||||
* @param count is number of threads to wake (usually 1 or `INT_MAX`)
|
||||
* @param pshare is `PTHREAD_PROCESS_PRIVATE` / `PTHREAD_PROCESS_SHARED`
|
||||
* @return number of threads woken on success, or -errno on error
|
||||
*/
|
||||
int cosmo_futex_wake (atomic_int *w, int count, char pshare) {
|
||||
int rc, op, fop;
|
||||
|
||||
cosmo_once (&g_cosmo_futex.once, cosmo_futex_init);
|
||||
|
||||
op = FUTEX_WAKE;
|
||||
if (pshare == PTHREAD_PROCESS_PRIVATE)
|
||||
op |= g_cosmo_futex.FUTEX_PRIVATE_FLAG_;
|
||||
|
||||
if (g_cosmo_futex.is_supported) {
|
||||
if (IsWindows ()) {
|
||||
if (pshare) {
|
||||
goto Polyfill;
|
||||
}
|
||||
if (count == 1) {
|
||||
WakeByAddressSingle (w);
|
||||
} else {
|
||||
WakeByAddressAll (w);
|
||||
}
|
||||
rc = 0;
|
||||
} else if (IsXnu ()) {
|
||||
uint32_t op;
|
||||
if (pshare) {
|
||||
op = UL_COMPARE_AND_WAIT_SHARED;
|
||||
} else {
|
||||
op = UL_COMPARE_AND_WAIT;
|
||||
}
|
||||
if (count > 1) {
|
||||
op |= ULF_WAKE_ALL;
|
||||
}
|
||||
rc = ulock_wake (op, w, 0);
|
||||
unassert (!rc || rc == -ENOENT);
|
||||
if (!rc) {
|
||||
rc = 1;
|
||||
} else if (rc == -ENOENT) {
|
||||
rc = 0;
|
||||
}
|
||||
} else if (IsFreebsd ()) {
|
||||
if (pshare) {
|
||||
fop = UMTX_OP_WAKE;
|
||||
} else {
|
||||
fop = UMTX_OP_WAKE_PRIVATE;
|
||||
}
|
||||
rc = _futex_wake (w, fop, count);
|
||||
} else {
|
||||
rc = _futex_wake (w, op, count);
|
||||
}
|
||||
} else {
|
||||
Polyfill:
|
||||
pthread_yield_np ();
|
||||
rc = 0;
|
||||
}
|
||||
|
||||
STRACE ("futex(%t [%d], %s, %d) → %d woken",
|
||||
w, atomic_load_explicit (w, memory_order_relaxed),
|
||||
DescribeFutexOp (op), count, rc);
|
||||
|
||||
return rc;
|
||||
}
|
|
@ -20,7 +20,7 @@
|
|||
#include "libc/macros.h"
|
||||
.privileged
|
||||
|
||||
_futex:
|
||||
cosmo_futex_thunk:
|
||||
#ifdef __x86_64__
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
|
@ -47,4 +47,4 @@ _futex:
|
|||
#error "unsupported architecture"
|
||||
#endif /* __x86_64__ */
|
||||
1: ret
|
||||
.endfn _futex,globl,hidden
|
||||
.endfn cosmo_futex_thunk,globl,hidden
|
32
libc/intrin/getcontext.S
Normal file
32
libc/intrin/getcontext.S
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│ vi: set noet ft=asm ts=8 sw=8 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.h"
|
||||
|
||||
// Gets machine state.
|
||||
//
|
||||
// @return 0 on success, or -1 w/ errno
|
||||
// @see makecontext()
|
||||
// @see swapcontext()
|
||||
// @see setcontext()
|
||||
.ftrace1
|
||||
getcontext:
|
||||
.ftrace2
|
||||
#include "libc/intrin/getcontext.inc"
|
||||
jmp __getcontextsig
|
||||
.endfn getcontext,globl
|
130
libc/intrin/getcontext.inc
Normal file
130
libc/intrin/getcontext.inc
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│ vi: set noet ft=asm ts=8 sw=8 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
|
||||
// @fileoverview textual include for loading processor state
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
mov %r8,40(%rdi)
|
||||
mov %r9,48(%rdi)
|
||||
mov %r10,56(%rdi)
|
||||
mov %r11,64(%rdi)
|
||||
mov %r12,72(%rdi)
|
||||
mov %r13,80(%rdi)
|
||||
mov %r14,88(%rdi)
|
||||
mov %r15,96(%rdi)
|
||||
mov %rdi,104(%rdi)
|
||||
mov %rsi,112(%rdi)
|
||||
mov %rbp,120(%rdi)
|
||||
mov %rbx,128(%rdi)
|
||||
mov %rdx,136(%rdi)
|
||||
mov %rcx,152(%rdi)
|
||||
lea 8(%rsp),%rax
|
||||
mov %rax,160(%rdi) // rsp = caller's rsp
|
||||
mov (%rsp),%rax
|
||||
mov %rax,168(%rdi) // rip = return address
|
||||
|
||||
lea 608(%rdi),%rax
|
||||
movaps %xmm0,-0x80(%rax)
|
||||
movaps %xmm1,-0x70(%rax)
|
||||
movaps %xmm2,-0x60(%rax)
|
||||
movaps %xmm3,-0x50(%rax)
|
||||
movaps %xmm4,-0x40(%rax)
|
||||
movaps %xmm5,-0x30(%rax)
|
||||
movaps %xmm6,-0x20(%rax)
|
||||
movaps %xmm7,-0x10(%rax)
|
||||
movaps %xmm8,0x00(%rax)
|
||||
movaps %xmm9,0x10(%rax)
|
||||
movaps %xmm10,0x20(%rax)
|
||||
movaps %xmm11,0x30(%rax)
|
||||
movaps %xmm12,0x40(%rax)
|
||||
movaps %xmm13,0x50(%rax)
|
||||
movaps %xmm14,0x60(%rax)
|
||||
movaps %xmm15,0x70(%rax)
|
||||
lea 320(%rdi),%rax // rax = &__fpustate
|
||||
mov %rax,224(%rdi) // fpregs = rax
|
||||
|
||||
#elif defined(__aarch64__)
|
||||
#define REGS(i) 184+i*8
|
||||
|
||||
// x14 and x15 are clobbered
|
||||
// all other registers are preserved
|
||||
|
||||
stp xzr,x1,[x0,REGS(0)] // context.x0 = 0
|
||||
stp x2,x3,[x0,REGS(2)]
|
||||
stp x4,x5,[x0,REGS(4)]
|
||||
stp x6,x7,[x0,REGS(6)]
|
||||
stp x8,x9,[x0,REGS(8)]
|
||||
stp x10,x11,[x0,REGS(10)]
|
||||
stp x12,x13,[x0,REGS(12)]
|
||||
stp x14,x15,[x0,REGS(14)]
|
||||
stp x16,x17,[x0,REGS(16)]
|
||||
stp x18,x19,[x0,REGS(18)]
|
||||
stp x20,x21,[x0,REGS(20)]
|
||||
stp x22,x23,[x0,REGS(22)]
|
||||
stp x24,x25,[x0,REGS(24)]
|
||||
stp x26,x27,[x0,REGS(26)]
|
||||
stp x28,x29,[x0,REGS(28)]
|
||||
str x30,[x0,REGS(30)]
|
||||
mov x15,sp
|
||||
stp x15,x30,[x0,REGS(31)] // sp, pc = caller sp, ret address
|
||||
str xzr,[x0,448] // pstate = 0
|
||||
str xzr,[x0,456] // no vectors yet
|
||||
|
||||
/*void getfpsimd(ucontext_t *uc) {
|
||||
struct fpsimd_context *fp;
|
||||
fp = (struct fpsimd_context *)uc->uc_mcontext.__reserved;
|
||||
fp[0].head.magic = FPSIMD_MAGIC;
|
||||
fp[0].head.size = sizeof(*fp);
|
||||
fp[1].head.magic = 0;
|
||||
fp[1].head.size = 0;
|
||||
asm("mrs\t%0,fpsr" : "=r"(fp->fpsr));
|
||||
asm("mrs\t%0,fpcr" : "=r"(fp->fpcr));
|
||||
asm("stp\tq0,q1,%0" : "=m"(fp->vregs[0]));
|
||||
asm("stp\tq2,q3,%0" : "=m"(fp->vregs[2]));
|
||||
asm("stp\tq4,q5,%0" : "=m"(fp->vregs[4]));
|
||||
asm("stp\tq6,q7,%0" : "=m"(fp->vregs[6]));
|
||||
asm("stp\tq8,q9,%0" : "=m"(fp->vregs[8]));
|
||||
asm("stp\tq10,q11,%0" : "=m"(fp->vregs[10]));
|
||||
asm("stp\tq12,q13,%0" : "=m"(fp->vregs[12]));
|
||||
asm("stp\tq14,q15,%0" : "=m"(fp->vregs[14]));
|
||||
}*/
|
||||
|
||||
add x15,x0,464
|
||||
mov x14,0x8001
|
||||
movk x14,0x4650,lsl 16
|
||||
str xzr,[x0,992]
|
||||
movk x14,0x210,lsl 32
|
||||
str x14,[x0,464]
|
||||
mrs x14,fpsr
|
||||
str w14,[x15,8]
|
||||
mrs x14,fpcr
|
||||
str w14,[x15,12]
|
||||
stp q0,q1,[x15,16]
|
||||
stp q2,q3,[x15,48]
|
||||
stp q4,q5,[x15,80]
|
||||
stp q6,q7,[x15,112]
|
||||
stp q8,q9,[x15,144]
|
||||
stp q10,q11,[x15,176]
|
||||
stp q12,q13,[x15,208]
|
||||
stp q14,q15,[x15,240]
|
||||
|
||||
#else
|
||||
#error "unsupported architecture"
|
||||
#endif
|
|
@ -19,6 +19,7 @@
|
|||
#include "libc/calls/blockcancel.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
|
@ -28,25 +29,8 @@
|
|||
#include "libc/runtime/internal.h"
|
||||
#include "libc/thread/lock.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "third_party/nsync/futex.internal.h"
|
||||
#include "third_party/nsync/mu.h"
|
||||
|
||||
static void pthread_mutex_lock_spin(atomic_int *word) {
|
||||
int backoff = 0;
|
||||
if (atomic_exchange_explicit(word, 1, memory_order_acquire)) {
|
||||
LOCKTRACE("acquiring pthread_mutex_lock_spin(%t)...", word);
|
||||
for (;;) {
|
||||
for (;;) {
|
||||
if (!atomic_load_explicit(word, memory_order_relaxed))
|
||||
break;
|
||||
backoff = pthread_delay_np(word, backoff);
|
||||
}
|
||||
if (!atomic_exchange_explicit(word, 1, memory_order_acquire))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// see "take 3" algorithm in "futexes are tricky" by ulrich drepper
|
||||
// slightly improved to attempt acquiring multiple times b4 syscall
|
||||
static void pthread_mutex_lock_drepper(atomic_int *futex, char pshare) {
|
||||
|
@ -59,7 +43,7 @@ static void pthread_mutex_lock_drepper(atomic_int *futex, char pshare) {
|
|||
word = atomic_exchange_explicit(futex, 2, memory_order_acquire);
|
||||
BLOCK_CANCELATION;
|
||||
while (word > 0) {
|
||||
_weaken(nsync_futex_wait_)(futex, 2, pshare, 0, 0);
|
||||
cosmo_futex_wait(futex, 2, pshare, 0, 0);
|
||||
word = atomic_exchange_explicit(futex, 2, memory_order_acquire);
|
||||
}
|
||||
ALLOW_CANCELATION;
|
||||
|
@ -164,11 +148,7 @@ static errno_t pthread_mutex_lock_impl(pthread_mutex_t *mutex) {
|
|||
|
||||
// handle normal mutexes
|
||||
if (MUTEX_TYPE(word) == PTHREAD_MUTEX_NORMAL) {
|
||||
if (_weaken(nsync_futex_wait_)) {
|
||||
pthread_mutex_lock_drepper(&mutex->_futex, MUTEX_PSHARED(word));
|
||||
} else {
|
||||
pthread_mutex_lock_spin(&mutex->_futex);
|
||||
}
|
||||
pthread_mutex_lock_drepper(&mutex->_futex, MUTEX_PSHARED(word));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,15 +24,8 @@
|
|||
#include "libc/runtime/internal.h"
|
||||
#include "libc/thread/lock.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "third_party/nsync/futex.internal.h"
|
||||
#include "third_party/nsync/mu.h"
|
||||
|
||||
static errno_t pthread_mutex_trylock_spin(atomic_int *word) {
|
||||
if (!atomic_exchange_explicit(word, 1, memory_order_acquire))
|
||||
return 0;
|
||||
return EBUSY;
|
||||
}
|
||||
|
||||
static errno_t pthread_mutex_trylock_drepper(atomic_int *futex) {
|
||||
int word = 0;
|
||||
if (atomic_compare_exchange_strong_explicit(
|
||||
|
@ -142,13 +135,8 @@ errno_t pthread_mutex_trylock(pthread_mutex_t *mutex) {
|
|||
#endif
|
||||
|
||||
// handle normal mutexes
|
||||
if (MUTEX_TYPE(word) == PTHREAD_MUTEX_NORMAL) {
|
||||
if (_weaken(nsync_futex_wait_)) {
|
||||
return pthread_mutex_trylock_drepper(&mutex->_futex);
|
||||
} else {
|
||||
return pthread_mutex_trylock_spin(&mutex->_futex);
|
||||
}
|
||||
}
|
||||
if (MUTEX_TYPE(word) == PTHREAD_MUTEX_NORMAL)
|
||||
return pthread_mutex_trylock_drepper(&mutex->_futex);
|
||||
|
||||
// handle recursive and error checking mutexes
|
||||
#if PTHREAD_USE_NSYNC
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
|
@ -26,19 +27,14 @@
|
|||
#include "libc/runtime/internal.h"
|
||||
#include "libc/thread/lock.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "third_party/nsync/futex.internal.h"
|
||||
#include "third_party/nsync/mu.h"
|
||||
|
||||
static void pthread_mutex_unlock_spin(atomic_int *word) {
|
||||
atomic_store_explicit(word, 0, memory_order_release);
|
||||
}
|
||||
|
||||
// see "take 3" algorithm in "futexes are tricky" by ulrich drepper
|
||||
static void pthread_mutex_unlock_drepper(atomic_int *futex, char pshare) {
|
||||
int word = atomic_fetch_sub_explicit(futex, 1, memory_order_release);
|
||||
if (word == 2) {
|
||||
atomic_store_explicit(futex, 0, memory_order_release);
|
||||
_weaken(nsync_futex_wake_)(futex, 1, pshare);
|
||||
cosmo_futex_wake(futex, 1, pshare);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,11 +133,7 @@ errno_t pthread_mutex_unlock(pthread_mutex_t *mutex) {
|
|||
|
||||
// implement barebones normal mutexes
|
||||
if (MUTEX_TYPE(word) == PTHREAD_MUTEX_NORMAL) {
|
||||
if (_weaken(nsync_futex_wake_)) {
|
||||
pthread_mutex_unlock_drepper(&mutex->_futex, MUTEX_PSHARED(word));
|
||||
} else {
|
||||
pthread_mutex_unlock_spin(&mutex->_futex);
|
||||
}
|
||||
pthread_mutex_unlock_drepper(&mutex->_futex, MUTEX_PSHARED(word));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
73
libc/intrin/restore.S
Normal file
73
libc/intrin/restore.S
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│ vi: set noet ft=asm ts=8 sw=8 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.h"
|
||||
.text.windows
|
||||
|
||||
// Restores thread to state before signal.
|
||||
//
|
||||
// @param rdi points to ucontext_t with machine state
|
||||
// @noreturn
|
||||
__sig_restore:
|
||||
|
||||
// restore vector registers
|
||||
lea 608(%rdi),%rax
|
||||
movaps -0x80(%rax),%xmm0
|
||||
movaps -0x70(%rax),%xmm1
|
||||
movaps -0x60(%rax),%xmm2
|
||||
movaps -0x50(%rax),%xmm3
|
||||
movaps -0x40(%rax),%xmm4
|
||||
movaps -0x30(%rax),%xmm5
|
||||
movaps -0x20(%rax),%xmm6
|
||||
movaps -0x10(%rax),%xmm7
|
||||
movaps 0x00(%rax),%xmm8
|
||||
movaps 0x10(%rax),%xmm9
|
||||
movaps 0x20(%rax),%xmm10
|
||||
movaps 0x30(%rax),%xmm11
|
||||
movaps 0x40(%rax),%xmm12
|
||||
movaps 0x50(%rax),%xmm13
|
||||
movaps 0x60(%rax),%xmm14
|
||||
movaps 0x70(%rax),%xmm15
|
||||
|
||||
// restore general registers
|
||||
lea 80(%rdi),%rax
|
||||
mov -40(%rax),%r8
|
||||
mov -32(%rax),%r9
|
||||
mov -24(%rax),%r10
|
||||
mov -16(%rax),%r11
|
||||
mov -8(%rax),%r12
|
||||
mov 0(%rax),%r13
|
||||
mov 8(%rax),%r14
|
||||
mov 16(%rax),%r15
|
||||
mov 24(%rax),%rdi
|
||||
mov 32(%rax),%rsi
|
||||
mov 48(%rax),%rbx
|
||||
mov 56(%rax),%rdx
|
||||
mov 72(%rax),%rcx
|
||||
mov 40(%rax),%rbp
|
||||
mov 80(%rax),%rsp
|
||||
|
||||
// this clobbers the red zone
|
||||
push 88(%rax) // rip
|
||||
push 64(%rax) // rax
|
||||
push 96(%rax) // flags
|
||||
popf
|
||||
pop %rax
|
||||
ret
|
||||
|
||||
.endfn __sig_restore,globl
|
|
@ -1,7 +1,7 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
|
@ -17,37 +17,701 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "ape/sections.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/siginfo.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/struct/ucontext.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/calls/ucontext.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/bsf.h"
|
||||
#include "libc/intrin/describebacktrace.h"
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/intrin/maps.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/enum/context.h"
|
||||
#include "libc/nt/enum/exceptionhandleractions.h"
|
||||
#include "libc/nt/enum/processcreationflags.h"
|
||||
#include "libc/nt/enum/signal.h"
|
||||
#include "libc/nt/enum/status.h"
|
||||
#include "libc/nt/events.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/signals.h"
|
||||
#include "libc/nt/struct/ntexceptionpointers.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/symbols.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/ss.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
struct Signals __sig;
|
||||
/**
|
||||
* @fileoverview Cosmopolitan Signals for Windows.
|
||||
*/
|
||||
|
||||
sigset_t __sig_block(void) {
|
||||
if (IsWindows() || IsMetal()) {
|
||||
if (__tls_enabled)
|
||||
return atomic_exchange_explicit(&__get_tls()->tib_sigmask, -1,
|
||||
memory_order_acquire);
|
||||
else
|
||||
#define STKSZ 65536
|
||||
|
||||
struct SignalFrame {
|
||||
unsigned rva;
|
||||
unsigned flags;
|
||||
siginfo_t si;
|
||||
ucontext_t ctx;
|
||||
};
|
||||
|
||||
static textwindows bool __sig_ignored_by_default(int sig) {
|
||||
return sig == SIGURG || //
|
||||
sig == SIGCONT || //
|
||||
sig == SIGCHLD || //
|
||||
sig == SIGWINCH;
|
||||
}
|
||||
|
||||
textwindows bool __sig_ignored(int sig) {
|
||||
return __sighandrvas[sig] == (intptr_t)SIG_IGN ||
|
||||
(__sighandrvas[sig] == (intptr_t)SIG_DFL &&
|
||||
__sig_ignored_by_default(sig));
|
||||
}
|
||||
|
||||
textwindows void __sig_delete(int sig) {
|
||||
struct Dll *e;
|
||||
atomic_fetch_and_explicit(__sig.process, ~(1ull << (sig - 1)),
|
||||
memory_order_relaxed);
|
||||
_pthread_lock();
|
||||
for (e = dll_last(_pthread_list); e; e = dll_prev(_pthread_list, e))
|
||||
atomic_fetch_and_explicit(&POSIXTHREAD_CONTAINER(e)->tib->tib_sigpending,
|
||||
~(1ull << (sig - 1)), memory_order_relaxed);
|
||||
_pthread_unlock();
|
||||
}
|
||||
|
||||
static textwindows int __sig_getter(atomic_ulong *sigs, sigset_t masked) {
|
||||
int sig;
|
||||
sigset_t bit, pending, deliverable;
|
||||
for (;;) {
|
||||
pending = atomic_load_explicit(sigs, memory_order_acquire);
|
||||
if ((deliverable = pending & ~masked)) {
|
||||
sig = bsfl(deliverable) + 1;
|
||||
bit = 1ull << (sig - 1);
|
||||
if (atomic_fetch_and_explicit(sigs, ~bit, memory_order_acq_rel) & bit)
|
||||
return sig;
|
||||
} else {
|
||||
return 0;
|
||||
} else {
|
||||
sigset_t res, neu = -1;
|
||||
sys_sigprocmask(SIG_SETMASK, &neu, &res);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void __sig_unblock(sigset_t m) {
|
||||
if (IsWindows() || IsMetal()) {
|
||||
if (__tls_enabled) {
|
||||
atomic_store_explicit(&__get_tls()->tib_sigmask, m, memory_order_release);
|
||||
if (_weaken(__sig_check))
|
||||
_weaken(__sig_check)();
|
||||
textwindows int __sig_get(sigset_t masked) {
|
||||
int sig;
|
||||
if (!(sig = __sig_getter(&__get_tls()->tib_sigpending, masked)))
|
||||
sig = __sig_getter(__sig.process, masked);
|
||||
return sig;
|
||||
}
|
||||
|
||||
static textwindows bool __sig_should_use_altstack(unsigned flags,
|
||||
struct CosmoTib *tib) {
|
||||
if (!(flags & SA_ONSTACK))
|
||||
return false; // signal handler didn't enable it
|
||||
if (!tib->tib_sigstack_size)
|
||||
return false; // sigaltstack() wasn't installed on this thread
|
||||
if (tib->tib_sigstack_flags & SS_DISABLE)
|
||||
return false; // sigaltstack() on this thread was disabled by user
|
||||
char *bp = __builtin_frame_address(0);
|
||||
if (tib->tib_sigstack_addr <= bp &&
|
||||
bp <= tib->tib_sigstack_addr + tib->tib_sigstack_size)
|
||||
return false; // we're already on the alternate stack
|
||||
return true;
|
||||
}
|
||||
|
||||
static textwindows wontreturn void __sig_terminate(int sig) {
|
||||
TerminateThisProcess(sig);
|
||||
}
|
||||
|
||||
textwindows static bool __sig_wake(struct PosixThread *pt, int sig) {
|
||||
atomic_int *blocker;
|
||||
blocker = atomic_load_explicit(&pt->pt_blocker, memory_order_acquire);
|
||||
if (!blocker)
|
||||
return false;
|
||||
// threads can create semaphores on an as-needed basis
|
||||
if (blocker == PT_BLOCKER_EVENT) {
|
||||
STRACE("%G set %d's event object", sig, _pthread_tid(pt));
|
||||
SetEvent(pt->pt_event);
|
||||
return !!atomic_load_explicit(&pt->pt_blocker, memory_order_acquire);
|
||||
}
|
||||
// all other blocking ops that aren't overlap should use futexes
|
||||
// we force restartable futexes to churn by waking w/o releasing
|
||||
STRACE("%G waking %d's futex", sig, _pthread_tid(pt));
|
||||
WakeByAddressSingle(blocker);
|
||||
return !!atomic_load_explicit(&pt->pt_blocker, memory_order_acquire);
|
||||
}
|
||||
|
||||
textwindows static bool __sig_start(struct PosixThread *pt, int sig,
|
||||
unsigned *rva, unsigned *flags) {
|
||||
*rva = __sighandrvas[sig];
|
||||
*flags = __sighandflags[sig];
|
||||
if (*rva == (intptr_t)SIG_IGN ||
|
||||
(*rva == (intptr_t)SIG_DFL && __sig_ignored_by_default(sig))) {
|
||||
STRACE("ignoring %G", sig);
|
||||
return false;
|
||||
}
|
||||
if (atomic_load_explicit(&pt->tib->tib_sigmask, memory_order_acquire) &
|
||||
(1ull << (sig - 1))) {
|
||||
STRACE("enqueing %G on %d", sig, _pthread_tid(pt));
|
||||
atomic_fetch_or_explicit(&pt->tib->tib_sigpending, 1ull << (sig - 1),
|
||||
memory_order_relaxed);
|
||||
__sig_wake(pt, sig);
|
||||
return false;
|
||||
}
|
||||
if (*rva == (intptr_t)SIG_DFL) {
|
||||
STRACE("terminating on %G due to no handler", sig);
|
||||
__sig_terminate(sig);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
textwindows static sigaction_f __sig_handler(unsigned rva) {
|
||||
atomic_fetch_add_explicit(&__sig.count, 1, memory_order_relaxed);
|
||||
return (sigaction_f)(__executable_start + rva);
|
||||
}
|
||||
|
||||
textwindows int __sig_raise(volatile int sig, int sic) {
|
||||
|
||||
// bitset of kinds of handlers called
|
||||
volatile int handler_was_called = 0;
|
||||
|
||||
// loop over pending signals
|
||||
ucontext_t ctx;
|
||||
getcontext(&ctx);
|
||||
if (!sig) {
|
||||
if ((sig = __sig_get(ctx.uc_sigmask))) {
|
||||
sic = SI_KERNEL;
|
||||
} else {
|
||||
return handler_was_called;
|
||||
}
|
||||
}
|
||||
|
||||
// process signal(s)
|
||||
unsigned rva, flags;
|
||||
struct PosixThread *pt = _pthread_self();
|
||||
if (__sig_start(pt, sig, &rva, &flags)) {
|
||||
|
||||
if (flags & SA_RESETHAND) {
|
||||
STRACE("resetting %G handler", sig);
|
||||
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
|
||||
}
|
||||
|
||||
// update the signal mask in preparation for signal handller
|
||||
sigset_t blocksigs = __sighandmask[sig];
|
||||
if (!(flags & SA_NODEFER))
|
||||
blocksigs |= 1ull << (sig - 1);
|
||||
ctx.uc_sigmask = atomic_fetch_or_explicit(&pt->tib->tib_sigmask, blocksigs,
|
||||
memory_order_acquire);
|
||||
|
||||
// call the user's signal handler
|
||||
char ssbuf[128];
|
||||
siginfo_t si = {.si_signo = sig, .si_code = sic};
|
||||
STRACE("__sig_raise(%G, %t) mask %s", sig, __sig_handler(rva),
|
||||
_DescribeSigset(ssbuf, 0, (sigset_t *)&pt->tib->tib_sigmask));
|
||||
__sig_handler(rva)(sig, &si, &ctx);
|
||||
|
||||
// record this handler
|
||||
if (flags & SA_RESTART) {
|
||||
handler_was_called |= SIG_HANDLED_SA_RESTART;
|
||||
} else {
|
||||
handler_was_called |= SIG_HANDLED_NO_RESTART;
|
||||
}
|
||||
}
|
||||
|
||||
// restore sigmask
|
||||
// loop back to top
|
||||
// jump where handler says
|
||||
sig = 0;
|
||||
return setcontext(&ctx);
|
||||
}
|
||||
|
||||
textwindows int __sig_relay(int sig, int sic, sigset_t waitmask) {
|
||||
sigset_t m;
|
||||
int handler_was_called;
|
||||
m = atomic_exchange_explicit(&__get_tls()->tib_sigmask, waitmask,
|
||||
memory_order_acquire);
|
||||
handler_was_called = __sig_raise(sig, SI_KERNEL);
|
||||
atomic_store_explicit(&__get_tls()->tib_sigmask, m, memory_order_release);
|
||||
return handler_was_called;
|
||||
}
|
||||
|
||||
// the user's signal handler callback is wrapped with this trampoline
|
||||
static textwindows wontreturn void __sig_tramp(struct SignalFrame *sf) {
|
||||
int sig = sf->si.si_signo;
|
||||
struct CosmoTib *tib = __get_tls();
|
||||
struct PosixThread *pt = (struct PosixThread *)tib->tib_pthread;
|
||||
atomic_store_explicit(&pt->pt_intoff, 0, memory_order_release);
|
||||
for (;;) {
|
||||
|
||||
// update the signal mask in preparation for signal handler
|
||||
sigset_t blocksigs = __sighandmask[sig];
|
||||
if (!(sf->flags & SA_NODEFER))
|
||||
blocksigs |= 1ull << (sig - 1);
|
||||
sf->ctx.uc_sigmask = atomic_fetch_or_explicit(&tib->tib_sigmask, blocksigs,
|
||||
memory_order_acquire);
|
||||
|
||||
// call the user's signal handler
|
||||
char ssbuf[2][128];
|
||||
STRACE("__sig_tramp(%G, %t) mask %s → %s", sig, __sig_handler(sf->rva),
|
||||
_DescribeSigset(ssbuf[0], 0, &sf->ctx.uc_sigmask),
|
||||
_DescribeSigset(ssbuf[1], 0, (sigset_t *)&tib->tib_sigmask));
|
||||
__sig_handler(sf->rva)(sig, &sf->si, &sf->ctx);
|
||||
|
||||
// restore the signal mask that was used by the interrupted code
|
||||
// this may have been modified by the signal handler in the callback
|
||||
atomic_store_explicit(&tib->tib_sigmask, sf->ctx.uc_sigmask,
|
||||
memory_order_release);
|
||||
|
||||
// jump back into original code if there aren't any pending signals
|
||||
do {
|
||||
if (!(sig = __sig_get(sf->ctx.uc_sigmask)))
|
||||
__sig_restore(&sf->ctx);
|
||||
} while (!__sig_start(pt, sig, &sf->rva, &sf->flags));
|
||||
|
||||
// tail recurse into another signal handler
|
||||
sf->si.si_signo = sig;
|
||||
sf->si.si_code = SI_KERNEL;
|
||||
if (sf->flags & SA_RESETHAND) {
|
||||
STRACE("resetting %G handler", sig);
|
||||
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
|
||||
}
|
||||
} else {
|
||||
sys_sigprocmask(SIG_SETMASK, &m, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// sends signal to another specific thread which is ref'd
|
||||
static textwindows int __sig_killer(struct PosixThread *pt, int sig, int sic) {
|
||||
unsigned rva = __sighandrvas[sig];
|
||||
unsigned flags = __sighandflags[sig];
|
||||
|
||||
// do nothing if signal is ignored
|
||||
if (rva == (intptr_t)SIG_IGN ||
|
||||
(rva == (intptr_t)SIG_DFL && __sig_ignored_by_default(sig))) {
|
||||
STRACE("ignoring %G", sig);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// we can't preempt threads that masked sigs or are blocked on i/o
|
||||
while ((atomic_load_explicit(&pt->tib->tib_sigmask, memory_order_acquire) &
|
||||
(1ull << (sig - 1)))) {
|
||||
if (atomic_fetch_or_explicit(&pt->tib->tib_sigpending, 1ull << (sig - 1),
|
||||
memory_order_acq_rel) &
|
||||
(1ull << (sig - 1)))
|
||||
// we believe signal was already enqueued
|
||||
return 0;
|
||||
if (__sig_wake(pt, sig))
|
||||
// we believe i/o routine will handle signal
|
||||
return 0;
|
||||
if (atomic_load_explicit(&pt->tib->tib_sigmask, memory_order_acquire) &
|
||||
(1ull << (sig - 1)))
|
||||
// we believe ALLOW_SIGNALS will handle signal
|
||||
return 0;
|
||||
if (!(atomic_fetch_and_explicit(&pt->tib->tib_sigpending,
|
||||
~(1ull << (sig - 1)),
|
||||
memory_order_acq_rel) &
|
||||
(1ull << (sig - 1))))
|
||||
// we believe another thread sniped our signal
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// avoid race conditions and deadlocks with thread suspend process
|
||||
if (atomic_exchange_explicit(&pt->pt_intoff, 1, memory_order_acquire)) {
|
||||
// we believe another thread is asynchronously waking the mark
|
||||
if (atomic_fetch_or_explicit(&pt->tib->tib_sigpending, 1ull << (sig - 1),
|
||||
memory_order_acq_rel) &
|
||||
(1ull << (sig - 1)))
|
||||
// we believe our signal is already being delivered
|
||||
return 0;
|
||||
if (atomic_load_explicit(&pt->pt_intoff, memory_order_acquire) ||
|
||||
atomic_exchange_explicit(&pt->pt_intoff, 1, memory_order_acquire))
|
||||
// we believe __sig_tramp will deliver our signal
|
||||
return 0;
|
||||
if (!(atomic_fetch_and_explicit(&pt->tib->tib_sigpending,
|
||||
~(1ull << (sig - 1)),
|
||||
memory_order_acq_rel) &
|
||||
(1ull << (sig - 1))))
|
||||
// we believe another thread sniped our signal
|
||||
return 0;
|
||||
}
|
||||
|
||||
// if there's no handler then killing a thread kills the process
|
||||
if (rva == (intptr_t)SIG_DFL) {
|
||||
STRACE("terminating on %G due to no handler", sig);
|
||||
__sig_terminate(sig);
|
||||
}
|
||||
|
||||
// take control of thread
|
||||
// suspending the thread happens asynchronously
|
||||
// however getting the context blocks until it's frozen
|
||||
uintptr_t th = _pthread_syshand(pt);
|
||||
if (SuspendThread(th) == -1u) {
|
||||
STRACE("SuspendThread failed w/ %d", GetLastError());
|
||||
atomic_store_explicit(&pt->pt_intoff, 0, memory_order_release);
|
||||
return ESRCH;
|
||||
}
|
||||
struct NtContext nc;
|
||||
nc.ContextFlags = kNtContextFull;
|
||||
if (!GetThreadContext(th, &nc)) {
|
||||
STRACE("GetThreadContext failed w/ %d", GetLastError());
|
||||
ResumeThread(th);
|
||||
atomic_store_explicit(&pt->pt_intoff, 0, memory_order_release);
|
||||
return ESRCH;
|
||||
}
|
||||
|
||||
// we can't preempt threads that masked sig or are blocked
|
||||
// we can't preempt threads that are running in win32 code
|
||||
// so we shall unblock the thread and let it signal itself
|
||||
if (!((uintptr_t)__executable_start <= nc.Rip &&
|
||||
nc.Rip < (uintptr_t)__privileged_start)) {
|
||||
atomic_fetch_or_explicit(&pt->tib->tib_sigpending, 1ull << (sig - 1),
|
||||
memory_order_relaxed);
|
||||
ResumeThread(th);
|
||||
atomic_store_explicit(&pt->pt_intoff, 0, memory_order_release);
|
||||
__sig_wake(pt, sig);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// preferring to live dangerously
|
||||
// the thread will be signaled asynchronously
|
||||
if (flags & SA_RESETHAND) {
|
||||
STRACE("resetting %G handler", sig);
|
||||
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
|
||||
}
|
||||
|
||||
// inject call to trampoline function into thread
|
||||
uintptr_t sp;
|
||||
if (__sig_should_use_altstack(flags, pt->tib)) {
|
||||
sp = (uintptr_t)pt->tib->tib_sigstack_addr + pt->tib->tib_sigstack_size;
|
||||
} else {
|
||||
sp = nc.Rsp;
|
||||
}
|
||||
sp -= sizeof(struct SignalFrame);
|
||||
sp &= -16;
|
||||
struct SignalFrame *sf = (struct SignalFrame *)sp;
|
||||
_ntcontext2linux(&sf->ctx, &nc);
|
||||
bzero(&sf->si, sizeof(sf->si));
|
||||
sf->rva = rva;
|
||||
sf->flags = flags;
|
||||
sf->si.si_code = sic;
|
||||
sf->si.si_signo = sig;
|
||||
*(uintptr_t *)(sp -= sizeof(uintptr_t)) = nc.Rip;
|
||||
nc.Rip = (intptr_t)__sig_tramp;
|
||||
nc.Rdi = (intptr_t)sf;
|
||||
nc.Rsp = sp;
|
||||
if (!SetThreadContext(th, &nc)) {
|
||||
STRACE("SetThreadContext failed w/ %d", GetLastError());
|
||||
atomic_store_explicit(&pt->pt_intoff, 0, memory_order_release);
|
||||
return ESRCH;
|
||||
}
|
||||
ResumeThread(th);
|
||||
__sig_wake(pt, sig);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// sends signal to another specific thread
|
||||
textwindows int __sig_kill(struct PosixThread *pt, int sig, int sic) {
|
||||
int rc;
|
||||
BLOCK_SIGNALS;
|
||||
rc = __sig_killer(pt, sig, sic);
|
||||
ALLOW_SIGNALS;
|
||||
return rc;
|
||||
}
|
||||
|
||||
// sends signal to any other thread
|
||||
// this should only be called by non-posix threads
|
||||
textwindows void __sig_generate(int sig, int sic) {
|
||||
struct Dll *e;
|
||||
struct PosixThread *pt, *mark = 0;
|
||||
if (__sig_ignored(sig)) {
|
||||
STRACE("ignoring %G", sig);
|
||||
return;
|
||||
}
|
||||
if (__sighandrvas[sig] == (intptr_t)SIG_DFL) {
|
||||
STRACE("terminating on %G due to no handler", sig);
|
||||
__sig_terminate(sig);
|
||||
}
|
||||
if (atomic_load_explicit(__sig.process, memory_order_acquire) &
|
||||
(1ull << (sig - 1))) {
|
||||
return;
|
||||
}
|
||||
_pthread_lock();
|
||||
for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) {
|
||||
pt = POSIXTHREAD_CONTAINER(e);
|
||||
// we don't want to signal ourself
|
||||
if (pt == _pthread_self())
|
||||
continue;
|
||||
// we don't want to signal a thread that isn't running
|
||||
if (atomic_load_explicit(&pt->pt_status, memory_order_acquire) >=
|
||||
kPosixThreadTerminated) {
|
||||
continue;
|
||||
}
|
||||
// choose this thread if it isn't masking sig
|
||||
if (!(atomic_load_explicit(&pt->tib->tib_sigmask, memory_order_acquire) &
|
||||
(1ull << (sig - 1)))) {
|
||||
_pthread_ref(pt);
|
||||
mark = pt;
|
||||
break;
|
||||
}
|
||||
// if a thread is blocking then we check to see if it's planning
|
||||
// to unblock our sig once the wait operation is completed; when
|
||||
// that's the case we can cancel the thread's i/o to deliver sig
|
||||
if (atomic_load_explicit(&pt->pt_blocker, memory_order_acquire) &&
|
||||
!(pt->pt_blkmask & (1ull << (sig - 1)))) {
|
||||
_pthread_ref(pt);
|
||||
mark = pt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_pthread_unlock();
|
||||
if (mark) {
|
||||
// no lock needed since current thread is nameless and formless
|
||||
__sig_killer(mark, sig, sic);
|
||||
_pthread_unref(mark);
|
||||
} else {
|
||||
atomic_fetch_or_explicit(__sig.process, 1ull << (sig - 1),
|
||||
memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows char *__sig_stpcpy(char *d, const char *s) {
|
||||
size_t i;
|
||||
for (i = 0;; ++i)
|
||||
if (!(d[i] = s[i]))
|
||||
return d + i;
|
||||
}
|
||||
|
||||
static textwindows wontreturn void __sig_death(int sig, const char *thing) {
|
||||
#ifndef TINY
|
||||
intptr_t hStderr;
|
||||
char sigbuf[21], s[128], *p;
|
||||
hStderr = GetStdHandle(kNtStdErrorHandle);
|
||||
p = __sig_stpcpy(s, "Terminating on ");
|
||||
p = __sig_stpcpy(p, thing);
|
||||
p = __sig_stpcpy(p, strsignal_r(sig, sigbuf));
|
||||
p = __sig_stpcpy(p,
|
||||
". Pass --strace and/or ShowCrashReports() for details.\n");
|
||||
WriteFile(hStderr, s, p - s, 0, 0);
|
||||
#endif
|
||||
__sig_terminate(sig);
|
||||
}
|
||||
|
||||
static textwindows void __sig_unmaskable(struct NtExceptionPointers *ep,
|
||||
int code, int sig,
|
||||
struct CosmoTib *tib) {
|
||||
|
||||
// log vital crash information reliably for --strace before doing much
|
||||
// we don't print this without the flag since raw numbers scare people
|
||||
// this needs at least one page of stack memory in order to get logged
|
||||
// otherwise it'll print a warning message about the lack of stack mem
|
||||
STRACE("win32 vectored exception 0x%08Xu raising %G "
|
||||
"cosmoaddr2line %s %lx %s",
|
||||
ep->ExceptionRecord->ExceptionCode, sig,
|
||||
_weaken(FindDebugBinary) ? _weaken(FindDebugBinary)()
|
||||
: program_invocation_name,
|
||||
ep->ContextRecord->Rip,
|
||||
DescribeBacktrace((struct StackFrame *)ep->ContextRecord->Rbp));
|
||||
|
||||
// if the user didn't install a signal handler for this unmaskable
|
||||
// exception, then print a friendly helpful hint message to stderr
|
||||
unsigned rva = __sighandrvas[sig];
|
||||
if (rva == (intptr_t)SIG_DFL || rva == (intptr_t)SIG_IGN)
|
||||
__sig_death(sig, "uncaught ");
|
||||
|
||||
// if this signal handler is configured to auto-reset to the default
|
||||
// then that reset needs to happen before the user handler is called
|
||||
unsigned flags = __sighandflags[sig];
|
||||
if (flags & SA_RESETHAND) {
|
||||
STRACE("resetting %G handler", sig);
|
||||
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
|
||||
}
|
||||
|
||||
// determine the true memory address at which fault occurred
|
||||
// if this is a stack overflow then reapply guard protection
|
||||
void *si_addr;
|
||||
if (ep->ExceptionRecord->ExceptionCode == kNtSignalGuardPage) {
|
||||
si_addr = (void *)ep->ExceptionRecord->ExceptionInformation[1];
|
||||
} else {
|
||||
si_addr = ep->ExceptionRecord->ExceptionAddress;
|
||||
}
|
||||
|
||||
// call the user signal handler
|
||||
// and a modifiable view of the faulting code's cpu state
|
||||
// temporarily replace signal mask while calling crash handler
|
||||
// abort process if sig is already blocked to avoid crash loop
|
||||
// note ucontext_t is a hefty data structures on top of NtContext
|
||||
ucontext_t ctx = {0};
|
||||
siginfo_t si = {.si_signo = sig, .si_code = code, .si_addr = si_addr};
|
||||
_ntcontext2linux(&ctx, ep->ContextRecord);
|
||||
sigset_t blocksigs = __sighandmask[sig];
|
||||
if (!(flags & SA_NODEFER))
|
||||
blocksigs |= 1ull << (sig - 1);
|
||||
ctx.uc_sigmask = atomic_fetch_or_explicit(&tib->tib_sigmask, blocksigs,
|
||||
memory_order_acquire);
|
||||
if (ctx.uc_sigmask & (1ull << (sig - 1))) {
|
||||
__sig_death(sig, "masked ");
|
||||
__sig_terminate(sig);
|
||||
}
|
||||
__sig_handler(rva)(sig, &si, &ctx);
|
||||
atomic_store_explicit(&tib->tib_sigmask, ctx.uc_sigmask,
|
||||
memory_order_release);
|
||||
_ntlinux2context(ep->ContextRecord, &ctx);
|
||||
}
|
||||
|
||||
void __stack_call(struct NtExceptionPointers *, int, int, struct CosmoTib *,
|
||||
void (*)(struct NtExceptionPointers *, int, int,
|
||||
struct CosmoTib *),
|
||||
void *);
|
||||
|
||||
// abashed the devil stood
|
||||
// and felt how awful goodness is
|
||||
__msabi dontinstrument unsigned __sig_crash(struct NtExceptionPointers *ep) {
|
||||
|
||||
// translate win32 to unix si_signo and si_code
|
||||
int code, sig = __sig_crash_sig(ep->ExceptionRecord->ExceptionCode, &code);
|
||||
|
||||
// advance the instruction pointer to skip over debugger breakpoints
|
||||
// this behavior is consistent with how unix kernels are implemented
|
||||
if (sig == SIGTRAP) {
|
||||
ep->ContextRecord->Rip++;
|
||||
if (__sig_ignored(sig))
|
||||
return kNtExceptionContinueExecution;
|
||||
}
|
||||
|
||||
// win32 stack overflow detection executes INSIDE the guard page
|
||||
// thus switch to the alternate signal stack as soon as possible
|
||||
struct CosmoTib *tib = __get_tls();
|
||||
unsigned flags = __sighandflags[sig];
|
||||
if (__sig_should_use_altstack(flags, tib)) {
|
||||
__stack_call(ep, code, sig, tib, __sig_unmaskable,
|
||||
tib->tib_sigstack_addr + tib->tib_sigstack_size);
|
||||
} else {
|
||||
__sig_unmaskable(ep, code, sig, tib);
|
||||
}
|
||||
|
||||
// resume running user program
|
||||
// hopefully the user fixed the cpu state
|
||||
// otherwise the crash will keep happening
|
||||
return kNtExceptionContinueExecution;
|
||||
}
|
||||
|
||||
static textwindows int __sig_console_sig(uint32_t dwCtrlType) {
|
||||
switch (dwCtrlType) {
|
||||
case kNtCtrlCEvent:
|
||||
return SIGINT;
|
||||
case kNtCtrlBreakEvent:
|
||||
return SIGQUIT;
|
||||
case kNtCtrlCloseEvent:
|
||||
case kNtCtrlLogoffEvent: // only received by services
|
||||
case kNtCtrlShutdownEvent: // only received by services
|
||||
return SIGHUP;
|
||||
default:
|
||||
return SIGSTKFLT;
|
||||
}
|
||||
}
|
||||
|
||||
__msabi textwindows dontinstrument bool32 __sig_console(uint32_t dwCtrlType) {
|
||||
// win32 launches a thread to deliver ctrl-c and ctrl-break when typed
|
||||
// it only happens when kNtEnableProcessedInput is in play on console.
|
||||
// otherwise we need to wait until read-nt.c discovers that keystroke.
|
||||
struct CosmoTib tls;
|
||||
__bootstrap_tls(&tls, __builtin_frame_address(0));
|
||||
__sig_generate(__sig_console_sig(dwCtrlType), SI_KERNEL);
|
||||
return true;
|
||||
}
|
||||
|
||||
// returns 0 if no signal handlers were called, otherwise a bitmask
|
||||
// consisting of `1` which means a signal handler was invoked which
|
||||
// didn't have the SA_RESTART flag, and `2`, which means SA_RESTART
|
||||
// handlers were called (or `3` if both were the case).
|
||||
textwindows int __sig_check(void) {
|
||||
int sig, res = 0;
|
||||
while ((sig = __sig_get(atomic_load_explicit(&__get_tls()->tib_sigmask,
|
||||
memory_order_acquire))))
|
||||
res |= __sig_raise(sig, SI_KERNEL);
|
||||
return res;
|
||||
}
|
||||
|
||||
// background thread for delivering inter-process signals asynchronously
|
||||
// this checks for undelivered process-wide signals, once per scheduling
|
||||
// quantum, which on windows should be every ~15ms or so, unless somehow
|
||||
// the process was tuned to have more fine-grained event timing. we want
|
||||
// signals to happen faster when possible; that happens when cancelation
|
||||
// points, e.g. read need to wait on i/o; they too check for new signals
|
||||
textwindows dontinstrument static uint32_t __sig_worker(void *arg) {
|
||||
struct CosmoTib tls;
|
||||
__bootstrap_tls(&tls, __builtin_frame_address(0));
|
||||
char *sp = __builtin_frame_address(0);
|
||||
__maps_track((char *)(((uintptr_t)sp + __pagesize - 1) & -__pagesize) - STKSZ,
|
||||
STKSZ);
|
||||
for (;;) {
|
||||
|
||||
// dequeue all pending signals and fire them off. if there's no
|
||||
// thread that can handle them then __sig_generate will requeue
|
||||
// those signals back to __sig.process; hence the need for xchg
|
||||
unsigned long sigs =
|
||||
atomic_exchange_explicit(__sig.process, 0, memory_order_acq_rel);
|
||||
while (sigs) {
|
||||
int sig = bsfl(sigs) + 1;
|
||||
sigs &= ~(1ull << (sig - 1));
|
||||
__sig_generate(sig, SI_KERNEL);
|
||||
}
|
||||
|
||||
// unblock stalled asynchronous signals in threads
|
||||
_pthread_lock();
|
||||
for (struct Dll *e = dll_first(_pthread_list); e;
|
||||
e = dll_next(_pthread_list, e)) {
|
||||
struct PosixThread *pt = POSIXTHREAD_CONTAINER(e);
|
||||
if (atomic_load_explicit(&pt->pt_status, memory_order_acquire) >=
|
||||
kPosixThreadTerminated) {
|
||||
break;
|
||||
}
|
||||
sigset_t pending =
|
||||
atomic_load_explicit(&pt->tib->tib_sigpending, memory_order_acquire);
|
||||
sigset_t mask =
|
||||
atomic_load_explicit(&pt->tib->tib_sigmask, memory_order_acquire);
|
||||
if (pending & ~mask) {
|
||||
_pthread_ref(pt);
|
||||
_pthread_unlock();
|
||||
while (!atomic_compare_exchange_weak_explicit(
|
||||
&pt->tib->tib_sigpending, &pending, pending & ~mask,
|
||||
memory_order_acq_rel, memory_order_relaxed)) {
|
||||
}
|
||||
while ((pending = pending & ~mask)) {
|
||||
int sig = bsfl(pending) + 1;
|
||||
pending &= ~(1ull << (sig - 1));
|
||||
__sig_killer(pt, sig, SI_KERNEL);
|
||||
}
|
||||
_pthread_lock();
|
||||
_pthread_unref(pt);
|
||||
}
|
||||
}
|
||||
_pthread_unlock();
|
||||
|
||||
// wait until next scheduler quantum
|
||||
Sleep(POLL_INTERVAL_MS);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((__constructor__(10))) textstartup void __sig_init(void) {
|
||||
if (!IsWindows())
|
||||
return;
|
||||
AddVectoredExceptionHandler(true, (void *)__sig_crash);
|
||||
SetConsoleCtrlHandler((void *)__sig_console, true);
|
||||
CreateThread(0, STKSZ, __sig_worker, 0, kNtStackSizeParamIsAReservation, 0);
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
|
|
53
libc/intrin/sigblock.c
Normal file
53
libc/intrin/sigblock.c
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
struct Signals __sig;
|
||||
|
||||
sigset_t __sig_block(void) {
|
||||
if (IsWindows() || IsMetal()) {
|
||||
if (__tls_enabled)
|
||||
return atomic_exchange_explicit(&__get_tls()->tib_sigmask, -1,
|
||||
memory_order_acquire);
|
||||
else
|
||||
return 0;
|
||||
} else {
|
||||
sigset_t res, neu = -1;
|
||||
sys_sigprocmask(SIG_SETMASK, &neu, &res);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
void __sig_unblock(sigset_t m) {
|
||||
if (IsWindows() || IsMetal()) {
|
||||
if (__tls_enabled) {
|
||||
atomic_store_explicit(&__get_tls()->tib_sigmask, m, memory_order_release);
|
||||
if (_weaken(__sig_check))
|
||||
_weaken(__sig_check)();
|
||||
}
|
||||
} else {
|
||||
sys_sigprocmask(SIG_SETMASK, &m, 0);
|
||||
}
|
||||
}
|
116
libc/intrin/sigcrashsig.c
Normal file
116
libc/intrin/sigcrashsig.c
Normal file
|
@ -0,0 +1,116 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2024 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/intrin/pushpop.h"
|
||||
#include "libc/macros.h"
|
||||
#include "libc/nt/enum/signal.h"
|
||||
#include "libc/nt/enum/status.h"
|
||||
#include "libc/nt/struct/ntexceptionpointers.h"
|
||||
|
||||
// this is all a mandatory dependency of winmain
|
||||
// so, we trade away maintanibility for tininess
|
||||
// see libc/sysv/consts.sh for canonical magnums
|
||||
|
||||
#define SIGILL_ 4
|
||||
#define SIGTRAP_ 5
|
||||
#define SIGABRT_ 6
|
||||
#define SIGFPE_ 8
|
||||
#define SIGSEGV_ 11
|
||||
#define SIGSYS_ 31
|
||||
|
||||
#define TRAP_BRKPT_ 1
|
||||
#define ILL_ILLOPC_ 1
|
||||
#define ILL_PRVOPC_ 5
|
||||
#define SEGV_MAPERR_ 1
|
||||
#define SEGV_ACCERR_ 2
|
||||
#define SI_USER_ 0
|
||||
#define FPE_FLTDIV_ 3
|
||||
#define FPE_FLTOVF_ 4
|
||||
#define FPE_INTOVF_ 2
|
||||
#define FPE_FLTUND_ 5
|
||||
#define FPE_FLTRES_ 6
|
||||
#define FPE_FLTINV_ 7
|
||||
#define SI_KERNEL_ 128
|
||||
|
||||
#define LO(x) (x & 255)
|
||||
#define HI(x) ((x >> 24) / !(x & 0x00ffff00u))
|
||||
#define ROW(x, sic, sig) \
|
||||
{ \
|
||||
{ \
|
||||
{ \
|
||||
LO(x), HI(x), sic / !(sic & 0xffffff00), sig / !(sig & 0xffffff00) \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
struct CrashSig {
|
||||
union {
|
||||
struct {
|
||||
unsigned char lo;
|
||||
unsigned char hi;
|
||||
unsigned char sic;
|
||||
unsigned char sig;
|
||||
};
|
||||
unsigned word;
|
||||
};
|
||||
};
|
||||
|
||||
static const struct CrashSig kNtCrashSigs[] = {
|
||||
ROW(kNtSignalBreakpoint, TRAP_BRKPT_, SIGTRAP_), //
|
||||
ROW(kNtSignalIllegalInstruction, ILL_ILLOPC_, SIGILL_), //
|
||||
ROW(kNtSignalPrivInstruction, ILL_PRVOPC_, SIGILL_), //
|
||||
ROW(kNtSignalInPageError, SEGV_MAPERR_, SIGSEGV_), //
|
||||
ROW(kNtStatusStackOverflow, SEGV_MAPERR_, SIGSEGV_), //
|
||||
ROW(kNtSignalGuardPage, SEGV_ACCERR_, SIGSEGV_), //
|
||||
ROW(kNtSignalAccessViolation, SEGV_ACCERR_, SIGSEGV_), //
|
||||
ROW(kNtSignalInvalidHandle, SI_USER_, SIGABRT_), //
|
||||
ROW(kNtSignalInvalidParameter, SI_USER_, SIGABRT_), //
|
||||
ROW(kNtStatusIntegerOverflow, FPE_INTOVF_, SIGFPE_), //
|
||||
ROW(kNtSignalFltDivideByZero, FPE_FLTDIV_, SIGFPE_), //
|
||||
ROW(kNtSignalFltOverflow, FPE_FLTOVF_, SIGFPE_), //
|
||||
ROW(kNtSignalFltUnderflow, FPE_FLTUND_, SIGFPE_), //
|
||||
ROW(kNtSignalFltInexactResult, FPE_FLTRES_, SIGFPE_), //
|
||||
ROW(kNtSignalFltDenormalOperand, FPE_FLTINV_, SIGFPE_), //
|
||||
ROW(kNtSignalFltInvalidOperation, FPE_FLTINV_, SIGFPE_), //
|
||||
ROW(kNtSignalFltStackCheck, FPE_FLTINV_, SIGFPE_), //
|
||||
ROW(kNtSignalIntegerDivideByZero, FPE_FLTINV_, SIGFPE_), //
|
||||
// ROW(kNtSignalAssertionFailure, SI_USER_, SIGABRT_),
|
||||
// ROW(kNtSignalFloatMultipleTraps, FPE_FLTINV_, SIGFPE_),
|
||||
// ROW(kNtSignalFloatMultipleFaults, FPE_FLTINV_, SIGFPE_),
|
||||
// ROW(kNtSignalDllNotFound, SI_KERNEL_, SIGSYS_),
|
||||
// ROW(kNtSignalOrdinalNotFound, SI_KERNEL_, SIGSYS_),
|
||||
// ROW(kNtSignalEntrypointNotFound, SI_KERNEL_, SIGSYS_),
|
||||
// ROW(kNtSignalDllInitFailed, SI_KERNEL_, SIGSYS_),
|
||||
};
|
||||
|
||||
textwindows dontinstrument int __sig_crash_sig(unsigned exception, int *code) {
|
||||
for (int i = 0; i < ARRAYLEN(kNtCrashSigs); ++i) {
|
||||
struct CrashSig cs;
|
||||
cs.word = kNtCrashSigs[i].word;
|
||||
unsigned lo = cs.lo;
|
||||
unsigned hi = cs.hi;
|
||||
unsigned ec = lo | hi << 24;
|
||||
if (ec == exception) {
|
||||
*code = cs.sic;
|
||||
return cs.sig;
|
||||
}
|
||||
}
|
||||
*code = exception;
|
||||
return SIGSEGV_;
|
||||
}
|
59
libc/intrin/swapcontext.S
Normal file
59
libc/intrin/swapcontext.S
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│ vi: set noet ft=asm ts=8 sw=8 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.h"
|
||||
|
||||
// Saves machine to 𝑥 and activates 𝑦, i.e.
|
||||
//
|
||||
// getcontext(x);
|
||||
// setcontext(y);
|
||||
//
|
||||
// Except using this API is safer and goes 2x faster:
|
||||
//
|
||||
// swapcontext(x, y);
|
||||
//
|
||||
// @return 0 on success, or -1 w/ errno
|
||||
// @returnstwice
|
||||
.ftrace1
|
||||
swapcontext:
|
||||
.ftrace2
|
||||
#include "libc/intrin/getcontext.inc"
|
||||
#ifdef __x86_64__
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
push %rsi
|
||||
push %rsi
|
||||
call __swapcontextsig
|
||||
pop %rdi
|
||||
pop %rdi
|
||||
pop %rbp
|
||||
test %eax,%eax
|
||||
jnz 1f
|
||||
#elif defined(__aarch64__)
|
||||
stp x29,x30,[sp,#-32]!
|
||||
mov x29,sp
|
||||
str x1,[sp,16]
|
||||
bl __swapcontextsig
|
||||
ldr x1,[sp,16]
|
||||
ldp x29,x30,[sp],#32
|
||||
cbnz w0,1f
|
||||
mov x0,x1
|
||||
#endif
|
||||
jmp __tailcontext
|
||||
1: ret
|
||||
.endfn swapcontext,globl
|
131
libc/intrin/tailcontext.S
Normal file
131
libc/intrin/tailcontext.S
Normal file
|
@ -0,0 +1,131 @@
|
|||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│ vi: set noet ft=asm ts=8 sw=8 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.h"
|
||||
|
||||
// tailed called by setcontext() implementation
|
||||
__tailcontext:
|
||||
#ifdef __x86_64__
|
||||
|
||||
lea 608(%rdi),%rax
|
||||
movaps -0x80(%rax),%xmm0
|
||||
movaps -0x70(%rax),%xmm1
|
||||
movaps -0x60(%rax),%xmm2
|
||||
movaps -0x50(%rax),%xmm3
|
||||
movaps -0x40(%rax),%xmm4
|
||||
movaps -0x30(%rax),%xmm5
|
||||
movaps -0x20(%rax),%xmm6
|
||||
movaps -0x10(%rax),%xmm7
|
||||
movaps 0x00(%rax),%xmm8
|
||||
movaps 0x10(%rax),%xmm9
|
||||
movaps 0x20(%rax),%xmm10
|
||||
movaps 0x30(%rax),%xmm11
|
||||
movaps 0x40(%rax),%xmm12
|
||||
movaps 0x50(%rax),%xmm13
|
||||
movaps 0x60(%rax),%xmm14
|
||||
movaps 0x70(%rax),%xmm15
|
||||
|
||||
lea 80(%rdi),%rax
|
||||
mov -40(%rax),%r8
|
||||
mov -32(%rax),%r9
|
||||
mov -24(%rax),%r10
|
||||
mov -16(%rax),%r11
|
||||
mov -8(%rax),%r12
|
||||
mov 0(%rax),%r13
|
||||
mov 8(%rax),%r14
|
||||
mov 16(%rax),%r15
|
||||
mov 32(%rax),%rsi
|
||||
mov 40(%rax),%rbp
|
||||
mov 48(%rax),%rbx
|
||||
mov 56(%rax),%rdx
|
||||
mov 72(%rax),%rcx
|
||||
mov 80(%rax),%rsp
|
||||
push 88(%rax)
|
||||
mov 24(%rax),%rdi
|
||||
|
||||
xor %eax,%eax
|
||||
ret
|
||||
|
||||
#elif defined(__aarch64__)
|
||||
#define REGS(i) 184+i*8
|
||||
|
||||
/*void setfpsimd(const ucontext_t *uc) {
|
||||
struct fpsimd_context *fp;
|
||||
fp = (struct fpsimd_context *)uc->uc_mcontext.__reserved;
|
||||
if (fp[0].head.magic == FPSIMD_MAGIC) {
|
||||
asm("msr\tfpsr,%0" ::"r"(fp->fpsr));
|
||||
asm("msr\tfpcr,%0" ::"r"(fp->fpcr));
|
||||
asm("ldp\tq0,q1,%0" ::"m"(fp->vregs[0]));
|
||||
asm("ldp\tq2,q3,%0" ::"m"(fp->vregs[2]));
|
||||
asm("ldp\tq4,q5,%0" ::"m"(fp->vregs[4]));
|
||||
asm("ldp\tq6,q7,%0" ::"m"(fp->vregs[6]));
|
||||
asm("ldp\tq8,q9,%0" ::"m"(fp->vregs[8]));
|
||||
asm("ldp\tq10,q11,%0" ::"m"(fp->vregs[10]));
|
||||
asm("ldp\tq12,q13,%0" ::"m"(fp->vregs[12]));
|
||||
asm("ldp\tq14,q15,%0" ::"m"(fp->vregs[14]));
|
||||
}
|
||||
}*/
|
||||
|
||||
ldr w14,[x0,464]
|
||||
add x15,x0,464
|
||||
mov w13,0x8001
|
||||
movk w13,0x4650,lsl 16
|
||||
cmp w14,w13
|
||||
bne 1f
|
||||
ldr w13,[x15,8]
|
||||
msr fpsr,x13
|
||||
ldr w13,[x15,12]
|
||||
msr fpcr,x13
|
||||
ldp q0,q1,[x15,16]
|
||||
ldp q2,q3,[x15,48]
|
||||
ldp q4,q5,[x15,80]
|
||||
ldp q6,q7,[x15,112]
|
||||
ldp q8,q9,[x15,144]
|
||||
ldp q10,q11,[x15,176]
|
||||
ldp q12,q13,[x15,208]
|
||||
ldp q14,q15,[x15,240]
|
||||
1:
|
||||
|
||||
// x16 is clobbered
|
||||
// x18 belongs to apple
|
||||
// all other registers are restored
|
||||
|
||||
ldp x1,x16,[x0,REGS(31)] // sp, pc
|
||||
mov sp,x1
|
||||
ldr x30,[x0,REGS(30)]
|
||||
ldp x28,x29,[x0,REGS(28)]
|
||||
ldp x26,x27,[x0,REGS(26)]
|
||||
ldp x24,x25,[x0,REGS(24)]
|
||||
ldp x22,x23,[x0,REGS(22)]
|
||||
ldp x20,x21,[x0,REGS(20)]
|
||||
ldr x19,[x0,REGS(19)]
|
||||
ldr x17,[x0,REGS(17)]
|
||||
ldp x14,x15,[x0,REGS(14)]
|
||||
ldp x12,x13,[x0,REGS(12)]
|
||||
ldp x10,x11,[x0,REGS(10)]
|
||||
ldp x8,x9,[x0,REGS(8)]
|
||||
ldp x6,x7,[x0,REGS(6)]
|
||||
ldp x4,x5,[x0,REGS(4)]
|
||||
ldp x2,x3,[x0,REGS(2)]
|
||||
ldp x0,x1,[x0,REGS(0)]
|
||||
br x16
|
||||
|
||||
#else
|
||||
#error "unsupported architecture"
|
||||
#endif
|
||||
.endfn __tailcontext,globl
|
32
libc/intrin/timespec_add.c
Normal file
32
libc/intrin/timespec_add.c
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
|
||||
/**
|
||||
* Adds two nanosecond timestamps.
|
||||
*/
|
||||
struct timespec timespec_add(struct timespec x, struct timespec y) {
|
||||
x.tv_sec += y.tv_sec;
|
||||
x.tv_nsec += y.tv_nsec;
|
||||
if (x.tv_nsec >= 1000000000) {
|
||||
x.tv_nsec -= 1000000000;
|
||||
x.tv_sec += 1;
|
||||
}
|
||||
return x;
|
||||
}
|
32
libc/intrin/timespec_cmp.c
Normal file
32
libc/intrin/timespec_cmp.c
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
|
||||
/**
|
||||
* Compares nanosecond timestamps.
|
||||
*
|
||||
* @return 0 if equal, -1 if `a < b`, or +1 if `a > b`
|
||||
*/
|
||||
int timespec_cmp(struct timespec a, struct timespec b) {
|
||||
int cmp;
|
||||
if (!(cmp = (a.tv_sec > b.tv_sec) - (a.tv_sec < b.tv_sec))) {
|
||||
cmp = (a.tv_nsec > b.tv_nsec) - (a.tv_nsec < b.tv_nsec);
|
||||
}
|
||||
return cmp;
|
||||
}
|
29
libc/intrin/timespec_frommicros.c
Normal file
29
libc/intrin/timespec_frommicros.c
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
|
||||
/**
|
||||
* Converts timespec interval from microseconds.
|
||||
*/
|
||||
struct timespec timespec_frommicros(int64_t x) {
|
||||
struct timespec ts;
|
||||
ts.tv_sec = x / 1000000;
|
||||
ts.tv_nsec = x % 1000000 * 1000;
|
||||
return ts;
|
||||
}
|
33
libc/intrin/timespec_frommillis.c
Normal file
33
libc/intrin/timespec_frommillis.c
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
|
||||
/**
|
||||
* Converts timespec interval from milliseconds.
|
||||
*/
|
||||
struct timespec timespec_frommillis(int64_t x) {
|
||||
struct timespec ts;
|
||||
if (x >= 0) {
|
||||
ts.tv_sec = x / 1000;
|
||||
ts.tv_nsec = x % 1000 * 1000000;
|
||||
} else {
|
||||
ts = timespec_max;
|
||||
}
|
||||
return ts;
|
||||
}
|
29
libc/intrin/timespec_fromnanos.c
Normal file
29
libc/intrin/timespec_fromnanos.c
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
|
||||
/**
|
||||
* Converts timespec interval from nanoseconds.
|
||||
*/
|
||||
struct timespec timespec_fromnanos(int64_t x) {
|
||||
struct timespec ts;
|
||||
ts.tv_sec = x / 1000000000;
|
||||
ts.tv_nsec = x % 1000000000;
|
||||
return ts;
|
||||
}
|
32
libc/intrin/timespec_sub.c
Normal file
32
libc/intrin/timespec_sub.c
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
|
||||
/**
|
||||
* Subtracts two nanosecond timestamps.
|
||||
*/
|
||||
struct timespec timespec_sub(struct timespec a, struct timespec b) {
|
||||
a.tv_sec -= b.tv_sec;
|
||||
if (a.tv_nsec < b.tv_nsec) {
|
||||
a.tv_nsec += 1000000000;
|
||||
a.tv_sec--;
|
||||
}
|
||||
a.tv_nsec -= b.tv_nsec;
|
||||
return a;
|
||||
}
|
32
libc/intrin/timespec_subz.c
Normal file
32
libc/intrin/timespec_subz.c
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
|
||||
/**
|
||||
* Subtracts two nanosecond timestamps.
|
||||
*
|
||||
* Unlike `timespec_sub()` this function will return zero if `x < y`.
|
||||
*/
|
||||
struct timespec timespec_subz(struct timespec x, struct timespec y) {
|
||||
if (timespec_cmp(x, y) > 0) {
|
||||
return timespec_sub(x, y);
|
||||
} else {
|
||||
return timespec_zero;
|
||||
}
|
||||
}
|
56
libc/intrin/timespec_tomicros.c
Normal file
56
libc/intrin/timespec_tomicros.c
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/stdckdint.h"
|
||||
|
||||
/**
|
||||
* Reduces `ts` from 1e-9 to 1e-6 granularity w/ ceil rounding.
|
||||
*
|
||||
* This function uses ceiling rounding. For example, if `ts` is one
|
||||
* nanosecond, then one microsecond will be returned. Ceil rounding
|
||||
* is needed by many interfaces, e.g. setitimer(), because the zero
|
||||
* timestamp has a special meaning.
|
||||
*
|
||||
* This function also detects overflow in which case `INT64_MAX` or
|
||||
* `INT64_MIN` may be returned. The `errno` variable isn't changed.
|
||||
*
|
||||
* @return 64-bit scalar holding microseconds since epoch
|
||||
* @see timespec_totimeval()
|
||||
*/
|
||||
int64_t timespec_tomicros(struct timespec ts) {
|
||||
int64_t us;
|
||||
// reduce precision from nanos to micros
|
||||
if (ts.tv_nsec <= 999999000) {
|
||||
ts.tv_nsec = (ts.tv_nsec + 999) / 1000;
|
||||
} else {
|
||||
ts.tv_nsec = 0;
|
||||
if (ts.tv_sec < INT64_MAX) {
|
||||
ts.tv_sec += 1;
|
||||
}
|
||||
}
|
||||
// convert to scalar result
|
||||
if (!ckd_mul(&us, ts.tv_sec, 1000000ul) && !ckd_add(&us, us, ts.tv_nsec)) {
|
||||
return us;
|
||||
} else if (ts.tv_sec < 0) {
|
||||
return INT64_MIN;
|
||||
} else {
|
||||
return INT64_MAX;
|
||||
}
|
||||
}
|
55
libc/intrin/timespec_tomillis.c
Normal file
55
libc/intrin/timespec_tomillis.c
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/stdckdint.h"
|
||||
|
||||
/**
|
||||
* Reduces `ts` from 1e-9 to 1e-3 granularity w/ ceil rounding.
|
||||
*
|
||||
* This function uses ceiling rounding. For example, if `ts` is one
|
||||
* nanosecond, then one millisecond will be returned. Ceil rounding
|
||||
* is needed by many interfaces, e.g. setitimer(), because the zero
|
||||
* timestamp has a special meaning.
|
||||
*
|
||||
* This function also detects overflow in which case `INT64_MAX` or
|
||||
* `INT64_MIN` may be returned. The `errno` variable isn't changed.
|
||||
*
|
||||
* @return 64-bit scalar milliseconds since epoch
|
||||
*/
|
||||
int64_t timespec_tomillis(struct timespec ts) {
|
||||
int64_t ms;
|
||||
// reduce precision from nanos to millis
|
||||
if (ts.tv_nsec <= 999000000) {
|
||||
ts.tv_nsec = (ts.tv_nsec + 999999) / 1000000;
|
||||
} else {
|
||||
ts.tv_nsec = 0;
|
||||
if (ts.tv_sec < INT64_MAX) {
|
||||
ts.tv_sec += 1;
|
||||
}
|
||||
}
|
||||
// convert to scalar result
|
||||
if (!ckd_mul(&ms, ts.tv_sec, 1000ul) && !ckd_add(&ms, ms, ts.tv_nsec)) {
|
||||
return ms;
|
||||
} else if (ts.tv_sec < 0) {
|
||||
return INT64_MIN;
|
||||
} else {
|
||||
return INT64_MAX;
|
||||
}
|
||||
}
|
41
libc/intrin/timespec_tonanos.c
Normal file
41
libc/intrin/timespec_tonanos.c
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/stdckdint.h"
|
||||
|
||||
/**
|
||||
* Converts timespec to scalar.
|
||||
*
|
||||
* This returns the absolute number of nanoseconds in a timespec. If
|
||||
* overflow happens, then `INT64_MAX` or `INT64_MIN` is returned. The
|
||||
* `errno` variable isn't changed.
|
||||
*
|
||||
* @return 64-bit integer holding nanoseconds since epoch
|
||||
*/
|
||||
int64_t timespec_tonanos(struct timespec x) {
|
||||
int64_t ns;
|
||||
if (!ckd_mul(&ns, x.tv_sec, 1000000000ul) && !ckd_add(&ns, ns, x.tv_nsec)) {
|
||||
return ns;
|
||||
} else if (x.tv_sec < 0) {
|
||||
return INT64_MIN;
|
||||
} else {
|
||||
return INT64_MAX;
|
||||
}
|
||||
}
|
38
libc/intrin/timespec_totimeval.c
Normal file
38
libc/intrin/timespec_totimeval.c
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
|
||||
/**
|
||||
* Reduces `ts` from 1e-9 to 1e-6 granularity w/ ceil rounding.
|
||||
*
|
||||
* This function uses ceiling rounding. For example, if `ts` is one
|
||||
* nanosecond, then one microsecond will be returned. Ceil rounding
|
||||
* is needed by many interfaces, e.g. setitimer(), because the zero
|
||||
* timestamp has a special meaning.
|
||||
*
|
||||
* @return microseconds since epoch
|
||||
* @see timespec_tomicros()
|
||||
*/
|
||||
struct timeval timespec_totimeval(struct timespec ts) {
|
||||
if (ts.tv_nsec < 1000000000 - 999) {
|
||||
return (struct timeval){ts.tv_sec, (ts.tv_nsec + 999) / 1000};
|
||||
} else {
|
||||
return (struct timeval){ts.tv_sec + 1, 0};
|
||||
}
|
||||
}
|
23
libc/intrin/timespectowindowstime.c
Normal file
23
libc/intrin/timespectowindowstime.c
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/fmt/wintime.internal.h"
|
||||
|
||||
int64_t TimeSpecToWindowsTime(struct timespec t) {
|
||||
return t.tv_nsec / 100 + (t.tv_sec + MODERNITYSECONDS) * HECTONANOSECONDS;
|
||||
}
|
32
libc/intrin/timeval_add.c
Normal file
32
libc/intrin/timeval_add.c
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
|
||||
/**
|
||||
* Adds two microsecond timestamps.
|
||||
*/
|
||||
struct timeval timeval_add(struct timeval x, struct timeval y) {
|
||||
x.tv_sec += y.tv_sec;
|
||||
x.tv_usec += y.tv_usec;
|
||||
if (x.tv_usec >= 1000000) {
|
||||
x.tv_usec -= 1000000;
|
||||
x.tv_sec += 1;
|
||||
}
|
||||
return x;
|
||||
}
|
32
libc/intrin/timeval_cmp.c
Normal file
32
libc/intrin/timeval_cmp.c
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
|
||||
/**
|
||||
* Compares microsecond timestamps.
|
||||
*
|
||||
* @return 0 if equal, -1 if `a < b`, or +1 if `a > b`
|
||||
*/
|
||||
int timeval_cmp(struct timeval a, struct timeval b) {
|
||||
int cmp;
|
||||
if (!(cmp = (a.tv_sec > b.tv_sec) - (a.tv_sec < b.tv_sec))) {
|
||||
cmp = (a.tv_usec > b.tv_usec) - (a.tv_usec < b.tv_usec);
|
||||
}
|
||||
return cmp;
|
||||
}
|
29
libc/intrin/timeval_frommicros.c
Normal file
29
libc/intrin/timeval_frommicros.c
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
|
||||
/**
|
||||
* Converts timeval interval from microseconds.
|
||||
*/
|
||||
struct timeval timeval_frommicros(int64_t x) {
|
||||
struct timeval tv;
|
||||
tv.tv_sec = x / 1000000;
|
||||
tv.tv_usec = x % 1000000;
|
||||
return tv;
|
||||
}
|
29
libc/intrin/timeval_frommillis.c
Normal file
29
libc/intrin/timeval_frommillis.c
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
|
||||
/**
|
||||
* Converts timeval interval from milliseconds.
|
||||
*/
|
||||
struct timeval timeval_frommillis(int64_t x) {
|
||||
struct timeval tv;
|
||||
tv.tv_sec = x / 1000;
|
||||
tv.tv_usec = x % 1000 * 1000;
|
||||
return tv;
|
||||
}
|
32
libc/intrin/timeval_sub.c
Normal file
32
libc/intrin/timeval_sub.c
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
|
||||
/**
|
||||
* Subtracts two nanosecond timestamps.
|
||||
*/
|
||||
struct timeval timeval_sub(struct timeval a, struct timeval b) {
|
||||
a.tv_sec -= b.tv_sec;
|
||||
if (a.tv_usec < b.tv_usec) {
|
||||
a.tv_usec += 1000000;
|
||||
a.tv_sec--;
|
||||
}
|
||||
a.tv_usec -= b.tv_usec;
|
||||
return a;
|
||||
}
|
30
libc/intrin/timeval_subz.c
Normal file
30
libc/intrin/timeval_subz.c
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
|
||||
/**
|
||||
* Subtracts two nanosecond timestamps.
|
||||
*/
|
||||
struct timeval timeval_subz(struct timeval x, struct timeval y) {
|
||||
if (timeval_cmp(x, y) > 0) {
|
||||
return timeval_sub(x, y);
|
||||
} else {
|
||||
return timeval_zero;
|
||||
}
|
||||
}
|
41
libc/intrin/timeval_tomicros.c
Normal file
41
libc/intrin/timeval_tomicros.c
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/stdckdint.h"
|
||||
|
||||
/**
|
||||
* Converts timeval to scalar.
|
||||
*
|
||||
* This returns the absolute number of microseconds in a timeval. If
|
||||
* overflow happens, then `INT64_MAX` or `INT64_MIN` is returned. The
|
||||
* `errno` variable isn't changed.
|
||||
*
|
||||
* @return 64-bit integer holding microseconds since epoch
|
||||
*/
|
||||
int64_t timeval_tomicros(struct timeval x) {
|
||||
int64_t ns;
|
||||
if (!ckd_mul(&ns, x.tv_sec, 1000000ul) && !ckd_add(&ns, ns, x.tv_usec)) {
|
||||
return ns;
|
||||
} else if (x.tv_sec < 0) {
|
||||
return INT64_MIN;
|
||||
} else {
|
||||
return INT64_MAX;
|
||||
}
|
||||
}
|
56
libc/intrin/timeval_tomillis.c
Normal file
56
libc/intrin/timeval_tomillis.c
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/stdckdint.h"
|
||||
|
||||
/**
|
||||
* Reduces `ts` from 1e-6 to 1e-3 granularity w/ ceil rounding.
|
||||
*
|
||||
* This function returns the absolute number of milliseconds in a
|
||||
* timeval. Ceiling rounding is used. For example, if `ts` is one
|
||||
* nanosecond, then one millisecond will be returned. Ceil rounding is
|
||||
* needed by many interfaces, e.g. setitimer(), because the zero
|
||||
* timestamp has a valial meaning.
|
||||
*
|
||||
* This function also detects overflow in which case `INT64_MAX` or
|
||||
* `INT64_MIN` may be returned. The `errno` variable isn't changed.
|
||||
*
|
||||
* @return 64-bit scalar milliseconds since epoch
|
||||
*/
|
||||
int64_t timeval_tomillis(struct timeval ts) {
|
||||
int64_t ms;
|
||||
// reduce precision from micros to millis
|
||||
if (ts.tv_usec <= 999000) {
|
||||
ts.tv_usec = (ts.tv_usec + 999) / 1000;
|
||||
} else {
|
||||
ts.tv_usec = 0;
|
||||
if (ts.tv_sec < INT64_MAX) {
|
||||
ts.tv_sec += 1;
|
||||
}
|
||||
}
|
||||
// convert to scalar result
|
||||
if (!ckd_mul(&ms, ts.tv_sec, 1000ul) && !ckd_add(&ms, ms, ts.tv_usec)) {
|
||||
return ms;
|
||||
} else if (ts.tv_sec < 0) {
|
||||
return INT64_MIN;
|
||||
} else {
|
||||
return INT64_MAX;
|
||||
}
|
||||
}
|
36
libc/intrin/timeval_toseconds.c
Normal file
36
libc/intrin/timeval_toseconds.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/limits.h"
|
||||
|
||||
/**
|
||||
* Converts timeval to seconds.
|
||||
*
|
||||
* This function uses ceil rounding, so 1µs becomes 1s. The addition
|
||||
* operation is saturating so timeval_toseconds(timeval_max) returns
|
||||
* INT64_MAX.
|
||||
*/
|
||||
int64_t timeval_toseconds(struct timeval tv) {
|
||||
int64_t secs;
|
||||
secs = tv.tv_sec;
|
||||
if (tv.tv_usec && secs < INT64_MAX) {
|
||||
++secs;
|
||||
}
|
||||
return secs;
|
||||
}
|
23
libc/intrin/timevaltowindowstime.c
Normal file
23
libc/intrin/timevaltowindowstime.c
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/fmt/wintime.internal.h"
|
||||
|
||||
int64_t TimeValToWindowsTime(struct timeval t) {
|
||||
return t.tv_usec * 10 + (t.tv_sec + MODERNITYSECONDS) * HECTONANOSECONDS;
|
||||
}
|
64
libc/intrin/ucontext.c
Normal file
64
libc/intrin/ucontext.c
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/ucontext.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
int __tailcontext(const ucontext_t *);
|
||||
|
||||
/**
|
||||
* Sets machine context.
|
||||
*
|
||||
* @return -1 on error w/ errno, otherwise won't return unless sent back
|
||||
* @see swapcontext()
|
||||
* @see makecontext()
|
||||
* @see getcontext()
|
||||
*/
|
||||
int setcontext(const ucontext_t *uc) {
|
||||
if (IsWindows() || IsMetal()) {
|
||||
atomic_store_explicit(&__get_tls()->tib_sigmask, uc->uc_sigmask,
|
||||
memory_order_release);
|
||||
} else {
|
||||
sys_sigprocmask(SIG_SETMASK, &uc->uc_sigmask, 0);
|
||||
}
|
||||
return __tailcontext(uc);
|
||||
}
|
||||
|
||||
int __getcontextsig(ucontext_t *uc) {
|
||||
if (IsWindows() || IsMetal()) {
|
||||
uc->uc_sigmask =
|
||||
atomic_load_explicit(&__get_tls()->tib_sigmask, memory_order_acquire);
|
||||
return 0;
|
||||
} else {
|
||||
return sys_sigprocmask(SIG_SETMASK, 0, &uc->uc_sigmask);
|
||||
}
|
||||
}
|
||||
|
||||
int __swapcontextsig(ucontext_t *x, const ucontext_t *y) {
|
||||
if (IsWindows() || IsMetal()) {
|
||||
x->uc_sigmask = atomic_exchange_explicit(
|
||||
&__get_tls()->tib_sigmask, y->uc_sigmask, memory_order_acquire);
|
||||
return 0;
|
||||
} else {
|
||||
return sys_sigprocmask(SIG_SETMASK, &y->uc_sigmask, &x->uc_sigmask);
|
||||
}
|
||||
}
|
153
libc/intrin/vdsofunc.c
Normal file
153
libc/intrin/vdsofunc.c
Normal file
|
@ -0,0 +1,153 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/elf/def.h"
|
||||
#include "libc/elf/scalar.h"
|
||||
#include "libc/elf/struct/ehdr.h"
|
||||
#include "libc/elf/struct/phdr.h"
|
||||
#include "libc/elf/struct/shdr.h"
|
||||
#include "libc/elf/struct/sym.h"
|
||||
#include "libc/elf/struct/verdaux.h"
|
||||
#include "libc/elf/struct/verdef.h"
|
||||
#include "libc/intrin/getauxval.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/auxv.h"
|
||||
|
||||
// needed to avoid asan restrictions on strcmp
|
||||
static int StrCmp(const char *l, const char *r) {
|
||||
size_t i = 0;
|
||||
while (l[i] == r[i] && r[i])
|
||||
++i;
|
||||
return (l[i] & 255) - (r[i] & 255);
|
||||
}
|
||||
|
||||
static inline int CheckDsoSymbolVersion(Elf64_Verdef *vd, int sym,
|
||||
const char *name, char *strtab) {
|
||||
Elf64_Verdaux *aux;
|
||||
for (;; vd = (Elf64_Verdef *)((char *)vd + vd->vd_next)) {
|
||||
if (!(vd->vd_flags & VER_FLG_BASE) &&
|
||||
(vd->vd_ndx & 0x7fff) == (sym & 0x7fff)) {
|
||||
aux = (Elf64_Verdaux *)((char *)vd + vd->vd_aux);
|
||||
return !StrCmp(name, strtab + aux->vda_name);
|
||||
}
|
||||
if (!vd->vd_next) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns address of "Virtual Dynamic Shared Object" function on Linux.
|
||||
*/
|
||||
void *__vdsosym(const char *version, const char *name) {
|
||||
void *p;
|
||||
size_t i;
|
||||
Elf64_Ehdr *ehdr;
|
||||
Elf64_Phdr *phdr;
|
||||
char *strtab = 0;
|
||||
size_t *dyn, base;
|
||||
Elf64_Sym *symtab = 0;
|
||||
uint16_t *versym = 0;
|
||||
Elf_Symndx *hashtab = 0;
|
||||
Elf64_Verdef *verdef = 0;
|
||||
struct AuxiliaryValue av;
|
||||
|
||||
av = __getauxval(AT_SYSINFO_EHDR);
|
||||
if (!av.isfound) {
|
||||
KERNTRACE("__vdsosym() → missing AT_SYSINFO_EHDR");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ehdr = (void *)av.value;
|
||||
phdr = (void *)((char *)ehdr + ehdr->e_phoff);
|
||||
for (base = -1, dyn = 0, i = 0; i < ehdr->e_phnum;
|
||||
i++, phdr = (void *)((char *)phdr + ehdr->e_phentsize)) {
|
||||
switch (phdr->p_type) {
|
||||
case PT_LOAD:
|
||||
// modern linux uses the base address zero, but elders
|
||||
// e.g. rhel7 uses the base address 0xffffffffff700000
|
||||
base = (size_t)ehdr + phdr->p_offset - phdr->p_vaddr;
|
||||
break;
|
||||
case PT_DYNAMIC:
|
||||
dyn = (void *)((char *)ehdr + phdr->p_offset);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!dyn || base == -1) {
|
||||
KERNTRACE("__vdsosym() → missing program headers");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; dyn[i]; i += 2) {
|
||||
p = (void *)(base + dyn[i + 1]);
|
||||
switch (dyn[i]) {
|
||||
case DT_STRTAB:
|
||||
strtab = p;
|
||||
break;
|
||||
case DT_SYMTAB:
|
||||
symtab = p;
|
||||
break;
|
||||
case DT_HASH:
|
||||
hashtab = p;
|
||||
break;
|
||||
case DT_VERSYM:
|
||||
versym = p;
|
||||
break;
|
||||
case DT_VERDEF:
|
||||
verdef = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!strtab || !symtab || !hashtab) {
|
||||
KERNTRACE("__vdsosym() → tables not found");
|
||||
return 0;
|
||||
}
|
||||
if (!verdef) {
|
||||
versym = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < hashtab[1]; i++) {
|
||||
if (ELF64_ST_TYPE(symtab[i].st_info) != STT_FUNC &&
|
||||
ELF64_ST_TYPE(symtab[i].st_info) != STT_OBJECT &&
|
||||
ELF64_ST_TYPE(symtab[i].st_info) != STT_NOTYPE) {
|
||||
continue;
|
||||
}
|
||||
if (ELF64_ST_BIND(symtab[i].st_info) != STB_GLOBAL) {
|
||||
continue;
|
||||
}
|
||||
if (!symtab[i].st_shndx) {
|
||||
continue;
|
||||
}
|
||||
if (StrCmp(name, strtab + symtab[i].st_name)) {
|
||||
continue;
|
||||
}
|
||||
if (versym && !CheckDsoSymbolVersion(verdef, versym[i], version, strtab)) {
|
||||
continue;
|
||||
}
|
||||
return (void *)(base + symtab[i].st_value);
|
||||
}
|
||||
|
||||
KERNTRACE("__vdsosym() → symbol not found");
|
||||
return 0;
|
||||
}
|
23
libc/intrin/windowsdurationtotimespec.c
Normal file
23
libc/intrin/windowsdurationtotimespec.c
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/fmt/wintime.internal.h"
|
||||
|
||||
struct timespec WindowsDurationToTimeSpec(int64_t x) {
|
||||
return (struct timespec){x / HECTONANOSECONDS, x % HECTONANOSECONDS * 100};
|
||||
}
|
23
libc/intrin/windowsdurationtotimeval.c
Normal file
23
libc/intrin/windowsdurationtotimeval.c
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/fmt/wintime.internal.h"
|
||||
|
||||
struct timeval WindowsDurationToTimeVal(int64_t x) {
|
||||
return (struct timeval){x / HECTONANOSECONDS, x % HECTONANOSECONDS / 10};
|
||||
}
|
26
libc/intrin/windowstimetotimespec.c
Normal file
26
libc/intrin/windowstimetotimespec.c
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/fmt/wintime.internal.h"
|
||||
|
||||
/**
|
||||
* Converts Windows COBOL timestamp to UNIX epoch in nanoseconds.
|
||||
*/
|
||||
struct timespec WindowsTimeToTimeSpec(int64_t x) {
|
||||
return WindowsDurationToTimeSpec(x - MODERNITYSECONDS * HECTONANOSECONDS);
|
||||
}
|
27
libc/intrin/windowstimetotimeval.c
Normal file
27
libc/intrin/windowstimetotimeval.c
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/fmt/wintime.internal.h"
|
||||
|
||||
/**
|
||||
* Converts Windows COBOL timestamp to UNIX epoch in microseconds.
|
||||
*/
|
||||
struct timeval WindowsTimeToTimeVal(int64_t x) {
|
||||
return (struct timeval){x / HECTONANOSECONDS - MODERNITYSECONDS,
|
||||
x % HECTONANOSECONDS / 10};
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue