Write more runtime tests and fix bugs

This change adds tests for the new memory manager code particularly with
its windows support. Function call tracing now works reliably on Silicon
since our function hooker was missing new Apple self-modifying code APIs

Many tests that were disabled a long time ago on aarch64 are reactivated
by this change, now that arm support is on equal terms with x86. There's
been a lot of places where ftrace could cause deadlocks, which have been
hunted down across all platforms thanks to new tests. A bug in Windows's
kill() function has been identified.
This commit is contained in:
Justine Tunney 2025-01-01 22:25:22 -08:00
parent 0b3c81dd4e
commit f24c854b28
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
45 changed files with 550 additions and 872 deletions

View file

@ -110,6 +110,8 @@ uint32_t nsync_spin_test_and_set_ (nsync_atomic_uint32_ *w, uint32_t test,
/* ====================================================================================== */
#if NSYNC_DEBUG
struct nsync_waiter_s *nsync_dll_nsync_waiter_ (struct Dll *e) {
struct nsync_waiter_s *nw = DLL_CONTAINER(struct nsync_waiter_s, q, e);
ASSERT (nw->tag == NSYNC_WAITER_TAG);
@ -133,6 +135,8 @@ waiter *nsync_dll_waiter_samecond_ (struct Dll *e) {
return (w);
}
#endif /* NSYNC_DEBUG */
/* -------------------------------- */
// TODO(jart): enforce in dbg mode once off-by-one flake is fixed
@ -249,8 +253,10 @@ static bool free_waiters_populate (void) {
return (false);
for (size_t i = 0; i < n; ++i) {
waiter *w = &waiters[i];
#if NSYNC_DEBUG
w->tag = WAITER_TAG;
w->nw.tag = NSYNC_WAITER_TAG;
#endif
if (!nsync_mu_semaphore_init (&w->sem)) {
if (!i) {
// netbsd can run out of semaphores
@ -327,18 +333,26 @@ void nsync_waiter_wipe_ (void) {
nsync_mu_semaphore_destroy (&w->sem);
for (w = wall; w; w = next) {
next = w->next_all;
w->tag = 0;
w->flags = 0;
w->nw.tag = 0;
#if NSYNC_DEBUG
w->tag = WAITER_TAG;
w->nw.tag = NSYNC_WAITER_TAG;
#endif
w->nw.flags = NSYNC_WAITER_FLAG_MUCV;
atomic_init(&w->nw.waiting, 0);
w->l_type = 0;
bzero (&w->cond, sizeof (w->cond));
w->cond.f = 0;
w->cond.v = 0;
w->cond.eq = 0;
dll_init (&w->same_condition);
if (w->wipe_mu)
bzero (w->wipe_mu, sizeof (*w->wipe_mu));
if (w->wipe_cv)
bzero (w->wipe_cv, sizeof (*w->wipe_cv));
if (w->wipe_mu) {
atomic_init(&w->wipe_mu->word, 0);
w->wipe_mu->waiters = 0;
}
if (w->wipe_cv) {
atomic_init(&w->wipe_cv->word, 0);
w->wipe_cv->waiters = 0;
}
if (!nsync_mu_semaphore_init (&w->sem))
continue; /* leak it */
w->next_free = prev;

View file

@ -9,15 +9,10 @@
#include "third_party/nsync/mu_semaphore.h"
#include "third_party/nsync/note.h"
#include "third_party/nsync/time.h"
#include "third_party/nsync/defs.h"
#include "third_party/nsync/wait_s.internal.h"
COSMOPOLITAN_C_START_
#ifdef MODE_DBG
#define NSYNC_DEBUG 1
#else
#define NSYNC_DEBUG 0
#endif
/* Yield the CPU. Platform specific. */
void nsync_yield_(void);
@ -191,13 +186,15 @@ struct wait_condition_s {
ATM_STORE_REL (&w.waiting, 0);
nsync_mu_semaphore_v (&w.sem); */
typedef struct waiter_s {
#if NSYNC_DEBUG
uint32_t tag; /* Debug DLL_NSYNC_WAITER, DLL_WAITER, DLL_WAITER_SAMECOND. */
#endif
int flags; /* See WAITER_* bits below. */
nsync_atomic_uint32_ remove_count; /* Monotonic count of removals from queue. */
nsync_semaphore sem; /* Thread waits on this semaphore. */
struct nsync_waiter_s nw; /* An embedded nsync_waiter_s. */
struct nsync_mu_s_ *cv_mu; /* Pointer to nsync_mu associated with a cv wait. */
lock_type *l_type; /* Lock type of the mu, or nil if not associated with a mu. */
nsync_atomic_uint32_ remove_count; /* Monotonic count of removals from queue. */
struct wait_condition_s cond; /* A condition on which to acquire a mu. */
struct Dll same_condition; /* Links neighbours in nw.q with same non-nil condition. */
struct waiter_s * next_all;

12
third_party/nsync/defs.h vendored Normal file
View file

@ -0,0 +1,12 @@
#ifndef COSMOPOLITAN_THIRD_PARTY_NSYNC_DEFS_H_
#define COSMOPOLITAN_THIRD_PARTY_NSYNC_DEFS_H_
COSMOPOLITAN_C_START_
#ifdef MODE_DBG
#define NSYNC_DEBUG 1
#else
#define NSYNC_DEBUG 0
#endif
COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_THIRD_PARTY_NSYNC_DEFS_H_ */

View file

@ -20,6 +20,7 @@
#include "third_party/nsync/common.internal.h"
#include "third_party/nsync/mu_semaphore.h"
#include "third_party/nsync/races.internal.h"
#include "third_party/nsync/defs.h"
#include "third_party/nsync/wait_s.internal.h"
__static_yoink("nsync_notice");
@ -148,15 +149,23 @@ static void emit_waiters (struct emit_buf *b, struct Dll *list) {
waiter *w = DLL_WAITER (p);
next = NULL;
emit_print (b, " %i", (uintptr_t) w);
#if NSYNC_DEBUG
if (w->tag != WAITER_TAG) {
emit_print (b, "bad WAITER_TAG %i",
(uintptr_t) w->tag);
} else {
#else
{
#endif
next = dll_next (list, p);
#if NSYNC_DEBUG
if (nw->tag != NSYNC_WAITER_TAG) {
emit_print (b, " bad WAITER_TAG %i",
(uintptr_t) nw->tag);
} else {
#else
{
#endif
emit_print (b, " embedded=%i waiting=%i",
(uintptr_t) (w->flags & NSYNC_WAITER_FLAG_MUCV),
(uintptr_t) ATM_LOAD (&nw->waiting));

View file

@ -40,7 +40,9 @@ int nsync_sem_wait_with_cancel_ (waiter *w, int clock, nsync_time abs_deadline,
sem_outcome = ECANCELED;
if (nsync_time_cmp (cancel_time, nsync_time_zero) > 0) {
struct nsync_waiter_s nw;
#if NSYNC_DEBUG
nw.tag = NSYNC_WAITER_TAG;
#endif
nw.sem = &w->sem;
dll_init (&nw.q);
ATM_STORE (&nw.waiting, 1);

View file

@ -51,7 +51,9 @@ int nsync_wait_n (void *mu, void (*lock) (void *), void (*unlock) (void *),
nw = (struct nsync_waiter_s *) malloc (count * sizeof (nw[0]));
}
for (i = 0; i != count && enqueued; i++) {
#if NSYNC_DEBUG
nw[i].tag = NSYNC_WAITER_TAG;
#endif
nw[i].sem = &w->sem;
dll_init (&nw[i].q);
ATM_STORE (&nw[i].waiting, 0);

View file

@ -48,7 +48,6 @@ COSMOPOLITAN_C_START_
*/
typedef struct nsync_mu_s_ {
nsync_atomic_uint32_ word; /* internal use only */
int _zero; /* c pthread_mutex_t */
struct Dll *waiters; /* internal use only */
} nsync_mu;

View file

@ -60,7 +60,7 @@ typedef struct cv_stress_data_s {
/* The delays in cv_stress_inc_loop(), cv_stress_reader_loop(), mu_stress_inc_loop(),
and mu_stress_reader_loop() are uniformly distributed from 0 to
STRESS_MAX_DELAY_MICROS-1 microseconds. */
#define STRESS_MAX_DELAY_MICROS (IsNetbsd() || IsOpenbsd() ? 20000 : 4000) /* maximum delay */
#define STRESS_MAX_DELAY_MICROS (IsNetbsd() || IsOpenbsd() ? 30000 : 4000) /* maximum delay */
#define STRESS_MEAN_DELAY_MICROS (STRESS_MAX_DELAY_MICROS / 2) /* mean delay */
#define STRESS_EXPECT_TIMEOUTS_PER_SEC (1000000 / STRESS_MEAN_DELAY_MICROS) /* expect timeouts/s*/

View file

@ -20,6 +20,7 @@
#include "third_party/nsync/testing/smprintf.h"
#include "third_party/nsync/testing/testing.h"
#include "third_party/nsync/testing/time_extra.h"
#include "libc/dce.h"
#include "third_party/nsync/time.h"
/* Verify the properties of a prenotified note. */
@ -78,7 +79,7 @@ static void test_note_unnotified (testing t) {
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) {
if (nsync_time_cmp (waited, nsync_time_ms (IsNetbsd() || IsOpenbsd() || IsFreebsd() ? 4000 : 2000)) > 0) {
TEST_ERROR (t, ("timed wait on unnotified note returned too slowly (1s wait took %s)",
nsync_time_str (waited, 2)));
}

View file

@ -1,6 +1,7 @@
#ifndef COSMOPOLITAN_LIBC_THREAD_WAIT_INTERNAL_H_
#define COSMOPOLITAN_LIBC_THREAD_WAIT_INTERNAL_H_
#include "libc/intrin/dll.h"
#include "third_party/nsync/defs.h"
#include "third_party/nsync/atomic.h"
COSMOPOLITAN_C_START_
@ -10,10 +11,12 @@ COSMOPOLITAN_C_START_
with v pointing to the client's object and nw pointing to a struct
nsync_waiter_s. */
struct nsync_waiter_s {
#if NSYNC_DEBUG
uint32_t tag; /* used for debugging */
#endif
uint32_t flags; /* see below */
struct Dll q; /* used to link children of parent */
nsync_atomic_uint32_ waiting; /* non-zero <=> the waiter is waiting */
struct Dll q; /* used to link children of parent */
struct nsync_semaphore_s_ *sem; /* *sem will be Ved when waiter is woken */
};