Improve quality of our ANSI C clock() function

It now works most excellently across all supported operating
sytsems (earlier it didn't work on NT and XNU). Demo code is
available in examples/clock.c and this change also adds some
of the newer ANSI C time functions like timespec_get(), plus
timespec_getres() which hasn't even come out yet as it's C23
This commit is contained in:
Justine Tunney 2022-09-05 21:43:49 -07:00
parent 7ff0ea8c13
commit 12d9e1e128
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
24 changed files with 254 additions and 76 deletions

41
examples/clock.c Normal file
View file

@ -0,0 +1,41 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/calls/struct/timespec.h"
#include "libc/stdio/stdio.h"
#include "libc/time/time.h"
/**
* @fileoverview clock() function demo
*/
int main(int argc, char *argv[]) {
unsigned long i;
volatile unsigned long x;
struct timespec now, start, next, interval;
printf("hammering the cpu...\n");
next = start = _timespec_mono();
interval = _timespec_frommillis(500);
next = _timespec_add(next, interval);
for (;;) {
for (i = 0;; ++i) {
x *= 7;
if (!(i % 256)) {
now = _timespec_mono();
if (_timespec_gte(now, next)) {
break;
}
}
}
next = _timespec_add(next, interval);
printf("consumed %10g seconds monotonic time and %10g seconds cpu time\n",
_timespec_tonanos(_timespec_sub(now, start)) / 1000000000.,
(double)clock() / CLOCKS_PER_SEC);
}
}

View file

@ -8,14 +8,14 @@
*/
#endif
#include "libc/calls/calls.h"
#include "libc/intrin/kprintf.h"
#include "libc/log/check.h"
#include "libc/stdio/stdio.h"
int main(int argc, char *argv[]) {
char name[254];
CHECK_NE(-1, gethostname(name, sizeof(name)));
printf("gethostname() → %`'s\n", name);
CHECK_NE(-1, getdomainname(name, sizeof(name)));
printf("getdomainname() → %`'s\n", name);
gethostname(name, sizeof(name));
kprintf("gethostname() → %#s\n", name);
getdomainname(name, sizeof(name));
kprintf("getdomainname() → %#s\n", name);
return 0;
}

View file

View file

@ -0,0 +1,29 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net 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;
}

View file

@ -184,9 +184,11 @@ o//libc/calls/statfs2cosmo.o: private \
# we always want -O2 because:
# division is expensive if not optimized
o/$(MODE)/libc/calls/clock.o \
o/$(MODE)/libc/calls/_timespec_tomillis.o \
o/$(MODE)/libc/calls/_timespec_tomicros.o \
o/$(MODE)/libc/calls/_timespec_totimeval.o \
o/$(MODE)/libc/calls/_timespec_fromnanos.o \
o/$(MODE)/libc/calls/_timespec_frommillis.o \
o/$(MODE)/libc/calls/_timespec_frommicros.o: private \
OVERRIDE_CFLAGS += \

View file

@ -16,36 +16,51 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/struct/rusage.h"
#include "libc/calls/struct/timespec.h"
#include "libc/dce.h"
#include "libc/fmt/conv.h"
#include "libc/nt/accounting.h"
#include "libc/nt/runtime.h"
#include "libc/nt/synchronization.h"
#include "libc/calls/struct/timeval.h"
#include "libc/errno.h"
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/consts/rusage.h"
#include "libc/time/time.h"
/**
* Returns how much CPU program has consumed on time-sharing system.
* Returns sum of CPU time consumed by current process since birth.
*
* @return value that can be divided by CLOCKS_PER_SEC, or -1 w/ errno
* @see clock_gettime()
* This function provides a basic idea of how computationally expensive
* your program is, in terms of both the userspace and kernel processor
* resources it's hitherto consumed. Here's an example of how you might
* display this information:
*
* printf("consumed %g seconds of cpu time\n",
* (double)clock() / CLOCKS_PER_SEC);
*
* This function offers at best microsecond accuracy on all supported
* platforms. Please note the reported values might be a bit chunkier
* depending on the kernel scheduler sampling interval see `CLK_TCK`.
*
* @return units of CPU time consumed, where each unit's time length
* should be `1./CLOCKS_PER_SEC` seconds; Cosmopolitan currently
* returns the unit count in microseconds, i.e. `CLOCKS_PER_SEC`
* is hard-coded as 1000000. On failure this returns -1 / errno.
* @raise ENOSYS should be returned currently if run on Bare Metal
* @see clock_gettime() which polyfills this on Linux and BSDs
* @see getrusage() which polyfills this on XNU and NT
*/
int64_t clock(void) {
int e;
struct rusage ru;
struct timespec ts;
struct NtFileTime creation_time, exit_time, kernel_time, user_time;
int64_t proc, total;
// polyfill on Windows where CLOCK_PROCESS_CPUTIME_ID may be not available
if (IsWindows() && CLOCK_PROCESS_CPUTIME_ID == -1) {
proc = GetCurrentProcess();
if (!GetProcessTimes(proc, &creation_time, &exit_time, &kernel_time,
&user_time))
e = errno;
if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) == -1) {
errno = e;
if (getrusage(RUSAGE_SELF, &ru) != -1) {
ts = _timeval_totimespec(_timeval_add(ru.ru_utime, ru.ru_stime));
} else {
return -1;
total = ReadFileTime(kernel_time) + ReadFileTime(user_time);
ts = WindowsDurationToTimeSpec(total);
} else if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) == -1) {
return -1;
}
}
return ts.tv_sec * CLOCKS_PER_SEC +
ts.tv_nsec / (1000000000 / CLOCKS_PER_SEC);
// convert nanoseconds to microseconds w/ ceil rounding
// this would need roughly ~7,019,309 years to overflow
return ts.tv_sec * 1000000 + (ts.tv_nsec + 999) / 1000;
}

View file

@ -25,8 +25,6 @@
#include "libc/sysv/errfuns.h"
#include "libc/time/time.h"
int sys_clock_getres(int, struct timespec *) hidden;
static int sys_clock_getres_poly(int clock, struct timespec *ts, int64_t real) {
if (clock == CLOCK_REALTIME) {
ts->tv_sec = 0;
@ -65,6 +63,7 @@ int clock_getres(int clock, struct timespec *ts) {
} else {
rc = sys_clock_getres(clock, ts);
}
STRACE("clock_getres(%d, [%s]) → %d% m", clock, DescribeTimespec(rc, ts), rc);
STRACE("clock_getres(%s, [%s]) → %d% m", DescribeClockName(clock),
DescribeTimespec(rc, ts), rc);
return rc;
}

View file

@ -16,38 +16,33 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/clock_gettime.internal.h"
#include "libc/calls/struct/timespec.h"
#include "libc/intrin/pthread.h"
#include "libc/nexgen32e/rdtsc.h"
#include "libc/nexgen32e/x86feature.h"
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/errfuns.h"
#include "libc/time/clockstonanos.internal.h"
static struct {
pthread_once_t once;
uint64_t base;
struct timespec mono;
struct timespec base_wall;
uint64_t base_tick;
} g_mono;
static void sys_clock_gettime_mono_init(void) {
clock_gettime(CLOCK_REALTIME, &g_mono.mono);
g_mono.base = rdtsc();
g_mono.once = true;
g_mono.base_wall = _timespec_real();
g_mono.base_tick = rdtsc();
}
int sys_clock_gettime_mono(struct timespec *ts) {
// this routine stops being monotonic after 194 years of uptime
int sys_clock_gettime_mono(struct timespec *time) {
uint64_t nanos;
uint64_t cycles;
struct timespec res;
if (X86_HAVE(INVTSC)) {
pthread_once(&g_mono.once, sys_clock_gettime_mono_init);
nanos = ClocksToNanos(rdtsc(), g_mono.base);
res = g_mono.mono;
res.tv_sec += nanos / 1000000000;
res.tv_nsec += nanos % 1000000000;
*ts = res;
cycles = rdtsc() - g_mono.base_tick;
nanos = cycles / 3;
*time = _timespec_add(g_mono.base_wall, _timespec_fromnanos(nanos));
return 0;
} else {
return einval();

View file

@ -66,8 +66,8 @@ int clock_gettime(int clock, struct timespec *ts) {
}
#if SYSDEBUG
if (!__time_critical) {
STRACE("clock_gettime(%d, [%s]) → %d% m", clock, DescribeTimespec(rc, ts),
rc);
STRACE("clock_gettime(%s, [%s]) → %d% m", DescribeClockName(clock),
DescribeTimespec(rc, ts), rc);
}
#endif
return rc;

View file

@ -14,6 +14,8 @@ int futimens(int, const struct timespec[2]);
int nanosleep(const struct timespec *, struct timespec *);
int sys_futex(int *, int, int, const struct timespec *, int *);
int utimensat(int, const char *, const struct timespec[2], int);
int timespec_get(struct timespec *, int);
int timespec_getres(struct timespec *, int);
bool _timespec_eq(struct timespec, struct timespec) pureconst;
bool _timespec_gte(struct timespec, struct timespec) pureconst;
@ -21,6 +23,7 @@ int64_t _timespec_tomicros(struct timespec) pureconst;
int64_t _timespec_tomillis(struct timespec) pureconst;
int64_t _timespec_tonanos(struct timespec) pureconst;
struct timespec _timespec_add(struct timespec, struct timespec) pureconst;
struct timespec _timespec_fromnanos(int64_t) pureconst;
struct timespec _timespec_frommicros(int64_t) pureconst;
struct timespec _timespec_frommillis(int64_t) pureconst;
struct timespec _timespec_mono(void);

View file

@ -6,14 +6,15 @@
COSMOPOLITAN_C_START_
int __sys_utimensat(int, const char *, const struct timespec *, int) hidden;
int sys_clock_getres(int, struct timespec *) hidden;
int sys_clock_gettime(int, struct timespec *) hidden;
int sys_clock_gettime_nt(int, struct timespec *) hidden;
int sys_clock_gettime_xnu(int, struct timespec *) hidden;
int sys_futimens(int, const struct timespec *) hidden;
int sys_nanosleep(const struct timespec *, struct timespec *) hidden;
int sys_utimensat(int, const char *, const struct timespec *, int) hidden;
int sys_clock_gettime_nt(int, struct timespec *) hidden;
int sys_nanosleep_nt(const struct timespec *, struct timespec *) hidden;
int sys_nanosleep_xnu(const struct timespec *, struct timespec *) hidden;
int sys_utimensat(int, const char *, const struct timespec *, int) hidden;
int sys_utimensat_nt(int, const char *, const struct timespec *, int) hidden;
int sys_utimensat_xnu(int, const char *, const struct timespec *, int) hidden;

36
libc/calls/timespec_get.c Normal file
View file

@ -0,0 +1,36 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net 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/sysv/consts/clock.h"
#include "libc/time/time.h"
/**
* Returns high-precision timestamp, the C11 way.
*
* @param ts receives `CLOCK_REALTIME` timestamp
* @param base must be `TIME_UTC`
* @return `base` on success, or `0` on failure
*/
int timespec_get(struct timespec *ts, int base) {
if (base == TIME_UTC && !clock_gettime(CLOCK_REALTIME, ts)) {
return base;
} else {
return 0;
}
}

View file

@ -0,0 +1,36 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net 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/sysv/consts/clock.h"
#include "libc/time/time.h"
/**
* Returns high-precision timestamp granularity, the C23 way.
*
* @param ts receives granularity as a relative timestamp
* @param base must be `TIME_UTC`
* @return `base` on success, or `0` on failure
*/
int timespec_getres(struct timespec *ts, int base) {
if (base == TIME_UTC && !clock_getres(CLOCK_REALTIME, ts)) {
return base;
} else {
return 0;
}
}

View file

@ -158,6 +158,9 @@ struct thatispacked mayalias __usi128ma {
#define _mm_srli_si128(M128I, IMM) \
((__m128i)__builtin_ia32_psrldqi128((__v2di)(__m128i)(M128I), (int)(IMM)*8))
#define _mm_cmpeq_epi8(a, b) ((__m128i)((__v16qi)(a) == (__v16qi)(b)))
#define _mm_movemask_epi8(a) __builtin_ia32_pmovmskb128((__v16qi)(a))
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § it's a trap! » sse2 » scalar ops
*/
@ -218,19 +221,18 @@ struct thatispacked mayalias __usi128ma {
#define _mm_cmpunord_sd(M128D_0, M128D_1) \
__builtin_ia32_cmpunordsd((__v2df)(M128D_0), (__v2df)(M128D_1))
#define _mm_SSE2(op, A, B) \
({ \
__m128i R = A; \
asm(#op " %1, %0" \
: "+x"(R) : "xm"(B)); \
R; \
#define _mm_SSE2(op, A, B) \
({ \
__m128i R = A; \
asm(#op " %1, %0" : "+x"(R) : "xm"(B)); \
R; \
})
#define _mm_mul_epu32(A, B) _mm_SSE2(pmuludq, A, B)
#define _mm_add_epi64(A, B) _mm_SSE2(paddq, A, B)
#define _mm_srli_epi64(A, B) _mm_SSE2(psrlq, A, B)
#define _mm_slli_epi64(A, B) _mm_SSE2(psllq, A, B)
#define _mm_unpacklo_epi64(A, B) _mm_SSE2(punpcklqdq, A, B)
#define _mm_unpackhi_epi64(A, B) _mm_SSE2(punpckhqdq, A, B)
#define _mm_mul_epu32(A, B) _mm_SSE2(pmuludq, A, B)
#define _mm_add_epi64(A, B) _mm_SSE2(paddq, A, B)
#define _mm_srli_epi64(A, B) _mm_SSE2(psrlq, A, B)
#define _mm_slli_epi64(A, B) _mm_SSE2(psllq, A, B)
#define _mm_unpacklo_epi64(A, B) _mm_SSE2(punpcklqdq, A, B)
#define _mm_unpackhi_epi64(A, B) _mm_SSE2(punpckhqdq, A, B)
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § it's a trap! » sse2 » miscellaneous

View file

@ -17,10 +17,6 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#define ShouldUseMsabiAttribute() 1
#include "libc/intrin/bits.h"
#include "libc/intrin/likely.h"
#include "libc/intrin/safemacros.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/calls/calls.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
@ -28,11 +24,15 @@
#include "libc/errno.h"
#include "libc/fmt/divmod10.internal.h"
#include "libc/fmt/fmt.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/likely.h"
#include "libc/intrin/lockcmpxchg.h"
#include "libc/intrin/nomultics.internal.h"
#include "libc/intrin/safemacros.internal.h"
#include "libc/intrin/spinlock.h"
#include "libc/intrin/weaken.h"
#include "libc/limits.h"
#include "libc/log/internal.h"
#include "libc/macros.internal.h"
@ -53,7 +53,6 @@
#include "libc/str/utf16.h"
#include "libc/sysv/consts/nr.h"
#include "libc/sysv/consts/prot.h"
#include "libc/time/clockstonanos.internal.h"
extern hidden struct SymbolTable *__symtab;
@ -306,7 +305,7 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt,
continue;
case 'T':
x = ClocksToNanos(rdtsc(), kStartTsc) % 86400000000000;
x = (rdtsc() - kStartTsc) / 3 % 86400000000000;
goto FormatUnsigned;
case 'P':

View file

@ -1615,8 +1615,6 @@ syscon iff IFF_MASTER 0x0400 0 0 0 0 0
syscon iff IFF_PORTSEL 0x2000 0 0 0 0 0
syscon iff IFF_SLAVE 0x0800 0 0 0 0 0
syscon misc CLOCKS_PER_SEC 1000000 1000000 0x80 100 100 10000000
syscon sock SOCK_STREAM 1 1 1 1 1 1 # consensus
syscon sock SOCK_DGRAM 2 2 2 2 2 2 # consensus
syscon sock SOCK_RAW 3 3 3 3 3 3 # consensus

View file

@ -1,2 +0,0 @@
.include "o/libc/sysv/consts/syscon.internal.inc"
.syscon misc,CLOCKS_PER_SEC,1000000,1000000,0x80,100,100,10000000

View file

@ -1,2 +1,2 @@
.include "o/libc/sysv/consts/syscon.internal.inc"
.syscon sicode,SI_KERNEL,0x80,0x80000000,0x010006,0x80000000,0x80000000,0x80
.syscon sicode,SI_KERNEL,128,0x80000000,0x010006,0x80000000,0x80000000,0x80

View file

@ -5,6 +5,10 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
/**
* @fileoverview Cosmopolitan POSIX Thread Internals
*/
enum PosixThreadStatus {
kPosixThreadStarted,
kPosixThreadDetached,

View file

@ -33,13 +33,13 @@ static int PosixThread(void *arg, int tid) {
((cthread_t)__get_tls())->pthread = pt;
pt->rc = pt->start_routine(pt->arg);
}
if (atomic_load_explicit(&pt->status, memory_order_relaxed) ==
if (atomic_load_explicit(&pt->status, memory_order_acquire) ==
kPosixThreadDetached) {
atomic_store_explicit(&pt->status, kPosixThreadZombie,
memory_order_relaxed);
memory_order_release);
} else {
atomic_store_explicit(&pt->status, kPosixThreadTerminated,
memory_order_relaxed);
memory_order_release);
}
return 0;
}
@ -47,6 +47,25 @@ static int PosixThread(void *arg, int tid) {
/**
* Creates thread.
*
* Here's the OSI model of threads in Cosmopolitan:
*
*
* pthread_create() - Standard
* Abstraction
*
* _spawn() - Cosmopolitan
* Abstraction
*
* clone() - Polyfill
*
* - Kernel
* Interfaces
* sys_clone tfork
*
* CreateThread
* bsdthread_create thr_new
*
*
* @return 0 on success, or errno on error
*/
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,

View file

@ -16,7 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/atomic.h"
#include "libc/intrin/atomic.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"

View file

@ -1,5 +1,9 @@
#ifndef COSMOPOLITAN_LIBC_TIME_TIME_H_
#define COSMOPOLITAN_LIBC_TIME_TIME_H_
#define TIME_UTC 1
#define CLOCKS_PER_SEC 1000000L
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
@ -10,7 +14,6 @@ hidden extern const char kMonthName[12][10];
hidden extern const unsigned short kMonthYearDay[2][12];
extern char *tzname[2];
extern long CLOCKS_PER_SEC;
extern long timezone;
extern int daylight;

View file

@ -110,7 +110,6 @@ PyDoc_STRVAR(time_ns_doc,
Return the current time in nanoseconds since the Epoch.");
#ifdef HAVE_CLOCK
#define CLOCKS_PER_SEC CLK_TCK
static PyObject *
floatclock(_Py_clock_info_t *info)
{