/proc/self/cwd/external/nsync/internal/note.c
Line | Count | Source (jump to first uncovered line) |
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 | | #include "nsync_cpp.h" |
16 | | #include "platform.h" |
17 | | #include "compiler.h" |
18 | | #include "cputype.h" |
19 | | #include "nsync.h" |
20 | | #include "dll.h" |
21 | | #include "sem.h" |
22 | | #include "wait_internal.h" |
23 | | #include "common.h" |
24 | | #include "atomic.h" |
25 | | |
26 | | NSYNC_CPP_START_ |
27 | | |
28 | | /* Locking discipline for the nsync_note implementation: |
29 | | |
30 | | Each nsync_note has a lock "note_mu" which protects the "parent" pointer, |
31 | | "waiters" list, and "disconnecting" count. It also protects the "children" |
32 | | list; thus each node's "parent_child_link", which links together the |
33 | | children of a single parent, is protected by the parent's "note_mu". |
34 | | |
35 | | To connect a parent to a child, or to disconnect one, the parent's lock must |
36 | | be held to manipulate its child list, and the child's lock must be held to |
37 | | change the parent pointer, so both must be held simultaneously. |
38 | | The locking order is "parent before child". |
39 | | |
40 | | Operations like notify and free are given a node pointer n and must |
41 | | disconnect *n from its parent n->parent. The call must hold n->note_mu to |
42 | | read n->parent, but need to release n->note_mu to acquire |
43 | | n->parent->note_mu. The parent could be disconnected and freed while |
44 | | n->note_mu is not held. The n->disconnecting count handles this; the |
45 | | operation acquires n->note_mu, increments n->disconnecting, and can then |
46 | | release n->note_mu, and acquire n->parent->note_mu and n->note_mu is the |
47 | | correct order. n->disconnecting!=0 indicates that a thread is already in |
48 | | the processes of disconnecting n from n->parent. A thread freeing or |
49 | | notifying the parent should not perform the disconnection of that child, but |
50 | | should instead wait for the "children" list to become empty via |
51 | | WAIT_FOR_NO_CHILDREN(). WAKEUP_NO_CHILDREN() should be used whenever this |
52 | | condition could become true. */ |
53 | | |
54 | | /* Set the expiry time in *n to t */ |
55 | 0 | static void set_expiry_time (nsync_note n, nsync_time t) { |
56 | 0 | n->expiry_time = t; |
57 | 0 | n->expiry_time_valid = 1; |
58 | 0 | } |
59 | | |
60 | | /* Return a pointer to the note containing nsync_dll_element_ *e. */ |
61 | 0 | #define DLL_NOTE(e) ((nsync_note)((e)->container)) |
62 | | |
63 | | /* Return whether n->children is empty. Assumes n->note_mu held. */ |
64 | 0 | static int no_children (const void *v) { |
65 | 0 | return (nsync_dll_is_empty_ (((nsync_note)v)->children)); |
66 | 0 | } |
67 | | |
68 | 0 | #define WAIT_FOR_NO_CHILDREN(pred_, n_) nsync_mu_wait (&(n_)->note_mu, &pred_, (n_), NULL) |
69 | 0 | #define WAKEUP_NO_CHILDREN(n_) do { } while (0) |
70 | | |
71 | | /* |
72 | | // These lines can be used in place of those above if conditional critical |
73 | | // sections have been removed from the source. |
74 | | #define WAIT_FOR_NO_CHILDREN(pred_, n_) do { \ |
75 | | while (!pred_ (n_)) { nsync_cv_wait (&(n_)->no_children_cv, &(n_)->note_mu); } \ |
76 | | } while (0) |
77 | | #define WAKEUP_NO_CHILDREN(n_) nsync_cv_broadcast (&(n_)->no_children_cv) |
78 | | */ |
79 | | |
80 | | /* Notify *n and all its descendants that are not already disconnnecting. |
81 | | n->note_mu is held. May release and reacquire n->note_mu. |
82 | | parent->note_mu is held if parent != NULL. */ |
83 | 0 | static void note_notify_child (nsync_note n, nsync_note parent) { |
84 | 0 | nsync_time t; |
85 | 0 | t = NOTIFIED_TIME (n); |
86 | 0 | if (nsync_time_cmp (t, nsync_time_zero) > 0) { |
87 | 0 | nsync_dll_element_ *p; |
88 | 0 | nsync_dll_element_ *next; |
89 | 0 | ATM_STORE_REL (&n->notified, 1); |
90 | 0 | while ((p = nsync_dll_first_ (n->waiters)) != NULL) { |
91 | 0 | struct nsync_waiter_s *nw = DLL_NSYNC_WAITER (p); |
92 | 0 | n->waiters = nsync_dll_remove_ (n->waiters, p); |
93 | 0 | ATM_STORE_REL (&nw->waiting, 0); |
94 | 0 | nsync_mu_semaphore_v (nw->sem); |
95 | 0 | } |
96 | 0 | for (p = nsync_dll_first_ (n->children); p != NULL; p = next) { |
97 | 0 | nsync_note child = DLL_NOTE (p); |
98 | 0 | next = nsync_dll_next_ (n->children, p); |
99 | 0 | nsync_mu_lock (&child->note_mu); |
100 | 0 | if (child->disconnecting == 0) { |
101 | 0 | note_notify_child (child, n); |
102 | 0 | } |
103 | 0 | nsync_mu_unlock (&child->note_mu); |
104 | 0 | } |
105 | 0 | WAIT_FOR_NO_CHILDREN (no_children, n); |
106 | 0 | if (parent != NULL) { |
107 | 0 | parent->children = nsync_dll_remove_ (parent->children, |
108 | 0 | &n->parent_child_link); |
109 | 0 | WAKEUP_NO_CHILDREN (parent); |
110 | 0 | n->parent = NULL; |
111 | 0 | } |
112 | 0 | } |
113 | 0 | } |
114 | | |
115 | | /* Notify *n and all its descendants that are not already disconnnecting. |
116 | | No locks are held. */ |
117 | 0 | static void notify (nsync_note n) { |
118 | 0 | nsync_time t; |
119 | 0 | nsync_mu_lock (&n->note_mu); |
120 | 0 | t = NOTIFIED_TIME (n); |
121 | 0 | if (nsync_time_cmp (t, nsync_time_zero) > 0) { |
122 | 0 | nsync_note parent; |
123 | 0 | n->disconnecting++; |
124 | 0 | parent = n->parent; |
125 | 0 | if (parent != NULL && !nsync_mu_trylock (&parent->note_mu)) { |
126 | 0 | nsync_mu_unlock (&n->note_mu); |
127 | 0 | nsync_mu_lock (&parent->note_mu); |
128 | 0 | nsync_mu_lock (&n->note_mu); |
129 | 0 | } |
130 | 0 | note_notify_child (n, parent); |
131 | 0 | if (parent != NULL) { |
132 | 0 | nsync_mu_unlock (&parent->note_mu); |
133 | 0 | } |
134 | 0 | n->disconnecting--; |
135 | 0 | } |
136 | 0 | nsync_mu_unlock (&n->note_mu); |
137 | 0 | } |
138 | | |
139 | | /* Return the deadline by which *n is certain to be notified, |
140 | | setting it to zero if it already has passed that time. |
141 | | Requires n->note_mu not held on entry. |
142 | | |
143 | | Not static; used in sem_wait.c */ |
144 | 0 | nsync_time nsync_note_notified_deadline_ (nsync_note n) { |
145 | 0 | nsync_time ntime; |
146 | 0 | if (ATM_LOAD_ACQ (&n->notified) != 0) { |
147 | 0 | ntime = nsync_time_zero; |
148 | 0 | } else { |
149 | 0 | nsync_mu_lock (&n->note_mu); |
150 | 0 | ntime = NOTIFIED_TIME (n); |
151 | 0 | nsync_mu_unlock (&n->note_mu); |
152 | 0 | if (nsync_time_cmp (ntime, nsync_time_zero) > 0) { |
153 | 0 | if (nsync_time_cmp (ntime, nsync_time_now ()) <= 0) { |
154 | 0 | notify (n); |
155 | 0 | ntime = nsync_time_zero; |
156 | 0 | } |
157 | 0 | } |
158 | 0 | } |
159 | 0 | return (ntime); |
160 | 0 | } |
161 | | |
162 | 0 | int nsync_note_is_notified (nsync_note n) { |
163 | 0 | int result; |
164 | 0 | IGNORE_RACES_START (); |
165 | 0 | result = (nsync_time_cmp (nsync_note_notified_deadline_ (n), nsync_time_zero) <= 0); |
166 | 0 | IGNORE_RACES_END (); |
167 | 0 | return (result); |
168 | 0 | } |
169 | | |
170 | | nsync_note nsync_note_new (nsync_note parent, |
171 | 0 | nsync_time abs_deadline) { |
172 | 0 | nsync_note n = (nsync_note) malloc (sizeof (*n)); |
173 | 0 | if (n != NULL) { |
174 | 0 | memset ((void *) n, 0, sizeof (*n)); |
175 | 0 | nsync_dll_init_ (&n->parent_child_link, n); |
176 | 0 | set_expiry_time (n, abs_deadline); |
177 | 0 | if (!nsync_note_is_notified (n) && parent != NULL) { |
178 | 0 | nsync_time parent_time; |
179 | 0 | nsync_mu_lock (&parent->note_mu); |
180 | 0 | parent_time = NOTIFIED_TIME (parent); |
181 | 0 | if (nsync_time_cmp (parent_time, abs_deadline) < 0) { |
182 | 0 | set_expiry_time (n, parent_time); |
183 | 0 | } |
184 | 0 | if (nsync_time_cmp (parent_time, nsync_time_zero) > 0) { |
185 | 0 | n->parent = parent; |
186 | 0 | parent->children = nsync_dll_make_last_in_list_ (parent->children, |
187 | 0 | &n->parent_child_link); |
188 | 0 | } |
189 | 0 | nsync_mu_unlock (&parent->note_mu); |
190 | 0 | } |
191 | 0 | } |
192 | 0 | return (n); |
193 | 0 | } |
194 | | |
195 | 0 | void nsync_note_free (nsync_note n) { |
196 | 0 | nsync_note parent; |
197 | 0 | nsync_dll_element_ *p; |
198 | 0 | nsync_dll_element_ *next; |
199 | 0 | nsync_mu_lock (&n->note_mu); |
200 | 0 | n->disconnecting++; |
201 | 0 | ASSERT (nsync_dll_is_empty_ (n->waiters)); |
202 | 0 | parent = n->parent; |
203 | 0 | if (parent != NULL && !nsync_mu_trylock (&parent->note_mu)) { |
204 | 0 | nsync_mu_unlock (&n->note_mu); |
205 | 0 | nsync_mu_lock (&parent->note_mu); |
206 | 0 | nsync_mu_lock (&n->note_mu); |
207 | 0 | } |
208 | 0 | for (p = nsync_dll_first_ (n->children); p != NULL; p = next) { |
209 | 0 | nsync_note child = DLL_NOTE (p); |
210 | 0 | next = nsync_dll_next_ (n->children, p); |
211 | 0 | nsync_mu_lock (&child->note_mu); |
212 | 0 | if (child->disconnecting == 0) { |
213 | 0 | n->children = nsync_dll_remove_ (n->children, |
214 | 0 | &child->parent_child_link); |
215 | 0 | if (parent != NULL) { |
216 | 0 | child->parent = parent; |
217 | 0 | parent->children = nsync_dll_make_last_in_list_ ( |
218 | 0 | parent->children, &child->parent_child_link); |
219 | 0 | } else { |
220 | 0 | child->parent = NULL; |
221 | 0 | } |
222 | 0 | } |
223 | 0 | nsync_mu_unlock (&child->note_mu); |
224 | 0 | } |
225 | 0 | WAIT_FOR_NO_CHILDREN (no_children, n); |
226 | 0 | if (parent != NULL) { |
227 | 0 | parent->children = nsync_dll_remove_ (parent->children, |
228 | 0 | &n->parent_child_link); |
229 | 0 | WAKEUP_NO_CHILDREN (parent); |
230 | 0 | n->parent = NULL; |
231 | 0 | nsync_mu_unlock (&parent->note_mu); |
232 | 0 | } |
233 | 0 | n->disconnecting--; |
234 | 0 | nsync_mu_unlock (&n->note_mu); |
235 | 0 | free (n); |
236 | 0 | } |
237 | | |
238 | 0 | void nsync_note_notify (nsync_note n) { |
239 | 0 | IGNORE_RACES_START (); |
240 | 0 | if (nsync_time_cmp (nsync_note_notified_deadline_ (n), nsync_time_zero) > 0) { |
241 | 0 | notify (n); |
242 | 0 | } |
243 | 0 | IGNORE_RACES_END (); |
244 | 0 | } |
245 | | |
246 | 0 | int nsync_note_wait (nsync_note n, nsync_time abs_deadline) { |
247 | 0 | struct nsync_waitable_s waitable; |
248 | 0 | struct nsync_waitable_s *pwaitable = &waitable; |
249 | 0 | waitable.v = n; |
250 | 0 | waitable.funcs = &nsync_note_waitable_funcs; |
251 | 0 | return (nsync_wait_n (NULL, NULL, NULL, abs_deadline, 1, &pwaitable) == 0); |
252 | 0 | } |
253 | | |
254 | 0 | nsync_time nsync_note_expiry (nsync_note n) { |
255 | 0 | return (n->expiry_time); |
256 | 0 | } |
257 | | |
258 | 0 | static nsync_time note_ready_time (void *v, struct nsync_waiter_s *nw UNUSED) { |
259 | 0 | return (nsync_note_notified_deadline_ ((nsync_note)v)); |
260 | 0 | } |
261 | | |
262 | 0 | static int note_enqueue (void *v, struct nsync_waiter_s *nw) { |
263 | 0 | int waiting = 0; |
264 | 0 | nsync_note n = (nsync_note) v; |
265 | 0 | nsync_time ntime; |
266 | 0 | nsync_mu_lock (&n->note_mu); |
267 | 0 | ntime = NOTIFIED_TIME (n); |
268 | 0 | if (nsync_time_cmp (ntime, nsync_time_zero) > 0) { |
269 | 0 | n->waiters = nsync_dll_make_last_in_list_ (n->waiters, &nw->q); |
270 | 0 | ATM_STORE (&nw->waiting, 1); |
271 | 0 | waiting = 1; |
272 | 0 | } else { |
273 | 0 | ATM_STORE (&nw->waiting, 0); |
274 | 0 | waiting = 0; |
275 | 0 | } |
276 | 0 | nsync_mu_unlock (&n->note_mu); |
277 | 0 | return (waiting); |
278 | 0 | } |
279 | | |
280 | 0 | static int note_dequeue (void *v, struct nsync_waiter_s *nw) { |
281 | 0 | int was_queued = 0; |
282 | 0 | nsync_note n = (nsync_note) v; |
283 | 0 | nsync_time ntime; |
284 | 0 | nsync_note_notified_deadline_ (n); |
285 | 0 | nsync_mu_lock (&n->note_mu); |
286 | 0 | ntime = NOTIFIED_TIME (n); |
287 | 0 | if (nsync_time_cmp (ntime, nsync_time_zero) > 0) { |
288 | 0 | n->waiters = nsync_dll_remove_ (n->waiters, &nw->q); |
289 | 0 | ATM_STORE (&nw->waiting, 0); |
290 | 0 | was_queued = 1; |
291 | 0 | } |
292 | 0 | nsync_mu_unlock (&n->note_mu); |
293 | 0 | return (was_queued); |
294 | 0 | } |
295 | | |
296 | | const struct nsync_waitable_funcs_s nsync_note_waitable_funcs = { |
297 | | ¬e_ready_time, |
298 | | ¬e_enqueue, |
299 | | ¬e_dequeue |
300 | | }; |
301 | | |
302 | | NSYNC_CPP_END_ |