source: mainline/uspace/lib/c/generic/fibril_synch.c@ 55bd76c

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 55bd76c was 55bd76c, checked in by Jakub Jermar <jakub@…>, 15 years ago

More straightforward use of check_for_deadlock().

  • Property mode set to 100644
File size: 8.7 KB
Line 
1/*
2 * Copyright (c) 2009 Jakub Jermar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libc
30 * @{
31 */
32/** @file
33 */
34
35#include <fibril_synch.h>
36#include <fibril.h>
37#include <async.h>
38#include <async_priv.h>
39#include <adt/list.h>
40#include <futex.h>
41#include <sys/time.h>
42#include <errno.h>
43#include <assert.h>
44#include <stacktrace.h>
45#include <stdlib.h>
46
47static void optimize_execution_power(void)
48{
49 /*
50 * When waking up a worker fibril previously blocked in fibril
51 * synchronization, chances are that there is an idle manager fibril
52 * waiting for IPC, that could start executing the awakened worker
53 * fibril right away. We try to detect this and bring the manager
54 * fibril back to fruitful work.
55 */
56 if (atomic_get(&threads_in_ipc_wait) > 0)
57 ipc_poke();
58}
59
60static void print_deadlock(fibril_owner_info_t *oi)
61{
62 fibril_t *f = (fibril_t *) fibril_get_id();
63
64 printf("Deadlock detected.\n");
65 stacktrace_print();
66
67 printf("Fibril %p waits for primitive %p.\n", f, oi);
68
69 while (oi && oi->owned_by) {
70 printf("Primitive %p is owned by fibril %p.\n",
71 oi, oi->owned_by);
72 if (oi->owned_by == f)
73 break;
74 stacktrace_print_fp_pc(context_get_fp(&oi->owned_by->ctx),
75 oi->owned_by->ctx.pc);
76 printf("Fibril %p waits for primitive %p.\n",
77 oi->owned_by, oi->owned_by->waits_for);
78 oi = oi->owned_by->waits_for;
79 }
80}
81
82
83static void check_for_deadlock(fibril_owner_info_t *oi)
84{
85 while (oi && oi->owned_by) {
86 if (oi->owned_by == (fibril_t *) fibril_get_id()) {
87 print_deadlock(oi);
88 abort();
89 }
90 oi = oi->owned_by->waits_for;
91 }
92}
93
94
95void fibril_mutex_initialize(fibril_mutex_t *fm)
96{
97 fm->oi.owned_by = NULL;
98 fm->counter = 1;
99 list_initialize(&fm->waiters);
100}
101
102void fibril_mutex_lock(fibril_mutex_t *fm)
103{
104 fibril_t *f = (fibril_t *) fibril_get_id();
105
106 futex_down(&async_futex);
107 if (fm->counter-- <= 0) {
108 awaiter_t wdata;
109
110 wdata.fid = fibril_get_id();
111 wdata.active = false;
112 wdata.wu_event.inlist = true;
113 link_initialize(&wdata.wu_event.link);
114 list_append(&wdata.wu_event.link, &fm->waiters);
115 check_for_deadlock(&fm->oi);
116 f->waits_for = &fm->oi;
117 fibril_switch(FIBRIL_TO_MANAGER);
118 } else {
119 fm->oi.owned_by = f;
120 futex_up(&async_futex);
121 }
122}
123
124bool fibril_mutex_trylock(fibril_mutex_t *fm)
125{
126 bool locked = false;
127
128 futex_down(&async_futex);
129 if (fm->counter > 0) {
130 fm->counter--;
131 fm->oi.owned_by = (fibril_t *) fibril_get_id();
132 locked = true;
133 }
134 futex_up(&async_futex);
135
136 return locked;
137}
138
139static void _fibril_mutex_unlock_unsafe(fibril_mutex_t *fm)
140{
141 assert(fm->counter <= 0);
142 if (fm->counter++ < 0) {
143 link_t *tmp;
144 awaiter_t *wdp;
145 fibril_t *f;
146
147 assert(!list_empty(&fm->waiters));
148 tmp = fm->waiters.next;
149 wdp = list_get_instance(tmp, awaiter_t, wu_event.link);
150 wdp->active = true;
151 wdp->wu_event.inlist = false;
152
153 f = (fibril_t *) wdp->fid;
154 fm->oi.owned_by = f;
155 f->waits_for = NULL;
156
157 list_remove(&wdp->wu_event.link);
158 fibril_add_ready(wdp->fid);
159 optimize_execution_power();
160 } else {
161 fm->oi.owned_by = NULL;
162 }
163}
164
165void fibril_mutex_unlock(fibril_mutex_t *fm)
166{
167 futex_down(&async_futex);
168 _fibril_mutex_unlock_unsafe(fm);
169 futex_up(&async_futex);
170}
171
172void fibril_rwlock_initialize(fibril_rwlock_t *frw)
173{
174 frw->oi.owned_by = NULL;
175 frw->writers = 0;
176 frw->readers = 0;
177 list_initialize(&frw->waiters);
178}
179
180void fibril_rwlock_read_lock(fibril_rwlock_t *frw)
181{
182 futex_down(&async_futex);
183 if (frw->writers) {
184 fibril_t *f = (fibril_t *) fibril_get_id();
185 awaiter_t wdata;
186
187 wdata.fid = (fid_t) f;
188 wdata.active = false;
189 wdata.wu_event.inlist = true;
190 link_initialize(&wdata.wu_event.link);
191 f->flags &= ~FIBRIL_WRITER;
192 list_append(&wdata.wu_event.link, &frw->waiters);
193 check_for_deadlock(&frw->oi);
194 f->waits_for = &frw->oi;
195 fibril_switch(FIBRIL_TO_MANAGER);
196 } else {
197 frw->readers++;
198 futex_up(&async_futex);
199 }
200}
201
202void fibril_rwlock_write_lock(fibril_rwlock_t *frw)
203{
204 fibril_t *f = (fibril_t *) fibril_get_id();
205
206 futex_down(&async_futex);
207 if (frw->writers || frw->readers) {
208 awaiter_t wdata;
209
210 wdata.fid = (fid_t) f;
211 wdata.active = false;
212 wdata.wu_event.inlist = true;
213 link_initialize(&wdata.wu_event.link);
214 f->flags |= FIBRIL_WRITER;
215 list_append(&wdata.wu_event.link, &frw->waiters);
216 check_for_deadlock(&frw->oi);
217 f->waits_for = &frw->oi;
218 fibril_switch(FIBRIL_TO_MANAGER);
219 } else {
220 frw->oi.owned_by = f;
221 frw->writers++;
222 futex_up(&async_futex);
223 }
224}
225
226static void _fibril_rwlock_common_unlock(fibril_rwlock_t *frw)
227{
228 futex_down(&async_futex);
229 assert(frw->readers || (frw->writers == 1));
230 if (frw->readers) {
231 if (--frw->readers)
232 goto out;
233 } else {
234 frw->writers--;
235 }
236
237 assert(!frw->readers && !frw->writers);
238
239 frw->oi.owned_by = NULL;
240
241 while (!list_empty(&frw->waiters)) {
242 link_t *tmp = frw->waiters.next;
243 awaiter_t *wdp;
244 fibril_t *f;
245
246 wdp = list_get_instance(tmp, awaiter_t, wu_event.link);
247 f = (fibril_t *) wdp->fid;
248
249 f->waits_for = NULL;
250
251 if (f->flags & FIBRIL_WRITER) {
252 if (frw->readers)
253 break;
254 wdp->active = true;
255 wdp->wu_event.inlist = false;
256 list_remove(&wdp->wu_event.link);
257 fibril_add_ready(wdp->fid);
258 frw->writers++;
259 frw->oi.owned_by = f;
260 optimize_execution_power();
261 break;
262 } else {
263 wdp->active = true;
264 wdp->wu_event.inlist = false;
265 list_remove(&wdp->wu_event.link);
266 fibril_add_ready(wdp->fid);
267 frw->readers++;
268 optimize_execution_power();
269 }
270 }
271out:
272 futex_up(&async_futex);
273}
274
275void fibril_rwlock_read_unlock(fibril_rwlock_t *frw)
276{
277 _fibril_rwlock_common_unlock(frw);
278}
279
280void fibril_rwlock_write_unlock(fibril_rwlock_t *frw)
281{
282 _fibril_rwlock_common_unlock(frw);
283}
284
285void fibril_condvar_initialize(fibril_condvar_t *fcv)
286{
287 list_initialize(&fcv->waiters);
288}
289
290int
291fibril_condvar_wait_timeout(fibril_condvar_t *fcv, fibril_mutex_t *fm,
292 suseconds_t timeout)
293{
294 awaiter_t wdata;
295
296 if (timeout < 0)
297 return ETIMEOUT;
298
299 wdata.fid = fibril_get_id();
300 wdata.active = false;
301
302 wdata.to_event.inlist = timeout > 0;
303 wdata.to_event.occurred = false;
304 link_initialize(&wdata.to_event.link);
305
306 wdata.wu_event.inlist = true;
307 link_initialize(&wdata.wu_event.link);
308
309 futex_down(&async_futex);
310 if (timeout) {
311 gettimeofday(&wdata.to_event.expires, NULL);
312 tv_add(&wdata.to_event.expires, timeout);
313 async_insert_timeout(&wdata);
314 }
315 list_append(&wdata.wu_event.link, &fcv->waiters);
316 _fibril_mutex_unlock_unsafe(fm);
317 fibril_switch(FIBRIL_TO_MANAGER);
318 fibril_mutex_lock(fm);
319
320 /* async_futex not held after fibril_switch() */
321 futex_down(&async_futex);
322 if (wdata.to_event.inlist)
323 list_remove(&wdata.to_event.link);
324 if (wdata.wu_event.inlist)
325 list_remove(&wdata.wu_event.link);
326 futex_up(&async_futex);
327
328 return wdata.to_event.occurred ? ETIMEOUT : EOK;
329}
330
331void fibril_condvar_wait(fibril_condvar_t *fcv, fibril_mutex_t *fm)
332{
333 int rc;
334
335 rc = fibril_condvar_wait_timeout(fcv, fm, 0);
336 assert(rc == EOK);
337}
338
339static void _fibril_condvar_wakeup_common(fibril_condvar_t *fcv, bool once)
340{
341 link_t *tmp;
342 awaiter_t *wdp;
343
344 futex_down(&async_futex);
345 while (!list_empty(&fcv->waiters)) {
346 tmp = fcv->waiters.next;
347 wdp = list_get_instance(tmp, awaiter_t, wu_event.link);
348 list_remove(&wdp->wu_event.link);
349 wdp->wu_event.inlist = false;
350 if (!wdp->active) {
351 wdp->active = true;
352 fibril_add_ready(wdp->fid);
353 optimize_execution_power();
354 if (once)
355 break;
356 }
357 }
358 futex_up(&async_futex);
359}
360
361void fibril_condvar_signal(fibril_condvar_t *fcv)
362{
363 _fibril_condvar_wakeup_common(fcv, true);
364}
365
366void fibril_condvar_broadcast(fibril_condvar_t *fcv)
367{
368 _fibril_condvar_wakeup_common(fcv, false);
369}
370
371/** @}
372 */
Note: See TracBrowser for help on using the repository browser.