mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-12 09:17:53 +00:00
The worst issue I had with consts.sh for clock_gettime is how it defined too many clocks. So I looked into these clocks all day to figure out how how they overlap in functionality. I discovered counter-intuitive things such as how CLOCK_MONOTONIC should be CLOCK_UPTIME on MacOS and BSD, and that CLOCK_BOOTTIME should be CLOCK_MONOTONIC on MacOS / BSD. Windows 10 also has some incredible new APIs, that let us simplify clock_gettime(). - Linux CLOCK_REALTIME -> GetSystemTimePreciseAsFileTime() - Linux CLOCK_MONOTONIC -> QueryUnbiasedInterruptTimePrecise() - Linux CLOCK_MONOTONIC_RAW -> QueryUnbiasedInterruptTimePrecise() - Linux CLOCK_REALTIME_COARSE -> GetSystemTimeAsFileTime() - Linux CLOCK_MONOTONIC_COARSE -> QueryUnbiasedInterruptTime() - Linux CLOCK_BOOTTIME -> QueryInterruptTimePrecise() Documentation on the clock crew has been added to clock_gettime() in the docstring and in redbean's documentation too. You can read that to learn interesting facts about eight essential clocks that survived this purge. This is original research you will not find on Google, OpenAI, or Claude I've tested this change by porting *NSYNC to become fully clock agnostic since it has extensive tests for spotting irregularities in time. I have also included these tests in the default build so they no longer need to be run manually. Both CLOCK_REALTIME and CLOCK_MONOTONIC are good across the entire amd64 and arm64 test fleets.
333 lines
12 KiB
C
333 lines
12 KiB
C
/*-*- 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 2016 Google Inc. │
|
|
│ │
|
|
│ Licensed under the Apache License, Version 2.0 (the "License"); │
|
|
│ you may not use this file except in compliance with the License. │
|
|
│ You may obtain a copy of the License at │
|
|
│ │
|
|
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
|
│ │
|
|
│ Unless required by applicable law or agreed to in writing, software │
|
|
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
|
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
|
│ See the License for the specific language governing permissions and │
|
|
│ limitations under the License. │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "third_party/nsync/note.h"
|
|
#include "third_party/nsync/testing/closure.h"
|
|
#include "third_party/nsync/testing/smprintf.h"
|
|
#include "third_party/nsync/testing/testing.h"
|
|
#include "third_party/nsync/testing/time_extra.h"
|
|
#include "third_party/nsync/time.h"
|
|
|
|
/* Verify the properties of a prenotified note. */
|
|
static void test_note_prenotified (testing t) {
|
|
int i;
|
|
nsync_note n = nsync_note_new (NULL, NSYNC_CLOCK, nsync_time_zero /* prenotified */);
|
|
nsync_time expiry;
|
|
expiry = nsync_note_expiry (n);
|
|
if (nsync_time_cmp (expiry, nsync_time_zero) != 0) {
|
|
TEST_ERROR (t, ("prenotified note time mismatch 0"));
|
|
}
|
|
for (i = 0; i != 2; i++) {
|
|
if (!nsync_note_is_notified (n)) {
|
|
TEST_ERROR (t, ("prenotified note is not notified (test, %d)", i));
|
|
}
|
|
if (!nsync_note_wait (n, nsync_time_zero)) {
|
|
TEST_ERROR (t, ("prenotified note is not notified (poll, %d)", i));
|
|
}
|
|
if (!nsync_note_wait (n, nsync_time_no_deadline)) {
|
|
TEST_ERROR (t, ("prenotified note is not notified (infinite wait, %d)", i));
|
|
}
|
|
nsync_note_notify (n);
|
|
}
|
|
expiry = nsync_note_expiry (n);
|
|
if (nsync_time_cmp (expiry, nsync_time_zero) != 0) {
|
|
TEST_ERROR (t, ("prenotified note time mismatch 1"));
|
|
}
|
|
nsync_note_free (n);
|
|
}
|
|
|
|
/* Verify the properties of a unnotified note. */
|
|
static void test_note_unnotified (testing t) {
|
|
nsync_time start;
|
|
nsync_time waited;
|
|
nsync_time deadline;
|
|
nsync_note n = nsync_note_new (NULL, NSYNC_CLOCK, nsync_time_no_deadline);
|
|
nsync_time expiry;
|
|
expiry = nsync_note_expiry (n);
|
|
if (nsync_time_cmp (expiry, nsync_time_no_deadline) != 0) {
|
|
TEST_ERROR (t, ("unnotified note time mismatch 0"));
|
|
}
|
|
|
|
if (nsync_note_is_notified (n)) {
|
|
TEST_ERROR (t, ("unnotified note is notified (test)"));
|
|
}
|
|
if (nsync_note_wait (n, nsync_time_zero)) {
|
|
TEST_ERROR (t, ("notified note is notified (poll)"));
|
|
}
|
|
start = nsync_time_now (NSYNC_CLOCK);
|
|
deadline = nsync_time_add (nsync_time_now (NSYNC_CLOCK), nsync_time_ms (1000));
|
|
if (nsync_note_wait (n, deadline)) {
|
|
TEST_ERROR (t, ("unnotified note is notified (1s wait)"));
|
|
}
|
|
waited = nsync_time_sub (nsync_time_now (NSYNC_CLOCK), start);
|
|
if (nsync_time_cmp (waited, nsync_time_ms (900)) < 0) {
|
|
TEST_ERROR (t, ("timed wait on unnotified note returned too quickly (1s wait took %s)",
|
|
nsync_time_str (waited, 2)));
|
|
}
|
|
if (nsync_time_cmp (waited, nsync_time_ms (2000)) > 0) {
|
|
TEST_ERROR (t, ("timed wait on unnotified note returned too slowly (1s wait took %s)",
|
|
nsync_time_str (waited, 2)));
|
|
}
|
|
|
|
nsync_note_notify (n);
|
|
|
|
if (!nsync_note_is_notified (n)) {
|
|
TEST_ERROR (t, ("notified note is not notified (test)"));
|
|
}
|
|
if (!nsync_note_wait (n, nsync_time_zero)) {
|
|
TEST_ERROR (t, ("notified note is not notified (poll)"));
|
|
}
|
|
if (!nsync_note_wait (n, nsync_time_no_deadline)) {
|
|
TEST_ERROR (t, ("notified note is not notified (infinite wait)"));
|
|
}
|
|
|
|
expiry = nsync_note_expiry (n);
|
|
if (nsync_time_cmp (expiry, nsync_time_no_deadline) != 0) {
|
|
TEST_ERROR (t, ("unnotified note time mismatch 1"));
|
|
}
|
|
|
|
nsync_note_free (n);
|
|
}
|
|
|
|
/* Test expiry on a note. */
|
|
static void test_note_expiry (testing t) {
|
|
nsync_time start;
|
|
nsync_time waited;
|
|
nsync_time deadline;
|
|
nsync_note n;
|
|
|
|
deadline = nsync_time_add (nsync_time_now (NSYNC_CLOCK), nsync_time_ms (1000));
|
|
n = nsync_note_new (NULL, NSYNC_CLOCK, deadline);
|
|
start = nsync_time_now (NSYNC_CLOCK);
|
|
if (!nsync_note_wait (n, nsync_time_no_deadline)) {
|
|
TEST_ERROR (t, ("expired note is not notified"));
|
|
}
|
|
waited = nsync_time_sub (nsync_time_now (NSYNC_CLOCK), start);
|
|
if (nsync_time_cmp (waited, nsync_time_ms (900)) < 0) {
|
|
TEST_ERROR (t, ("note expired too quickly (1s expiry took %s)",
|
|
nsync_time_str (waited, 2)));
|
|
}
|
|
if (nsync_time_cmp (waited, nsync_time_ms (2000)) > 0) {
|
|
TEST_ERROR (t, ("timed expired too slowly (1s expiry took %s)",
|
|
nsync_time_str (waited, 2)));
|
|
}
|
|
if (!nsync_note_is_notified (n)) {
|
|
TEST_ERROR (t, ("expired note note is not notified (test)"));
|
|
}
|
|
if (!nsync_note_wait (n, nsync_time_zero)) {
|
|
TEST_ERROR (t, ("expired note note is not notified (poll)"));
|
|
}
|
|
if (!nsync_note_wait (n, nsync_time_no_deadline)) {
|
|
TEST_ERROR (t, ("expired note note is not notified (infinite wait)"));
|
|
}
|
|
nsync_note_free (n);
|
|
|
|
deadline = nsync_time_add (nsync_time_now (NSYNC_CLOCK), nsync_time_ms (1000));
|
|
n = nsync_note_new (NULL, NSYNC_CLOCK, deadline);
|
|
start = nsync_time_now (NSYNC_CLOCK);
|
|
while (!nsync_note_is_notified (n)) {
|
|
nsync_time_sleep (NSYNC_CLOCK, nsync_time_ms (10));
|
|
}
|
|
waited = nsync_time_sub (nsync_time_now (NSYNC_CLOCK), start);
|
|
if (nsync_time_cmp (waited, nsync_time_ms (900)) < 0) {
|
|
TEST_ERROR (t, ("note expired too quickly (1s expiry took %s)",
|
|
nsync_time_str (waited, 2)));
|
|
}
|
|
if (nsync_time_cmp (waited, nsync_time_ms (2000)) > 0) {
|
|
TEST_ERROR (t, ("timed expired too slowly (1s expiry took %s)",
|
|
nsync_time_str (waited, 2)));
|
|
}
|
|
if (!nsync_note_is_notified (n)) {
|
|
TEST_ERROR (t, ("expired note note is not notified (test)"));
|
|
}
|
|
if (!nsync_note_wait (n, nsync_time_zero)) {
|
|
TEST_ERROR (t, ("expired note note is not notified (poll)"));
|
|
}
|
|
if (!nsync_note_wait (n, nsync_time_no_deadline)) {
|
|
TEST_ERROR (t, ("expired note note is not notified (infinite wait)"));
|
|
}
|
|
nsync_note_free (n);
|
|
}
|
|
|
|
static void notify_at (nsync_note n, nsync_time abs_deadline) {
|
|
nsync_time_sleep_until (NSYNC_CLOCK, abs_deadline);
|
|
nsync_note_notify (n);
|
|
}
|
|
|
|
CLOSURE_DECL_BODY2 (notify, nsync_note, nsync_time)
|
|
|
|
/* Test notification of a note. */
|
|
static void test_note_notify (testing t) {
|
|
nsync_time start;
|
|
nsync_time waited;
|
|
nsync_time deadline;
|
|
nsync_note n;
|
|
|
|
deadline = nsync_time_add (nsync_time_now (NSYNC_CLOCK), nsync_time_ms (10000));
|
|
n = nsync_note_new (NULL, NSYNC_CLOCK, deadline);
|
|
closure_fork (closure_notify (¬ify_at, n, nsync_time_add (nsync_time_now (NSYNC_CLOCK), nsync_time_ms (1000))));
|
|
start = nsync_time_now (NSYNC_CLOCK);
|
|
if (!nsync_note_wait (n, nsync_time_no_deadline)) {
|
|
TEST_ERROR (t, ("expired note is not notified"));
|
|
}
|
|
waited = nsync_time_sub (nsync_time_now (NSYNC_CLOCK), start);
|
|
if (nsync_time_cmp (waited, nsync_time_ms (900)) < 0) {
|
|
TEST_ERROR (t, ("note expired too quickly (1s expiry took %s)",
|
|
nsync_time_str (waited, 2)));
|
|
}
|
|
if (nsync_time_cmp (waited, nsync_time_ms (2000)) > 0) {
|
|
TEST_ERROR (t, ("timed expired too slowly (1s expiry took %s)",
|
|
nsync_time_str (waited, 2)));
|
|
}
|
|
if (!nsync_note_is_notified (n)) {
|
|
TEST_ERROR (t, ("expired note note is not notified (test)"));
|
|
}
|
|
if (!nsync_note_wait (n, nsync_time_zero)) {
|
|
TEST_ERROR (t, ("expired note note is not notified (poll)"));
|
|
}
|
|
if (!nsync_note_wait (n, nsync_time_no_deadline)) {
|
|
TEST_ERROR (t, ("expired note note is not notified (infinite wait)"));
|
|
}
|
|
nsync_note_free (n);
|
|
|
|
deadline = nsync_time_add (nsync_time_now (NSYNC_CLOCK), nsync_time_ms (10000));
|
|
n = nsync_note_new (NULL, NSYNC_CLOCK, deadline);
|
|
closure_fork (closure_notify (¬ify_at, n, nsync_time_add (nsync_time_now (NSYNC_CLOCK), nsync_time_ms (1000))));
|
|
start = nsync_time_now (NSYNC_CLOCK);
|
|
while (!nsync_note_is_notified (n)) {
|
|
nsync_time_sleep (NSYNC_CLOCK, nsync_time_ms (10));
|
|
}
|
|
waited = nsync_time_sub (nsync_time_now (NSYNC_CLOCK), start);
|
|
if (nsync_time_cmp (waited, nsync_time_ms (900)) < 0) {
|
|
TEST_ERROR (t, ("note expired too quickly (1s expiry took %s)",
|
|
nsync_time_str (waited, 2)));
|
|
}
|
|
if (nsync_time_cmp (waited, nsync_time_ms (2000)) > 0) {
|
|
TEST_ERROR (t, ("timed expired too slowly (1s expiry took %s)",
|
|
nsync_time_str (waited, 2)));
|
|
}
|
|
if (!nsync_note_is_notified (n)) {
|
|
TEST_ERROR (t, ("expired note note is not notified (test)"));
|
|
}
|
|
if (!nsync_note_wait (n, nsync_time_zero)) {
|
|
TEST_ERROR (t, ("expired note note is not notified (poll)"));
|
|
}
|
|
if (!nsync_note_wait (n, nsync_time_no_deadline)) {
|
|
TEST_ERROR (t, ("expired note note is not notified (infinite wait)"));
|
|
}
|
|
nsync_note_free (n);
|
|
}
|
|
|
|
/* Test notification of parent/child note. */
|
|
static void test_note_in_tree (testing t) {
|
|
int i;
|
|
enum { /* Indexes of nodes that form a heap in the array node[]. */
|
|
parent_i = 0,
|
|
focus_i = 1,
|
|
sibling_i = 2,
|
|
child0_i = 3,
|
|
child1_i = 4,
|
|
nephew0_i = 5,
|
|
nephew1_i = 6,
|
|
grandchild00 = 7,
|
|
grandchild01 = 8,
|
|
grandchild10 = 9,
|
|
grandchild11 = 10,
|
|
|
|
count_i = 11
|
|
};
|
|
nsync_note node[count_i];
|
|
|
|
/* Initialize heap structure in the nodes. No deadlines. */
|
|
node[0] = nsync_note_new (NULL, NSYNC_CLOCK, nsync_time_no_deadline);
|
|
for (i = 1; i != count_i; i++) {
|
|
node[i] = nsync_note_new (node[(i-1)/2], NSYNC_CLOCK, nsync_time_no_deadline);
|
|
}
|
|
|
|
/* check that the nodes are not yet notified. */
|
|
for (i = 0; i != count_i; i++) {
|
|
if (nsync_note_is_notified (node[i])) {
|
|
TEST_ERROR (t, ("unnotified note %d is notified", i));
|
|
}
|
|
}
|
|
|
|
/* Notify the focus node */
|
|
nsync_note_notify (node[focus_i]);
|
|
|
|
/* Check that the right nodes have been notified. */
|
|
for (i = 0; i != count_i; i++) {
|
|
int is_notified = nsync_note_is_notified (node[i]);
|
|
if (i == parent_i || i == sibling_i || i == nephew0_i || i == nephew1_i) {
|
|
/* Parent, sibling, and nephew nodes should not have been notified. */
|
|
if (is_notified) {
|
|
TEST_ERROR (t, ("unnotified note %d is notified", i));
|
|
}
|
|
} else if (!is_notified) { /* But the node and its descendents should be. */
|
|
TEST_ERROR (t, ("notified note %d is not notified", i));
|
|
}
|
|
}
|
|
for (i = 0; i != count_i; i++) {
|
|
nsync_note_free (node[i]);
|
|
}
|
|
|
|
/* Initialize heap structure in the nodes. The focus node has a 1s deadline. */
|
|
node[0] = nsync_note_new (NULL, NSYNC_CLOCK, nsync_time_no_deadline);
|
|
for (i = 1; i != count_i; i++) {
|
|
nsync_time deadline;
|
|
deadline = nsync_time_add (nsync_time_now (NSYNC_CLOCK), nsync_time_ms (1000));
|
|
if (i != focus_i) {
|
|
deadline = nsync_time_no_deadline;
|
|
}
|
|
node[i] = nsync_note_new (node[(i - 1) / 2], NSYNC_CLOCK, deadline);
|
|
}
|
|
|
|
/* check that the nodes are not yet notified. */
|
|
for (i = 0; i != count_i; i++) {
|
|
if (nsync_note_is_notified (node[i])) {
|
|
TEST_ERROR (t, ("unnotified note %d is notified", i));
|
|
}
|
|
}
|
|
|
|
/* Wait for timer to go off. */
|
|
nsync_time_sleep (NSYNC_CLOCK, nsync_time_ms (1100));
|
|
|
|
/* Check that the right nodes have been notified. */
|
|
for (i = 0; i != count_i; i++) {
|
|
int is_notified = nsync_note_is_notified (node[i]);
|
|
if (i == parent_i || i == sibling_i || i == nephew0_i || i == nephew1_i) {
|
|
/* Parent, sibling, and nephew nodes should not have been notified. */
|
|
if (is_notified) {
|
|
TEST_ERROR (t, ("unnotified note %d is notified", i));
|
|
}
|
|
} else if (!is_notified) { /* But the node and its descendents should be. */
|
|
TEST_ERROR (t, ("notified note %d is not notified", i));
|
|
}
|
|
}
|
|
for (i = 0; i != count_i; i++) {
|
|
nsync_note_free (node[i]);
|
|
}
|
|
}
|
|
|
|
int main (int argc, char *argv[]) {
|
|
testing_base tb = testing_new (argc, argv, 0);
|
|
TEST_RUN (tb, test_note_prenotified);
|
|
TEST_RUN (tb, test_note_unnotified);
|
|
TEST_RUN (tb, test_note_expiry);
|
|
TEST_RUN (tb, test_note_notify);
|
|
TEST_RUN (tb, test_note_in_tree);
|
|
return (testing_base_exit (tb));
|
|
}
|