diff --git a/libc/assert.h b/libc/assert.h index ce6933b2a..e7c8d22e4 100644 --- a/libc/assert.h +++ b/libc/assert.h @@ -1,5 +1,6 @@ #ifndef COSMOPOLITAN_LIBC_ASSERT_H_ #define COSMOPOLITAN_LIBC_ASSERT_H_ +#include "libc/bits/likely.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -10,7 +11,7 @@ void __assert_fail(const char *, const char *, int) hidden relegated; #define assert(EXPR) ((void)0) #else #define assert(EXPR) \ - ((void)((EXPR) || (__assert_fail(#EXPR, __FILE__, __LINE__), 0))) + ((void)(LIKELY(EXPR) || (__assert_fail(#EXPR, __FILE__, __LINE__), 0))) #endif #ifndef __cplusplus diff --git a/libc/calls/_timespec_eq.c b/libc/calls/_timespec_eq.c new file mode 100644 index 000000000..e0a1b9ccb --- /dev/null +++ b/libc/calls/_timespec_eq.c @@ -0,0 +1,26 @@ +/*-*- 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" + +/** + * Checks if 𝑥 = 𝑦. + */ +bool _timespec_eq(struct timespec x, struct timespec y) { + return x.tv_sec == y.tv_sec && x.tv_nsec == y.tv_nsec; +} diff --git a/libc/calls/_timespec_frommicros.c b/libc/calls/_timespec_frommicros.c new file mode 100644 index 000000000..261faba9a --- /dev/null +++ b/libc/calls/_timespec_frommicros.c @@ -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 microseconds. + */ +struct timespec _timespec_frommicros(int64_t x) { + struct timespec ts; + ts.tv_sec = x / 1000000; + ts.tv_nsec = x % 1000000 * 1000; + return ts; +} diff --git a/libc/calls/_timespec_frommillis.c b/libc/calls/_timespec_frommillis.c new file mode 100644 index 000000000..e52cb6e5e --- /dev/null +++ b/libc/calls/_timespec_frommillis.c @@ -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 milliseconds. + */ +struct timespec _timespec_frommillis(int64_t x) { + struct timespec ts; + ts.tv_sec = x / 1000; + ts.tv_nsec = x % 1000 * 1000000; + return ts; +} diff --git a/libc/calls/_timespec_gte.c b/libc/calls/_timespec_gte.c new file mode 100644 index 000000000..3b197a6fa --- /dev/null +++ b/libc/calls/_timespec_gte.c @@ -0,0 +1,28 @@ +/*-*- 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" + +/** + * Checks if 𝑥 ≥ 𝑦. + */ +bool _timespec_gte(struct timespec x, struct timespec y) { + if (x.tv_sec > y.tv_sec) return true; + if (x.tv_sec < y.tv_sec) return false; + return x.tv_nsec >= y.tv_nsec; +} diff --git a/libc/calls/_timespec_mono.c b/libc/calls/_timespec_mono.c new file mode 100644 index 000000000..191784f4f --- /dev/null +++ b/libc/calls/_timespec_mono.c @@ -0,0 +1,31 @@ +/*-*- 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/assert.h" +#include "libc/calls/struct/timespec.h" +#include "libc/sysv/consts/clock.h" +#include "libc/sysv/consts/nr.h" +#include "libc/time/time.h" + +struct timespec _timespec_mono(void) { + int ax, dx; + struct timespec ts; + ax = clock_gettime(CLOCK_MONOTONIC_FAST, &ts); + assert(!ax); + return ts; +} diff --git a/libc/calls/_timespec_real.c b/libc/calls/_timespec_real.c new file mode 100644 index 000000000..805c4b9d6 --- /dev/null +++ b/libc/calls/_timespec_real.c @@ -0,0 +1,31 @@ +/*-*- 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/assert.h" +#include "libc/calls/struct/timespec.h" +#include "libc/sysv/consts/clock.h" +#include "libc/sysv/consts/nr.h" +#include "libc/time/time.h" + +struct timespec _timespec_real(void) { + int ax, dx; + struct timespec ts; + ax = clock_gettime(CLOCK_REALTIME_FAST, &ts); + assert(!ax); + return ts; +} diff --git a/libc/calls/_timespec_tomicros.c b/libc/calls/_timespec_tomicros.c new file mode 100644 index 000000000..88757de56 --- /dev/null +++ b/libc/calls/_timespec_tomicros.c @@ -0,0 +1,32 @@ +/*-*- 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/limits.h" + +/** + * Converts timespec interval to microseconds. + */ +int64_t _timespec_tomicros(struct timespec x) { + int64_t us; + if (!__builtin_add_overflow(x.tv_sec, x.tv_nsec / 1000, &us)) { + return us; + } else { + return INT64_MAX; + } +} diff --git a/libc/calls/_timespec_tomillis.c b/libc/calls/_timespec_tomillis.c new file mode 100644 index 000000000..7d98127bf --- /dev/null +++ b/libc/calls/_timespec_tomillis.c @@ -0,0 +1,32 @@ +/*-*- 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/limits.h" + +/** + * Converts timespec interval to milliseconds. + */ +int64_t _timespec_tomillis(struct timespec x) { + int64_t us; + if (!__builtin_add_overflow(x.tv_sec, x.tv_nsec / 1000000, &us)) { + return us; + } else { + return INT64_MAX; + } +} diff --git a/libc/calls/calls.mk b/libc/calls/calls.mk index 4de9fca70..1dca6c8a0 100644 --- a/libc/calls/calls.mk +++ b/libc/calls/calls.mk @@ -179,6 +179,15 @@ o//libc/calls/getcwd-xnu.greg.o: \ OVERRIDE_CFLAGS += \ -Os +# we always want -O2 because: +# division is expensive if not optimized +o/$(MODE)/libc/calls/_timespec_tomillis.o \ +o/$(MODE)/libc/calls/_timespec_tomicros.o \ +o/$(MODE)/libc/calls/_timespec_frommillis.o \ +o/$(MODE)/libc/calls/_timespec_frommicros.o: \ + OVERRIDE_CFLAGS += \ + -O2 + o/$(MODE)/libc/calls/pledge.o \ o/$(MODE)/libc/calls/unveil.o: \ OVERRIDE_CFLAGS += \ diff --git a/libc/calls/struct/timespec.h b/libc/calls/struct/timespec.h index 720d9c480..6e9b454e2 100644 --- a/libc/calls/struct/timespec.h +++ b/libc/calls/struct/timespec.h @@ -8,9 +8,16 @@ struct timespec { }; int sys_futex(int *, int, int, const struct timespec *, int *); -bool _timespec_gt(struct timespec, struct timespec); -struct timespec _timespec_add(struct timespec, struct timespec); -struct timespec _timespec_sub(struct timespec, struct timespec); +int64_t _timespec_tomicros(struct timespec) pureconst; +int64_t _timespec_tomillis(struct timespec) pureconst; +struct timespec _timespec_frommicros(int64_t) pureconst; +struct timespec _timespec_frommillis(int64_t) pureconst; +bool _timespec_eq(struct timespec, struct timespec) pureconst; +bool _timespec_gte(struct timespec, struct timespec) pureconst; +struct timespec _timespec_add(struct timespec, struct timespec) pureconst; +struct timespec _timespec_sub(struct timespec, struct timespec) pureconst; +struct timespec _timespec_real(void); +struct timespec _timespec_mono(void); #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_TIMESPEC_H_ */ diff --git a/libc/calls/timespec_add.c b/libc/calls/timespec_add.c index 163cddf1e..ae9627da5 100644 --- a/libc/calls/timespec_add.c +++ b/libc/calls/timespec_add.c @@ -24,8 +24,8 @@ 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 >= 10000000000) { - x.tv_nsec -= 10000000000; + if (x.tv_nsec >= 1000000000) { + x.tv_nsec -= 1000000000; x.tv_sec += 1; } return x; diff --git a/libc/log/vflogf.c b/libc/log/vflogf.c index 9d4b4e191..6307bcb81 100644 --- a/libc/log/vflogf.c +++ b/libc/log/vflogf.c @@ -85,35 +85,39 @@ static void vflogf_onfail(FILE *f) { void(vflogf)(unsigned level, const char *file, int line, FILE *f, const char *fmt, va_list va) { int bufmode; + int64_t dots; struct tm tm; - long double t2; - const char *prog; - bool issamesecond; char buf32[32]; - int64_t secs, nsec, dots; + const char *prog; + const char *sign; + bool issamesecond; + struct timespec t2; if (!f) f = __log_file; if (!f) return; flockfile(f); --__strace; - t2 = nowl(); - secs = t2; - nsec = (t2 - secs) * 1e9L; - issamesecond = secs == vflogf_ts.tv_sec; - dots = issamesecond ? nsec - vflogf_ts.tv_nsec : nsec; - vflogf_ts.tv_sec = secs; - vflogf_ts.tv_nsec = nsec; + // We display TIMESTAMP.MICROS normally. However, when we log multiple + // times in the same second, we display TIMESTAMP+DELTAMICROS instead. + t2 = _timespec_real(); + if (t2.tv_sec == vflogf_ts.tv_sec) { + sign = "+"; + dots = t2.tv_nsec - vflogf_ts.tv_nsec; + } else { + sign = "."; + dots = t2.tv_nsec; + } + vflogf_ts = t2; - localtime_r(&secs, &tm); - strcpy(iso8601(buf32, &tm), issamesecond ? "+" : "."); + localtime_r(&t2.tv_sec, &tm); + strcpy(iso8601(buf32, &tm), sign); prog = basename(firstnonnull(program_invocation_name, "unknown")); bufmode = f->bufmode; if (bufmode == _IOLBF) f->bufmode = _IOFBF; if ((fprintf_unlocked)(f, "%r%c%s%06ld:%s:%d:%.*s:%d] ", - "FEWIVDNT"[level & 7], buf32, dots / 1000 % 1000000, - file, line, strchrnul(prog, '.') - prog, prog, - getpid()) <= 0) { + "FEWIVDNT"[level & 7], buf32, dots / 1000, file, line, + strchrnul(prog, '.') - prog, prog, getpid()) <= 0) { vflogf_onfail(f); } (vfprintf_unlocked)(f, fmt, va); diff --git a/test/libc/calls/_timespec_test.c b/test/libc/calls/_timespec_test.c new file mode 100644 index 000000000..46e29eabd --- /dev/null +++ b/test/libc/calls/_timespec_test.c @@ -0,0 +1,76 @@ +/*-*- 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/intrin/kprintf.h" +#include "libc/rand/rand.h" +#include "libc/testlib/testlib.h" + +TEST(_timespec_gte, test) { + EXPECT_FALSE(_timespec_gte((struct timespec){1}, (struct timespec){2})); + EXPECT_TRUE(_timespec_gte((struct timespec){2}, (struct timespec){2})); + EXPECT_TRUE(_timespec_gte((struct timespec){2}, (struct timespec){1})); + EXPECT_FALSE(_timespec_gte((struct timespec){2}, (struct timespec){2, 1})); + EXPECT_TRUE(_timespec_gte((struct timespec){2, 1}, (struct timespec){2})); +} + +TEST(_timespec_sub, test) { + EXPECT_TRUE( + _timespec_eq((struct timespec){-1}, + _timespec_sub((struct timespec){1}, (struct timespec){2}))); + EXPECT_TRUE( + _timespec_eq((struct timespec){1}, + _timespec_sub((struct timespec){2}, (struct timespec){1}))); + EXPECT_TRUE(_timespec_eq( + (struct timespec){1, 1}, + _timespec_sub((struct timespec){2, 2}, (struct timespec){1, 1}))); + EXPECT_TRUE(_timespec_eq( + (struct timespec){0, 999999999}, + _timespec_sub((struct timespec){2, 1}, (struct timespec){1, 2}))); +} + +TEST(_timespec_frommillis, test) { + EXPECT_TRUE( + _timespec_eq((struct timespec){0, 1000000}, _timespec_frommillis(1))); + EXPECT_TRUE( + _timespec_eq((struct timespec){0, 2000000}, _timespec_frommillis(2))); + EXPECT_TRUE(_timespec_eq((struct timespec){1}, _timespec_frommillis(1000))); +} + +TEST(_timespec_frommicros, test) { + EXPECT_TRUE( + _timespec_eq((struct timespec){0, 1000}, _timespec_frommicros(1))); + EXPECT_TRUE( + _timespec_eq((struct timespec){0, 2000}, _timespec_frommicros(2))); + EXPECT_TRUE( + _timespec_eq((struct timespec){1}, _timespec_frommicros(1000000))); +} + +static long mod(long x, long y) { + if (y == -1) return 0; + return x - y * (x / y - (x % y && (x ^ y) < 0)); +} + +TEST(_timespec_sub, math) { + for (int i = 0; i < 1000; ++i) { + struct timespec x = {mod(lemur64(), 10), mod(lemur64(), 10)}; + struct timespec y = {mod(lemur64(), 10), mod(lemur64(), 10)}; + struct timespec z = _timespec_add(_timespec_sub(x, y), y); + EXPECT_TRUE(_timespec_eq(x, _timespec_add(_timespec_sub(x, y), y))); + } +} diff --git a/test/libc/calls/clock_gettime_test.c b/test/libc/calls/clock_gettime_test.c index 97264ff93..78de1fc62 100644 --- a/test/libc/calls/clock_gettime_test.c +++ b/test/libc/calls/clock_gettime_test.c @@ -55,6 +55,7 @@ BENCH(clock_gettime, bench) { EZBENCH2("nowl", donothing, nowl()); EZBENCH2("rdtsc", donothing, rdtsc()); EZBENCH2("gettimeofday", donothing, gettimeofday(&tv, 0)); + EZBENCH2("_timespec_real", donothing, _timespec_real()); EZBENCH2("clock_gettime 0", donothing, clock_gettime(CLOCK_REALTIME_FAST, &ts)); EZBENCH2("clock_gettime 1", donothing, diff --git a/tool/net/help.txt b/tool/net/help.txt index c3dd4c4b7..2c6abe660 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -1374,9 +1374,10 @@ FUNCTIONS as Content-Range and Date, which are abstracted by the transport layer. - ProgramHeartbeatInterval(milliseconds:int) - Sets the heartbeat interval (in milliseconds). 5000ms is the default - and 100ms is the minimum. + ProgramHeartbeatInterval([milliseconds:int]) + Sets the heartbeat interval (in milliseconds). 5000ms is the + default and 100ms is the minimum. If `milliseconds` is not + specified, then the current interval is returned. ProgramTimeout(milliseconds:int|seconds:int) Default timeout is 60000ms. Minimal value of timeout is 10(ms). diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 67be08455..50698416d 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -328,9 +328,9 @@ static struct Assets { static struct Shared { int workers; - long double nowish; - long double lastreindex; - long double lastmeltdown; + struct timespec nowish; + struct timespec lastreindex; + struct timespec lastmeltdown; char currentdate[32]; struct rusage server; struct rusage children; @@ -430,7 +430,6 @@ static int sslpskindex; static int oldloglevel; static int messageshandled; static int sslticketlifetime; -static int heartbeatint = 5000; // ms static uint32_t clientaddrsize; static size_t zsize; @@ -470,16 +469,17 @@ static struct Buffer oldin; static struct Buffer hdrbuf; static struct timeval timeout; static struct Buffer effectivepath; +static struct timespec heartbeatinterval; static struct Url url; static struct stat zst; -static long double startread; -static long double lastrefresh; -static long double startserver; -static long double startrequest; -static long double lastheartbeat; -static long double startconnection; +static struct timespec startread; +static struct timespec lastrefresh; +static struct timespec startserver; +static struct timespec startrequest; +static struct timespec lastheartbeat; +static struct timespec startconnection; static struct sockaddr_in clientaddr; static struct sockaddr_in *serveraddr; @@ -1314,6 +1314,11 @@ static ssize_t ReadAll(int fd, char *p, size_t n) { return i; } +static bool IsTakingTooLong(void) { + return meltdown && _timespec_gte(_timespec_sub(_timespec_real(), startread), + (struct timespec){2}); +} + static ssize_t WritevAll(int fd, struct iovec *iov, int iovlen) { int i; ssize_t rc; @@ -1340,7 +1345,7 @@ static ssize_t WritevAll(int fd, struct iovec *iov, int iovlen) { } else if (errno == EINTR) { errno = 0; LockInc(&shared->c.writeinterruputs); - if (killed || (meltdown && nowl() - startread > 2)) { + if (killed || IsTakingTooLong()) { return total ? total : -1; } } else { @@ -1556,7 +1561,6 @@ static void CertsDestroy(void) { static void WipeServingKeys(void) { size_t i; - long double t = nowl(); if (uniprocess) return; mbedtls_ssl_ticket_free(&ssltick); mbedtls_ssl_key_cert_free(conf.key_cert), conf.key_cert = 0; @@ -1675,7 +1679,7 @@ static bool TlsSetup(void) { return true; } else if (r == MBEDTLS_ERR_SSL_WANT_READ) { LockInc(&shared->c.handshakeinterrupts); - if (terminated || killed || (meltdown && nowl() - startread > 2)) { + if (terminated || killed || IsTakingTooLong()) { return false; } } else { @@ -1935,10 +1939,10 @@ char *FormatUnixHttpDateTime(char *s, int64_t t) { return s; } -static void UpdateCurrentDate(long double now) { +static void UpdateCurrentDate(struct timespec now) { int64_t t; struct tm tm; - t = now; + t = now.tv_sec; shared->nowish = now; gmtime_r(&t, &tm); FormatHttpDateTime(shared->currentdate, &tm); @@ -2169,7 +2173,7 @@ static char *AppendCache(char *p, int64_t seconds) { p = stpcpy(p, ", must-revalidate"); } p = AppendCrlf(p); - return AppendExpires(p, (int64_t)shared->nowish + seconds); + return AppendExpires(p, shared->nowish.tv_sec + seconds); } static inline char *AppendContentLength(char *p, size_t n) { @@ -2923,7 +2927,7 @@ td { padding-right: 3em; }\r\n\ } appends(&cpm.outbuf, "