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 | |
97 | NSYNC_CPP_START_ |
98 | |
99 | struct 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. */ |
104 | struct 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. */ |
120 | int 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. */ |
128 | struct nsync_waiter_s; |
129 | struct 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. */ |
148 | extern const struct nsync_waitable_funcs_s nsync_note_waitable_funcs; |
149 | extern const struct nsync_waitable_funcs_s nsync_counter_waitable_funcs; |
150 | extern const struct nsync_waitable_funcs_s nsync_cv_waitable_funcs; |
151 | |
152 | NSYNC_WAITER_CPP_OVERLOAD_ |
153 | NSYNC_CPP_END_ |
154 | |
155 | #endif /*NSYNC_PUBLIC_NSYNC_WAITER_H_*/ |
156 | |