mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-05-23 05:42:29 +00:00
Improve cancellations, randomness, and time
- Exhaustively document cancellation points - Rename SIGCANCEL to SIGTHR just like BSDs - Further improve POSIX thread cancellations - Ensure asynchronous cancellations work correctly - Elevate the quality of getrandom() and getentropy() - Make futexes cancel correctly on OpenBSD 6.x and 7.x - Add reboot.com and shutdown.com to examples directory - Remove underscore prefix from awesome timespec_*() APIs - Create assertions that help verify our cancellation points - Remove bad timespec APIs (cmp generalizes eq/ne/gt/gte/lt/lte)
This commit is contained in:
parent
0d7c265392
commit
3f0bcdc3ef
173 changed files with 1599 additions and 782 deletions
|
@ -23,73 +23,65 @@
|
|||
#include "libc/testlib/testlib.h"
|
||||
#include "third_party/nsync/time.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) {
|
||||
TEST(timespec_sub, test) {
|
||||
EXPECT_TRUE(
|
||||
_timespec_eq((struct timespec){-1},
|
||||
_timespec_sub((struct timespec){1}, (struct timespec){2})));
|
||||
!timespec_cmp((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(
|
||||
!timespec_cmp((struct timespec){1},
|
||||
timespec_sub((struct timespec){2}, (struct timespec){1})));
|
||||
EXPECT_TRUE(!timespec_cmp(
|
||||
(struct timespec){1, 1},
|
||||
_timespec_sub((struct timespec){2, 2}, (struct timespec){1, 1})));
|
||||
EXPECT_TRUE(_timespec_eq(
|
||||
timespec_sub((struct timespec){2, 2}, (struct timespec){1, 1})));
|
||||
EXPECT_TRUE(!timespec_cmp(
|
||||
(struct timespec){0, 999999999},
|
||||
_timespec_sub((struct timespec){2, 1}, (struct timespec){1, 2})));
|
||||
timespec_sub((struct timespec){2, 1}, (struct timespec){1, 2})));
|
||||
}
|
||||
|
||||
TEST(_timespec_frommillis, test) {
|
||||
TEST(timespec_frommillis, test) {
|
||||
EXPECT_TRUE(
|
||||
_timespec_eq((struct timespec){0, 1000000}, _timespec_frommillis(1)));
|
||||
!timespec_cmp((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)));
|
||||
!timespec_cmp((struct timespec){0, 2000000}, timespec_frommillis(2)));
|
||||
EXPECT_TRUE(!timespec_cmp((struct timespec){1}, timespec_frommillis(1000)));
|
||||
}
|
||||
|
||||
TEST(_timespec_frommicros, test) {
|
||||
TEST(timespec_frommicros, test) {
|
||||
EXPECT_TRUE(
|
||||
_timespec_eq((struct timespec){0, 1000}, _timespec_frommicros(1)));
|
||||
!timespec_cmp((struct timespec){0, 1000}, timespec_frommicros(1)));
|
||||
EXPECT_TRUE(
|
||||
_timespec_eq((struct timespec){0, 2000}, _timespec_frommicros(2)));
|
||||
!timespec_cmp((struct timespec){0, 2000}, timespec_frommicros(2)));
|
||||
EXPECT_TRUE(
|
||||
_timespec_eq((struct timespec){1}, _timespec_frommicros(1000000)));
|
||||
EXPECT_TRUE(_timespec_eq((struct timespec){2, 123000},
|
||||
_timespec_frommicros(2000123)));
|
||||
!timespec_cmp((struct timespec){1}, timespec_frommicros(1000000)));
|
||||
EXPECT_TRUE(!timespec_cmp((struct timespec){2, 123000},
|
||||
timespec_frommicros(2000123)));
|
||||
}
|
||||
|
||||
TEST(_timespec_tomillis, test) {
|
||||
EXPECT_EQ(0, _timespec_tomillis((struct timespec){0, 0}));
|
||||
EXPECT_EQ(1, _timespec_tomillis((struct timespec){0, 1}));
|
||||
EXPECT_EQ(1, _timespec_tomillis((struct timespec){0, 999999}));
|
||||
EXPECT_EQ(1, _timespec_tomillis((struct timespec){0, 1000000}));
|
||||
EXPECT_EQ(1000, _timespec_tomillis((struct timespec){0, 999999999}));
|
||||
EXPECT_EQ(2123, _timespec_tomillis((struct timespec){2, 123000000}));
|
||||
EXPECT_EQ(INT64_MAX, _timespec_tomillis((struct timespec){INT64_MAX, 0}));
|
||||
EXPECT_EQ(INT64_MIN, _timespec_tomillis((struct timespec){INT64_MIN, 0}));
|
||||
EXPECT_EQ(INT64_MAX, _timespec_tomillis(
|
||||
TEST(timespec_tomillis, test) {
|
||||
EXPECT_EQ(0, timespec_tomillis((struct timespec){0, 0}));
|
||||
EXPECT_EQ(1, timespec_tomillis((struct timespec){0, 1}));
|
||||
EXPECT_EQ(1, timespec_tomillis((struct timespec){0, 999999}));
|
||||
EXPECT_EQ(1, timespec_tomillis((struct timespec){0, 1000000}));
|
||||
EXPECT_EQ(1000, timespec_tomillis((struct timespec){0, 999999999}));
|
||||
EXPECT_EQ(2123, timespec_tomillis((struct timespec){2, 123000000}));
|
||||
EXPECT_EQ(INT64_MAX, timespec_tomillis((struct timespec){INT64_MAX, 0}));
|
||||
EXPECT_EQ(INT64_MIN, timespec_tomillis((struct timespec){INT64_MIN, 0}));
|
||||
EXPECT_EQ(INT64_MAX, timespec_tomillis(
|
||||
(struct timespec){0x7fffffffffffffff, 999999999}));
|
||||
}
|
||||
|
||||
TEST(_timespec_tomicros, test) {
|
||||
EXPECT_EQ(0, _timespec_tomicros((struct timespec){0, 0}));
|
||||
EXPECT_EQ(1, _timespec_tomicros((struct timespec){0, 1}));
|
||||
EXPECT_EQ(2000123, _timespec_tomicros((struct timespec){2, 123000}));
|
||||
EXPECT_EQ(INT64_MAX, _timespec_tomicros((struct timespec){INT64_MAX, 0}));
|
||||
EXPECT_EQ(INT64_MIN, _timespec_tomicros((struct timespec){INT64_MIN, 0}));
|
||||
TEST(timespec_tomicros, test) {
|
||||
EXPECT_EQ(0, timespec_tomicros((struct timespec){0, 0}));
|
||||
EXPECT_EQ(1, timespec_tomicros((struct timespec){0, 1}));
|
||||
EXPECT_EQ(2000123, timespec_tomicros((struct timespec){2, 123000}));
|
||||
EXPECT_EQ(INT64_MAX, timespec_tomicros((struct timespec){INT64_MAX, 0}));
|
||||
EXPECT_EQ(INT64_MIN, timespec_tomicros((struct timespec){INT64_MIN, 0}));
|
||||
}
|
||||
|
||||
TEST(_timespec_tonanos, test) {
|
||||
EXPECT_EQ(2000123000, _timespec_tonanos((struct timespec){2, 123000}));
|
||||
EXPECT_EQ(INT64_MAX, _timespec_tonanos((struct timespec){INT64_MAX, 0}));
|
||||
EXPECT_EQ(INT64_MIN, _timespec_tonanos((struct timespec){INT64_MIN, 0}));
|
||||
TEST(timespec_tonanos, test) {
|
||||
EXPECT_EQ(2000123000, timespec_tonanos((struct timespec){2, 123000}));
|
||||
EXPECT_EQ(INT64_MAX, timespec_tonanos((struct timespec){INT64_MAX, 0}));
|
||||
EXPECT_EQ(INT64_MIN, timespec_tonanos((struct timespec){INT64_MIN, 0}));
|
||||
}
|
||||
|
||||
static long mod(long x, long y) {
|
||||
|
@ -97,11 +89,11 @@ static long mod(long x, long y) {
|
|||
return x - y * (x / y - (x % y && (x ^ y) < 0));
|
||||
}
|
||||
|
||||
TEST(_timespec_sub, math) {
|
||||
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)));
|
||||
struct timespec z = timespec_add(timespec_sub(x, y), y);
|
||||
EXPECT_TRUE(!timespec_cmp(x, timespec_add(timespec_sub(x, y), y)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,7 +57,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("timespec_real", donothing, timespec_real());
|
||||
EZBENCH2("clock_gettime 0", donothing,
|
||||
clock_gettime(CLOCK_REALTIME_FAST, &ts));
|
||||
EZBENCH2("clock_gettime 1", donothing,
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/blockcancel.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/flock.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
|
@ -50,7 +51,9 @@ bool SupportsOfdLocks(void) {
|
|||
// getrandom() was introduced in linux 3.17
|
||||
// testing for getrandom() should be a sure thing w/o creating an fd
|
||||
e = errno;
|
||||
BLOCK_CANCELLATIONS;
|
||||
r = !sys_getrandom(0, 0, 0);
|
||||
ALLOW_CANCELLATIONS;
|
||||
errno = e;
|
||||
return r;
|
||||
}
|
||||
|
|
|
@ -26,10 +26,12 @@
|
|||
TEST(DescribeSigset, full) {
|
||||
sigset_t ss;
|
||||
sigfillset(&ss);
|
||||
if (IsXnu() || IsOpenbsd()) {
|
||||
EXPECT_STREQ("~{ABRT,CANCEL,KILL,STOP}", DescribeSigset(0, &ss));
|
||||
if (IsXnu()) {
|
||||
EXPECT_STREQ("~{ABRT,THR,KILL,STOP}", DescribeSigset(0, &ss));
|
||||
} else if (IsOpenbsd()) {
|
||||
EXPECT_STREQ("~{ABRT,KILL,STOP,THR}", DescribeSigset(0, &ss));
|
||||
} else {
|
||||
EXPECT_STREQ("~{ABRT,KILL,STOP,CANCEL}", DescribeSigset(0, &ss));
|
||||
EXPECT_STREQ("~{ABRT,KILL,STOP,THR}", DescribeSigset(0, &ss));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,11 +48,11 @@ TEST(DescribeSigset, absent) {
|
|||
sigfillset(&ss);
|
||||
sigdelset(&ss, SIGINT);
|
||||
sigdelset(&ss, SIGUSR1);
|
||||
if (IsOpenbsd() || IsXnu()) {
|
||||
EXPECT_STREQ("~{INT,ABRT,CANCEL,KILL,STOP,USR1}", DescribeSigset(0, &ss));
|
||||
} else if (IsFreebsd() || IsNetbsd()) {
|
||||
EXPECT_STREQ("~{INT,ABRT,KILL,STOP,USR1,CANCEL}", DescribeSigset(0, &ss));
|
||||
if (IsXnu()) {
|
||||
EXPECT_STREQ("~{INT,ABRT,THR,KILL,STOP,USR1}", DescribeSigset(0, &ss));
|
||||
} else if (IsBsd()) {
|
||||
EXPECT_STREQ("~{INT,ABRT,KILL,STOP,USR1,THR}", DescribeSigset(0, &ss));
|
||||
} else {
|
||||
EXPECT_STREQ("~{INT,ABRT,KILL,USR1,STOP,CANCEL}", DescribeSigset(0, &ss));
|
||||
EXPECT_STREQ("~{INT,ABRT,KILL,USR1,STOP,THR}", DescribeSigset(0, &ss));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -131,7 +131,7 @@ void TestContendedLock(const char *name, int kind) {
|
|||
_Exit(1);
|
||||
}
|
||||
while (!atomic_load(&ready)) donothing;
|
||||
t1 = _timespec_real();
|
||||
t1 = timespec_real();
|
||||
for (i = 0; i < n; ++i) {
|
||||
ASSERT_EQ(0, pthread_mutex_lock(&mu));
|
||||
x = atomic_load_explicit(&counter, memory_order_relaxed);
|
||||
|
@ -139,13 +139,13 @@ void TestContendedLock(const char *name, int kind) {
|
|||
ASSERT_EQ(x - 1, atomic_load_explicit(&counter, memory_order_relaxed));
|
||||
ASSERT_EQ(0, pthread_mutex_unlock(&mu));
|
||||
}
|
||||
t2 = _timespec_real();
|
||||
t2 = timespec_real();
|
||||
while (tib.tib_tid) donothing;
|
||||
ASSERT_EQ(1, atomic_load(&success));
|
||||
ASSERT_EQ(0, atomic_load(&counter));
|
||||
_freestack(stk);
|
||||
ASSERT_EQ(0, pthread_mutex_destroy(&mu));
|
||||
ns = time2dbl(_timespec_sub(t2, t1)) / n;
|
||||
ns = time2dbl(timespec_sub(t2, t1)) / n;
|
||||
kprintf("%s contended took %s\n", name, time2str(ns));
|
||||
}
|
||||
|
||||
|
@ -159,14 +159,14 @@ void TestUncontendedLock(const char *name, int kind) {
|
|||
pthread_mutexattr_settype(&attr, kind);
|
||||
pthread_mutex_init(&lock, &attr);
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
t1 = _timespec_real();
|
||||
t1 = timespec_real();
|
||||
for (i = 0; i < n; ++i) {
|
||||
pthread_mutex_lock(&lock);
|
||||
pthread_mutex_unlock(&lock);
|
||||
}
|
||||
t2 = _timespec_real();
|
||||
t2 = timespec_real();
|
||||
pthread_mutex_destroy(&lock);
|
||||
ns = time2dbl(_timespec_sub(t2, t1)) / n;
|
||||
ns = time2dbl(timespec_sub(t2, t1)) / n;
|
||||
kprintf("%s took %s\n", name, time2str(ns));
|
||||
}
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ void *Waiter(void *arg) {
|
|||
BENCH(lock, scalability) {
|
||||
int i;
|
||||
struct timespec t1, t2;
|
||||
t1 = _timespec_real();
|
||||
t1 = timespec_real();
|
||||
pthread_mutex_init(&lock, 0);
|
||||
pthread_barrier_init(&barrier, 0, WAITERS + 1);
|
||||
for (i = 0; i < WAITERS; ++i) {
|
||||
|
@ -79,8 +79,8 @@ BENCH(lock, scalability) {
|
|||
}
|
||||
pthread_barrier_destroy(&barrier);
|
||||
pthread_mutex_destroy(&lock);
|
||||
t2 = _timespec_real();
|
||||
t2 = timespec_real();
|
||||
printf("consumed %10g seconds real time and %10g seconds cpu time\n",
|
||||
_timespec_tonanos(_timespec_sub(t2, t1)) / 1e9,
|
||||
timespec_tonanos(timespec_sub(t2, t1)) / 1e9,
|
||||
(double)clock() / CLOCKS_PER_SEC);
|
||||
}
|
||||
|
|
|
@ -34,5 +34,6 @@ TEST(strsignal, test) {
|
|||
|
||||
TEST(strsignal, realtime) {
|
||||
if (!SIGRTMIN) return;
|
||||
EXPECT_STREQ("SIGTHR", strsignal(SIGTHR));
|
||||
ASSERT_STREQ("SIGRTMIN+1", strsignal(SIGRTMIN + 1));
|
||||
}
|
||||
|
|
|
@ -171,16 +171,16 @@ BENCH(malloc, torture) {
|
|||
printf("\nmalloc torture test w/ %d threads and %d iterations\n", n,
|
||||
ITERATIONS);
|
||||
SPAWN(fork);
|
||||
struct timespec t1 = _timespec_real();
|
||||
struct timespec t1 = timespec_real();
|
||||
for (i = 0; i < n; ++i) {
|
||||
ASSERT_EQ(0, pthread_create(t + i, 0, Worker, 0));
|
||||
}
|
||||
for (i = 0; i < n; ++i) {
|
||||
ASSERT_EQ(0, pthread_join(t[i], 0));
|
||||
}
|
||||
struct timespec t2 = _timespec_real();
|
||||
struct timespec t2 = timespec_real();
|
||||
printf("consumed %g wall and %g cpu seconds\n",
|
||||
_timespec_tomicros(_timespec_sub(t2, t1)) * 1e-6,
|
||||
timespec_tomicros(timespec_sub(t2, t1)) * 1e-6,
|
||||
(double)clock() / CLOCKS_PER_SEC);
|
||||
EXITS(0);
|
||||
}
|
||||
|
|
95
test/libc/stdio/getentropy_test.c
Normal file
95
test/libc/stdio/getentropy_test.c
Normal file
|
@ -0,0 +1,95 @@
|
|||
/*-*- 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/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/mem/gc.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/rand.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/tab.internal.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
// TODO(jart): Why can EINTR happen on Windows?
|
||||
|
||||
atomic_int done;
|
||||
atomic_int ready;
|
||||
pthread_t parent;
|
||||
atomic_int gotsome;
|
||||
|
||||
void OnSig(int sig) {
|
||||
++gotsome;
|
||||
}
|
||||
|
||||
void *TortureWorker(void *arg) {
|
||||
sigset_t ss;
|
||||
sigfillset(&ss);
|
||||
ASSERT_SYS(0, 0, sigprocmask(SIG_SETMASK, &ss, 0));
|
||||
ready = true;
|
||||
while (!done) {
|
||||
if (!IsWindows()) pthread_kill(parent, SIGUSR1);
|
||||
usleep(3);
|
||||
if (!IsWindows()) pthread_kill(parent, SIGUSR2);
|
||||
usleep(3);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(getentropy, test) {
|
||||
pthread_t child;
|
||||
double e, w = 7.7;
|
||||
struct sigaction sa;
|
||||
int i, j, k, n = 999;
|
||||
char *buf = _gc(calloc(1, n));
|
||||
sa.sa_flags = 0;
|
||||
sa.sa_handler = OnSig;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
ASSERT_SYS(0, 0, sigaction(SIGUSR1, &sa, 0));
|
||||
ASSERT_SYS(0, 0, sigaction(SIGUSR2, &sa, 0));
|
||||
parent = pthread_self();
|
||||
ASSERT_EQ(0, pthread_create(&child, 0, TortureWorker, 0));
|
||||
while (!ready) pthread_yield();
|
||||
for (k = 0; k < 200; ++k) {
|
||||
ASSERT_SYS(0, 0, getrandom(0, 0, 0));
|
||||
ASSERT_SYS(0, n, getrandom(buf, n, 0));
|
||||
ASSERT_SYS(EFAULT, -1, getrandom(0, n, 0));
|
||||
ASSERT_SYS(EINVAL, -1, getrandom(buf, n, -1));
|
||||
if ((e = MeasureEntropy(buf, n)) < w) {
|
||||
fprintf(stderr, "error: entropy suspect! got %g but want >=%g\n", e, w);
|
||||
for (i = 0; i < n;) {
|
||||
if (!(i % 16)) fprintf(stderr, "%6x ", i);
|
||||
fprintf(stderr, "%lc", kCp437[buf[i] & 255]);
|
||||
if (!(++i % 16)) fprintf(stderr, "\n");
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
done = true;
|
||||
pthread_join(child, 0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
done = true;
|
||||
ASSERT_EQ(0, pthread_join(child, 0));
|
||||
if (!IsWindows()) ASSERT_GT(gotsome, 0);
|
||||
}
|
|
@ -17,19 +17,49 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "ape/sections.internal.h"
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/math.h"
|
||||
#include "libc/mem/gc.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nexgen32e/x86feature.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/lcg.internal.h"
|
||||
#include "libc/stdio/rand.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/str/tab.internal.h"
|
||||
#include "libc/sysv/consts/grnd.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/testlib/ezbench.h"
|
||||
#include "libc/testlib/hyperion.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
TEST(getrandom, test) {
|
||||
double e, w = 7.7;
|
||||
int i, j, n = 999;
|
||||
char *buf = _gc(calloc(1, n));
|
||||
ASSERT_SYS(0, 0, getrandom(0, 0, 0));
|
||||
ASSERT_SYS(0, n, getrandom(buf, n, 0));
|
||||
ASSERT_SYS(EFAULT, -1, getrandom(0, n, 0));
|
||||
ASSERT_SYS(EINVAL, -1, getrandom(buf, n, -1));
|
||||
if ((e = MeasureEntropy(buf, n)) < w) {
|
||||
fprintf(stderr, "error: entropy is suspect! got %g but want >=%g\n", e, w);
|
||||
for (i = 0; i < n;) {
|
||||
if (!(i % 16)) fprintf(stderr, "%6x ", i);
|
||||
fprintf(stderr, "%lc", kCp437[buf[i] & 255]);
|
||||
if (!(++i % 16)) fprintf(stderr, "\n");
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* JustReturnZero */
|
||||
/* entropy: 0 */
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
#include "libc/calls/calls.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/mem/gc.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nexgen32e/nexgen32e.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
@ -62,27 +65,6 @@ TEST(pthread_cancel, self_deferred_waitsForCancellationPoint) {
|
|||
ASSERT_SYS(0, 0, close(pfds[0]));
|
||||
}
|
||||
|
||||
void *CancelSelfWorkerAsync(void *arg) {
|
||||
char buf[8];
|
||||
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0);
|
||||
pthread_cleanup_push(OnCleanup, 0);
|
||||
pthread_cancel(pthread_self());
|
||||
pthread_cleanup_pop(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(pthread_cancel, self_asynchronous_takesImmediateEffect) {
|
||||
void *rc;
|
||||
pthread_t th;
|
||||
ASSERT_SYS(0, 0, pipe(pfds));
|
||||
ASSERT_EQ(0, pthread_create(&th, 0, CancelSelfWorkerAsync, 0));
|
||||
ASSERT_EQ(0, pthread_join(th, &rc));
|
||||
ASSERT_EQ(PTHREAD_CANCELED, rc);
|
||||
ASSERT_TRUE(gotcleanup);
|
||||
ASSERT_SYS(0, 0, close(pfds[1]));
|
||||
ASSERT_SYS(0, 0, close(pfds[0]));
|
||||
}
|
||||
|
||||
void *Worker(void *arg) {
|
||||
int n;
|
||||
char buf[8];
|
||||
|
@ -212,3 +194,84 @@ TEST(pthread_cancel, condDeferredWaitDelayed) {
|
|||
ASSERT_EQ(0, pthread_mutex_trylock(&mu));
|
||||
ASSERT_EQ(0, pthread_mutex_unlock(&mu));
|
||||
}
|
||||
|
||||
char *wouldleak;
|
||||
pthread_key_t key;
|
||||
bool key_destructor_was_run;
|
||||
atomic_bool is_in_infinite_loop;
|
||||
|
||||
void KeyDestructor(void *arg) {
|
||||
CheckStackIsAligned();
|
||||
ASSERT_EQ(31337, (intptr_t)arg);
|
||||
key_destructor_was_run = true;
|
||||
}
|
||||
|
||||
void TortureStack(void) {
|
||||
asm("sub\t$4,%rsp\n\t"
|
||||
"pause\n\t"
|
||||
"sub\t$2,%rsp\n\t"
|
||||
"pause\n\t"
|
||||
"add\t$6,%rsp");
|
||||
}
|
||||
|
||||
void *CpuBoundWorker(void *arg) {
|
||||
char *volatile wontleak1;
|
||||
char *volatile wontleak2;
|
||||
CheckStackIsAligned();
|
||||
wouldleak = malloc(123);
|
||||
wontleak1 = malloc(123);
|
||||
pthread_cleanup_push(free, wontleak1);
|
||||
wontleak2 = _gc(malloc(123));
|
||||
ASSERT_EQ(0, pthread_setspecific(key, (void *)31337));
|
||||
ASSERT_EQ(0, pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0));
|
||||
for (;;) {
|
||||
TortureStack();
|
||||
is_in_infinite_loop = true;
|
||||
}
|
||||
pthread_cleanup_pop(1);
|
||||
free(wouldleak);
|
||||
}
|
||||
|
||||
TEST(pthread_cancel, async) {
|
||||
void *rc;
|
||||
pthread_t th;
|
||||
ASSERT_EQ(0, pthread_key_create(&key, KeyDestructor));
|
||||
if (IsWindows()) {
|
||||
// asynchronous cancellations aren't possible on windows yet
|
||||
ASSERT_EQ(ENOTSUP, pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0));
|
||||
return;
|
||||
}
|
||||
wouldleak = NULL;
|
||||
is_in_infinite_loop = false;
|
||||
key_destructor_was_run = false;
|
||||
ASSERT_EQ(0, pthread_create(&th, 0, CpuBoundWorker, 0));
|
||||
while (!is_in_infinite_loop) pthread_yield();
|
||||
ASSERT_EQ(0, pthread_cancel(th));
|
||||
ASSERT_EQ(0, pthread_join(th, &rc));
|
||||
ASSERT_EQ(PTHREAD_CANCELED, rc);
|
||||
ASSERT_TRUE(key_destructor_was_run);
|
||||
ASSERT_EQ(0, pthread_key_delete(key));
|
||||
free(wouldleak);
|
||||
}
|
||||
|
||||
void *CancelSelfWorkerAsync(void *arg) {
|
||||
char buf[8];
|
||||
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0);
|
||||
pthread_cleanup_push(OnCleanup, 0);
|
||||
pthread_cancel(pthread_self());
|
||||
pthread_cleanup_pop(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(pthread_cancel, self_asynchronous_takesImmediateEffect) {
|
||||
void *rc;
|
||||
pthread_t th;
|
||||
if (IsWindows()) return;
|
||||
ASSERT_SYS(0, 0, pipe(pfds));
|
||||
ASSERT_EQ(0, pthread_create(&th, 0, CancelSelfWorkerAsync, 0));
|
||||
ASSERT_EQ(0, pthread_join(th, &rc));
|
||||
ASSERT_EQ(PTHREAD_CANCELED, rc);
|
||||
ASSERT_TRUE(gotcleanup);
|
||||
ASSERT_SYS(0, 0, close(pfds[1]));
|
||||
ASSERT_SYS(0, 0, close(pfds[0]));
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ TEST(sem_open, test) {
|
|||
TEST(sem_close, withUnnamedSemaphore_isUndefinedBehavior) {
|
||||
if (!IsModeDbg()) return;
|
||||
sem_t sem;
|
||||
ASSERT_SYS(0, 0, sem_init(&sem, 1, 0));
|
||||
ASSERT_SYS(0, 0, sem_init(&sem, 0, 0));
|
||||
SPAWN(fork);
|
||||
IgnoreStderr();
|
||||
sem_close(&sem);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue