cosmopolitan/third_party/nsync/mu.h
Justine Tunney f24c854b28
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.
2025-01-01 22:25:22 -08:00

100 lines
3.7 KiB
C

#ifndef NSYNC_MU_H_
#define NSYNC_MU_H_
#include "libc/intrin/dll.h"
#include "third_party/nsync/atomic.h"
COSMOPOLITAN_C_START_
/* An nsync_mu is a lock. If initialized to zero, it's valid and unlocked.
An nsync_mu can be "free", held by a single thread (aka fiber,
goroutine) in "write" (exclusive) mode, or by many threads in "read"
(shared) mode. A thread that acquires it should eventually release
it. It is illegal to acquire an nsync_mu in one thread and release it
in another. It is illegal for a thread to reacquire an nsync_mu while
holding it (even a second share of a "read" lock).
Example usage:
static struct foo {
nsync_mu mu; // protects invariant a+b==0 on fields below.
int a;
int b;
} p = { NSYNC_MU_INIT, 0, 0 };
// ....
nsync_mu_lock (&p.mu);
// The current thread now has exclusive access to p.a and p.b;
// invariant assumed true.
p.a++;
p.b--; // restore invariant p.a+p.b==0 before releasing p.mu
nsync_mu_unlock (&p.mu)
Mutexes can be used with condition variables; see nsync_cv.h.
nsync_mu_wait() and nsync_mu_wait_with_deadline() can be used instead
of condition variables. See nsync_mu_wait.h for more details. Example
use of nsync_mu_wait() to wait for p.a==0, using definition above:
int a_is_zero (const void *condition_arg) {
return (((const struct foo *)condition_arg)->a == 0);
}
...
nsync_mu_lock (&p.mu);
nsync_mu_wait (&p.mu, &a_is_zero, &p, NULL);
// The current thread now has exclusive access to
// p.a and p.b, and p.a==0.
...
nsync_mu_unlock (&p.mu);
*/
typedef struct nsync_mu_s_ {
nsync_atomic_uint32_ word; /* internal use only */
struct Dll *waiters; /* internal use only */
} nsync_mu;
/* An nsync_mu should be zeroed to initialize, which can be accomplished
by initializing with static initializer NSYNC_MU_INIT, or by setting
the entire structure to all zeroes, or using nsync_mu_init(). */
#define NSYNC_MU_INIT \
{ NSYNC_ATOMIC_UINT32_INIT_, 0 }
void nsync_mu_init(nsync_mu *mu);
/* Block until *mu is free and then acquire it in writer mode. Requires
that the calling thread not already hold *mu in any mode. */
void nsync_mu_lock(nsync_mu *mu);
/* Unlock *mu, which must have been acquired in write mode by the
calling thread, and wake waiters, if appropriate. */
void nsync_mu_unlock(nsync_mu *mu);
/* Attempt to acquire *mu in writer mode without blocking, and return
non-zero iff successful. Return non-zero with high probability if *mu
was free on entry. */
int nsync_mu_trylock(nsync_mu *mu);
/* Block until *mu can be acquired in reader mode and then acquire it.
Requires that the calling thread not already hold *mu in any mode. */
void nsync_mu_rlock(nsync_mu *mu);
/* Unlock *mu, which must have been acquired in read mode by the calling
thread, and wake waiters, if appropriate. */
void nsync_mu_runlock(nsync_mu *mu);
/* Attempt to acquire *mu in reader mode without blocking, and return
non-zero iff successful. Return non-zero with high probability if *mu
was free on entry. Perhaps fail to acquire if a writer is waiting, to
avoid starvation. */
int nsync_mu_rtrylock(nsync_mu *mu);
/* May abort if *mu is not held in write mode by the calling thread. */
void nsync_mu_assert_held(const nsync_mu *mu);
/* May abort if *mu is not held in read or write mode
by the calling thread. */
void nsync_mu_rassert_held(const nsync_mu *mu);
/* Return whether *mu is held in read mode.
Requires that the calling thread holds *mu in some mode. */
int nsync_mu_is_reader(const nsync_mu *mu);
COSMOPOLITAN_C_END_
#endif /* NSYNC_MU_H_ */