mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
fa20edc44d
- Remove most __ASSEMBLER__ __LINKER__ ifdefs - Rename libc/intrin/bits.h to libc/serialize.h - Block pthread cancelation in fchmodat() polyfill - Remove `clang-format off` statements in third_party
136 lines
5.2 KiB
C
136 lines
5.2 KiB
C
#ifndef NSYNC_WAITER_H_
|
|
#define NSYNC_WAITER_H_
|
|
#include "third_party/nsync/time.h"
|
|
COSMOPOLITAN_C_START_
|
|
|
|
/* nsync_wait_n() allows the client to wait on multiple objects
|
|
(condition variables, nsync_notes, nsync_counters, etc.) until at
|
|
least one of them becomes ready, or a deadline expires.
|
|
|
|
It can be thought of as rather like Unix's select() or poll(), except
|
|
the the objects being waited for are synchronization data structures,
|
|
rather than file descriptors.
|
|
|
|
The client can construct new objects that can be waited for by
|
|
implementing three routines.
|
|
|
|
Examples:
|
|
|
|
To wait on two nsync_notes n0, n1, and a nsync_counter c0, with a
|
|
deadline of abs_deadline:
|
|
|
|
// Form an array of struct nsync_waitable_s, identifying the
|
|
// objects and the corresponding descriptors. (Static
|
|
// initialization syntax is used for brevity.)
|
|
static struct nsync_waitable_s w[] = {
|
|
{ &n0, &nsync_note_waitable_funcs },
|
|
{ &n1, &nsync_note_waitable_funcs },
|
|
{ &c0, &nsync_counter_waitable_funcs }
|
|
};
|
|
static struct nsync_waitable_s *pw[] = { &w[0], &w[1], &w[2] };
|
|
int n = sizeof (w) / sizeof (w[0]);
|
|
|
|
// Wait. The mu, lock, and unlock arguments are NULL because no
|
|
// condition variables are invovled.
|
|
int i = nsync_wait_n (NULL, NULL, NULL, abs_deadline, n, pw);
|
|
if (i == n) {
|
|
// timeout
|
|
} else {
|
|
// w[i].v became ready.
|
|
}
|
|
|
|
To wait on multiple condition variables, the mu/lock/unlock
|
|
parameters are used. Imagine cv0 and cv1 are signalled when
|
|
predicates pred0() (under lock mu0) and pred1() (under lock mu1)
|
|
become true respectively. Assume that mu0 is acquired before mu1.
|
|
|
|
static void lock2 (void *v) { // lock two mutexes in order
|
|
nsync_mu **mu = (nsync_mu **) v;
|
|
nsync_mu_lock (mu[0]);
|
|
nsync_mu_lock (mu[1]);
|
|
}
|
|
static void unlock2 (void *v) { // unlock two mutexes.
|
|
nsync_mu **mu = (nsync_mu **) v;
|
|
nsync_mu_unlock (mu[1]);
|
|
nsync_mu_unlock (mu[0]);
|
|
}
|
|
|
|
// Describe the condition variables and the locks.
|
|
static struct nsync_waitable_s w[] = {
|
|
{ &cv0, &nsync_cv_waitable_funcs },
|
|
{ &cv1, &nsync_cv_waitable_funcs }
|
|
};
|
|
static struct nsync_waitable_s *pw[] = { &w[0], &w[1] };
|
|
nsync_mu *lock_list[] = { &mu0, &mu1 };
|
|
int n = sizeof (w) / sizeof (w[0]);
|
|
|
|
lock2 (list_list);
|
|
while (!pred0 () && !pred1 ()) {
|
|
// Wait for one of the condition variables to be signalled,
|
|
// with no timeout.
|
|
nsync_wait_n (lock_list, &lock2, &unlock2,
|
|
nsync_time_no_deadline, n, pw);
|
|
}
|
|
if (pred0 ()) { ... }
|
|
if (pred1 ()) { ... }
|
|
unlock2 (list_list);
|
|
|
|
*/
|
|
|
|
/* forward declaration of struct that contains type dependent wait
|
|
operations */
|
|
struct nsync_waitable_funcs_s;
|
|
|
|
/* Clients wait on objects by forming an array of struct
|
|
nsync_waitable_s. Each each element points to one object and its
|
|
type-dependent functions. */
|
|
struct nsync_waitable_s {
|
|
/* pointer to object */
|
|
void *v;
|
|
/* pointer to type-dependent functions. Use
|
|
&nsync_note_waitable_funcs for an nsync_note,
|
|
&nsync_counternote_waitable_funcs for an nsync_counter,
|
|
&nsync_cv_waitable_funcs for an nsync_cv. */
|
|
const struct nsync_waitable_funcs_s *funcs;
|
|
};
|
|
|
|
/* Wait until at least one of *waitable[0,..,count-1] is has been
|
|
notified, or abs_deadline is reached. Return the index of the
|
|
notified element of waitable[], or count if no such element exists.
|
|
If mu!=NULL, (*unlock)(mu) is called after the thread is queued on
|
|
the various waiters, and (*lock)(mu) is called before return;
|
|
mu/lock/unlock are used to acquire and release the relevant locks
|
|
whan waiting on condition variables. */
|
|
int nsync_wait_n(void *mu, void (*lock)(void *), void (*unlock)(void *),
|
|
nsync_time abs_deadline, int count,
|
|
struct nsync_waitable_s *waitable[]);
|
|
|
|
/* A "struct nsync_waitable_s" implementation must implement these
|
|
functions. Clients should ignore the internals. */
|
|
struct nsync_waiter_s;
|
|
struct nsync_waitable_funcs_s {
|
|
|
|
/* Return the time when *v will be ready (max time if unknown), or 0
|
|
if it is already ready. The parameter nw may be passed as NULL, in
|
|
which case the result should indicate whether the thread would
|
|
block if it were to wait on *v. All calls with the same *v must
|
|
report the same result until the object becomes ready, from which
|
|
point calls must report 0. */
|
|
nsync_time (*ready_time)(void *v, struct nsync_waiter_s *nw);
|
|
|
|
/* If *v is ready, return zero; otherwise enqueue *nw on *v and return
|
|
non-zero. */
|
|
int (*enqueue)(void *v, struct nsync_waiter_s *nw);
|
|
|
|
/* If nw has been previously dequeued, return zero; otherwise dequeue
|
|
*nw from *v and return non-zero. */
|
|
int (*dequeue)(void *v, struct nsync_waiter_s *nw);
|
|
};
|
|
|
|
/* The "struct nsync_waitable_s" for nsync_note, nsync_counter, and nsync_cv. */
|
|
extern const struct nsync_waitable_funcs_s nsync_note_waitable_funcs;
|
|
extern const struct nsync_waitable_funcs_s nsync_counter_waitable_funcs;
|
|
extern const struct nsync_waitable_funcs_s nsync_cv_waitable_funcs;
|
|
|
|
COSMOPOLITAN_C_END_
|
|
#endif /* NSYNC_WAITER_H_ */
|