1/* Copyright 2016 Google Inc.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License. */
14
15#ifndef NSYNC_PUBLIC_NSYNC_WAITER_H_
16#define NSYNC_PUBLIC_NSYNC_WAITER_H_
17
18/* nsync_wait_n() allows the client to wait on multiple objects (condition
19 variables, nsync_notes, nsync_counters, etc.) until at least one of them
20 becomes ready, or a deadline expires.
21
22 It can be thought of as rather like Unix's select() or poll(),
23 except the the objects being waited for are synchronization
24 data structures, rather than file descriptors.
25
26 The client can construct new objects that can be waited for by implementing
27 three routines.
28
29 Examples:
30
31 To wait on two nsync_notes n0, n1, and a nsync_counter c0,
32 with a deadline of abs_deadline:
33
34 // Form an array of struct nsync_waitable_s, identifying the
35 // objects and the corresponding descriptors. (static initialization
36 // syntax is used for brevity)
37 static struct nsync_waitable_s w[] = {
38 { &n0, &nsync_note_waitable_funcs },
39 { &n1, &nsync_note_waitable_funcs },
40 { &c0, &nsync_counter_waitable_funcs }
41 };
42 static struct nsync_waitable_s *pw[] = { &w[0], &w[1], &w[2] };
43 int n = sizeof (w) / sizeof (w[0]);
44
45 // Wait. The mu, lock, and unlock arguments are NULL because
46 // no condition variables are invovled.
47 int i = nsync_wait_n (NULL, NULL, NULL, abs_deadline, n, pw);
48 if (i == n) {
49 // timeout
50 } else {
51 // w[i].v became ready.
52 }
53
54 To wait on multiple condition variables, the mu/lock/unlock parameters are
55 used. Imagine cv0 and cv1 are signalled when predicates pred0() (under
56 lock mu0) and pred1() (under lock mu1) become true respectively. Assume
57 that mu0 is acquired before mu1.
58 static void lock2 (void *v) { // lock two mutexes in order
59 nsync_mu **mu = (nsync_mu **) v;
60 nsync_mu_lock (mu[0]);
61 nsync_mu_lock (mu[1]);
62 }
63 static void unlock2 (void *v) { // unlock two mutexes.
64 nsync_mu **mu = (nsync_mu **) v;
65 nsync_mu_unlock (mu[1]);
66 nsync_mu_unlock (mu[0]);
67 }
68
69 // Describe the condition variables and the locks.
70 static struct nsync_waitable_s w[] = {
71 { &cv0, &nsync_cv_waitable_funcs },
72 { &cv1, &nsync_cv_waitable_funcs }
73 };
74 static struct nsync_waitable_s *pw[] = { &w[0], &w[1] };
75 nsync_mu *lock_list[] = { &mu0, &mu1 };
76 int n = sizeof (w) / sizeof (w[0]);
77
78 lock2 (list_list);
79 while (!pred0 () && !pred1 ()) {
80 // Wait for one of the condition variables to be signalled,
81 // with no timeout.
82 nsync_wait_n (lock_list, &lock2, &unlock2,
83 nsync_time_no_deadline, n, pw);
84 }
85 if (pred0 ()) { ... }
86 if (pred1 ()) { ... }
87 unlock2 (list_list);
88
89 */
90
91#include <inttypes.h>
92#include <time.h>
93#include "nsync_cpp.h"
94#include "nsync_atomic.h"
95#include "nsync_time.h"
96
97NSYNC_CPP_START_
98
99struct nsync_waitable_funcs_s; /* forward declaration of struct that contains
100 type dependent wait operations */
101
102/* Clients wait on objects by forming an array of struct nsync_waitable_s.
103 Each each element points to one object and its type-dependent functions. */
104struct nsync_waitable_s {
105 void *v; /* pointer to object */
106 /* pointer to type-dependent functions. Use
107 &nsync_note_waitable_funcs for an nsync_note,
108 &nsync_counternote_waitable_funcs for an nsync_counter,
109 &nsync_cv_waitable_funcs for an nsync_cv. */
110 const struct nsync_waitable_funcs_s *funcs;
111};
112
113/* Wait until at least one of *waitable[0,..,count-1] is has been notified, or
114 abs_deadline is reached. Return the index of the notified element of
115 waitable[], or count if no such element exists.
116 If mu!=NULL, (*unlock)(mu) is called after the thread is queued on the
117 various waiters, and (*lock)(mu) is called before return; mu/lock/unlock are
118 used to acquire and release the relevant locks whan waiting on condition
119 variables. */
120int nsync_wait_n (void *mu, void (*lock) (void *), void (*unlock) (void *),
121 nsync_time abs_deadline, int count,
122 struct nsync_waitable_s *waitable[]);
123
124/* --------------------------------------------------- */
125
126/* A "struct nsync_waitable_s" implementation must implement these functions.
127 Clients should ignore the internals. */
128struct nsync_waiter_s;
129struct nsync_waitable_funcs_s {
130 /* Return the time when *v will be ready (max time if
131 unknown), or 0 if it is already ready. The parameter nw may be
132 passed as NULL, in which case the result should indicate whether the
133 thread would block if it were to wait on *v.
134 All calls with the same *v must report the same result until the
135 object becomes ready, from which point calls must report 0. */
136 nsync_time (*ready_time) (void *v, struct nsync_waiter_s *nw);
137
138 /* If *v is ready, return zero; otherwise enqueue *nw on *v and return
139 non-zero. */
140 int (*enqueue) (void *v, struct nsync_waiter_s *nw);
141
142 /* If nw has been previously dequeued, return zero; otherwise dequeue
143 *nw from *v and return non-zero. */
144 int (*dequeue) (void *v, struct nsync_waiter_s *nw);
145};
146
147/* The "struct nsync_waitable_s" for nsync_note, nsync_counter, and nsync_cv. */
148extern const struct nsync_waitable_funcs_s nsync_note_waitable_funcs;
149extern const struct nsync_waitable_funcs_s nsync_counter_waitable_funcs;
150extern const struct nsync_waitable_funcs_s nsync_cv_waitable_funcs;
151
152NSYNC_WAITER_CPP_OVERLOAD_
153NSYNC_CPP_END_
154
155#endif /*NSYNC_PUBLIC_NSYNC_WAITER_H_*/
156