source: mainline/uspace/lib/c/generic/fibril_synch.c@ 649efcd

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

Add support for detecting deadlocks involving fibril rwlocks.

Readers are only tracked when waiting for a rwlock, but it is unfortunatelly not
possible to track their ownership of a rwlock. Because of this, some deadlock
scenarios can go unnoticed by the detection system.

  • Property mode set to 100644
File size: 8.8 KB
RevLine 
[f3afd24]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
[1e4cada]35#include <fibril_synch.h>
[f3afd24]36#include <fibril.h>
37#include <async.h>
[cadfa8e]38#include <async_priv.h>
[f3afd24]39#include <adt/list.h>
40#include <futex.h>
[cadfa8e]41#include <sys/time.h>
42#include <errno.h>
[f3afd24]43#include <assert.h>
[1fa010c]44#include <stacktrace.h>
45#include <stdlib.h>
[f3afd24]46
[8619f25]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
[1fa010c]60static bool check_for_deadlock(fibril_owner_info_t *oi)
61{
62 while (oi && oi->owned_by) {
63 if (oi->owned_by == (fibril_t *) fibril_get_id())
64 return true;
65 oi = oi->owned_by->waits_for;
66 }
67
68 return false;
69}
70
71static void print_deadlock(fibril_owner_info_t *oi)
72{
73 fibril_t *f = (fibril_t *) fibril_get_id();
74
[12c38f5]75 printf("Deadlock detected.\n");
76 stacktrace_print();
[1fa010c]77
78 printf("Fibril %p waits for primitive %p.\n", f, oi);
79
80 while (oi && oi->owned_by) {
81 printf("Primitive %p is owned by fibril %p.\n",
82 oi, oi->owned_by);
83 if (oi->owned_by == f)
84 break;
[84b7384]85 stacktrace_print_fp_pc(context_get_fp(&oi->owned_by->ctx),
[12c38f5]86 oi->owned_by->ctx.pc);
[1fa010c]87 printf("Fibril %p waits for primitive %p.\n",
88 oi->owned_by, oi->owned_by->waits_for);
89 oi = oi->owned_by->waits_for;
90 }
91
92 abort();
93}
94
[f3afd24]95void fibril_mutex_initialize(fibril_mutex_t *fm)
96{
[3e20fd48]97 fm->oi.owned_by = NULL;
[f3afd24]98 fm->counter = 1;
99 list_initialize(&fm->waiters);
100}
101
102void fibril_mutex_lock(fibril_mutex_t *fm)
103{
[525df28]104 fibril_t *f = (fibril_t *) fibril_get_id();
105
[f3afd24]106 futex_down(&async_futex);
107 if (fm->counter-- <= 0) {
[854ad23]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);
[525df28]115
[1fa010c]116 if (check_for_deadlock(&fm->oi))
117 print_deadlock(&fm->oi);
[525df28]118 f->waits_for = &fm->oi;
119
[f3afd24]120 fibril_switch(FIBRIL_TO_MANAGER);
121 } else {
[525df28]122 fm->oi.owned_by = f;
[f3afd24]123 futex_up(&async_futex);
124 }
125}
126
127bool fibril_mutex_trylock(fibril_mutex_t *fm)
128{
129 bool locked = false;
130
131 futex_down(&async_futex);
132 if (fm->counter > 0) {
133 fm->counter--;
[3e20fd48]134 fm->oi.owned_by = (fibril_t *) fibril_get_id();
[f3afd24]135 locked = true;
136 }
137 futex_up(&async_futex);
138
139 return locked;
140}
141
[9ae22ba]142static void _fibril_mutex_unlock_unsafe(fibril_mutex_t *fm)
[f3afd24]143{
144 assert(fm->counter <= 0);
145 if (fm->counter++ < 0) {
146 link_t *tmp;
[854ad23]147 awaiter_t *wdp;
[525df28]148 fibril_t *f;
[f3afd24]149
150 assert(!list_empty(&fm->waiters));
151 tmp = fm->waiters.next;
[854ad23]152 wdp = list_get_instance(tmp, awaiter_t, wu_event.link);
[bbb01b98]153 wdp->active = true;
[854ad23]154 wdp->wu_event.inlist = false;
[525df28]155
156 f = (fibril_t *) wdp->fid;
157 fm->oi.owned_by = f;
158 f->waits_for = NULL;
159
[854ad23]160 list_remove(&wdp->wu_event.link);
161 fibril_add_ready(wdp->fid);
[8619f25]162 optimize_execution_power();
[3e20fd48]163 } else {
164 fm->oi.owned_by = NULL;
[f3afd24]165 }
[9ae22ba]166}
167
168void fibril_mutex_unlock(fibril_mutex_t *fm)
169{
170 futex_down(&async_futex);
171 _fibril_mutex_unlock_unsafe(fm);
[f3afd24]172 futex_up(&async_futex);
173}
174
175void fibril_rwlock_initialize(fibril_rwlock_t *frw)
176{
[3e20fd48]177 frw->oi.owned_by = NULL;
[92d34f0b]178 frw->writers = 0;
179 frw->readers = 0;
180 list_initialize(&frw->waiters);
[f3afd24]181}
182
183void fibril_rwlock_read_lock(fibril_rwlock_t *frw)
184{
[92d34f0b]185 futex_down(&async_futex);
186 if (frw->writers) {
187 fibril_t *f = (fibril_t *) fibril_get_id();
[b69bec5]188 awaiter_t wdata;
189
190 wdata.fid = (fid_t) f;
191 wdata.active = false;
192 wdata.wu_event.inlist = true;
193 link_initialize(&wdata.wu_event.link);
[92d34f0b]194 f->flags &= ~FIBRIL_WRITER;
[b69bec5]195 list_append(&wdata.wu_event.link, &frw->waiters);
[649efcd]196
197 if (check_for_deadlock(&frw->oi))
198 print_deadlock(&frw->oi);
199 f->waits_for = &frw->oi;
200
[92d34f0b]201 fibril_switch(FIBRIL_TO_MANAGER);
202 } else {
203 frw->readers++;
204 futex_up(&async_futex);
205 }
[f3afd24]206}
207
208void fibril_rwlock_write_lock(fibril_rwlock_t *frw)
209{
[649efcd]210 fibril_t *f = (fibril_t *) fibril_get_id();
211
[92d34f0b]212 futex_down(&async_futex);
213 if (frw->writers || frw->readers) {
[b69bec5]214 awaiter_t wdata;
215
216 wdata.fid = (fid_t) f;
217 wdata.active = false;
218 wdata.wu_event.inlist = true;
219 link_initialize(&wdata.wu_event.link);
[92d34f0b]220 f->flags |= FIBRIL_WRITER;
[b69bec5]221 list_append(&wdata.wu_event.link, &frw->waiters);
[649efcd]222
223 if (check_for_deadlock(&frw->oi))
224 print_deadlock(&frw->oi);
225 f->waits_for = &frw->oi;
226
[92d34f0b]227 fibril_switch(FIBRIL_TO_MANAGER);
228 } else {
[649efcd]229 frw->oi.owned_by = f;
[92d34f0b]230 frw->writers++;
231 futex_up(&async_futex);
232 }
233}
234
235static void _fibril_rwlock_common_unlock(fibril_rwlock_t *frw)
236{
237 futex_down(&async_futex);
238 assert(frw->readers || (frw->writers == 1));
239 if (frw->readers) {
240 if (--frw->readers)
241 goto out;
242 } else {
243 frw->writers--;
244 }
245
246 assert(!frw->readers && !frw->writers);
247
[649efcd]248 frw->oi.owned_by = NULL;
249
[92d34f0b]250 while (!list_empty(&frw->waiters)) {
251 link_t *tmp = frw->waiters.next;
[b69bec5]252 awaiter_t *wdp;
253 fibril_t *f;
254
255 wdp = list_get_instance(tmp, awaiter_t, wu_event.link);
256 f = (fibril_t *) wdp->fid;
[92d34f0b]257
[649efcd]258 f->waits_for = NULL;
259
[92d34f0b]260 if (f->flags & FIBRIL_WRITER) {
261 if (frw->readers)
262 break;
[b69bec5]263 wdp->active = true;
264 wdp->wu_event.inlist = false;
265 list_remove(&wdp->wu_event.link);
266 fibril_add_ready(wdp->fid);
[92d34f0b]267 frw->writers++;
[649efcd]268 frw->oi.owned_by = f;
[8619f25]269 optimize_execution_power();
[92d34f0b]270 break;
271 } else {
[b69bec5]272 wdp->active = true;
273 wdp->wu_event.inlist = false;
274 list_remove(&wdp->wu_event.link);
275 fibril_add_ready(wdp->fid);
[92d34f0b]276 frw->readers++;
[8619f25]277 optimize_execution_power();
[92d34f0b]278 }
279 }
280out:
281 futex_up(&async_futex);
[f3afd24]282}
283
284void fibril_rwlock_read_unlock(fibril_rwlock_t *frw)
285{
[92d34f0b]286 _fibril_rwlock_common_unlock(frw);
[f3afd24]287}
288
289void fibril_rwlock_write_unlock(fibril_rwlock_t *frw)
290{
[92d34f0b]291 _fibril_rwlock_common_unlock(frw);
[f3afd24]292}
293
[9ae22ba]294void fibril_condvar_initialize(fibril_condvar_t *fcv)
295{
296 list_initialize(&fcv->waiters);
297}
298
[cadfa8e]299int
300fibril_condvar_wait_timeout(fibril_condvar_t *fcv, fibril_mutex_t *fm,
301 suseconds_t timeout)
[9ae22ba]302{
[cadfa8e]303 awaiter_t wdata;
304
305 if (timeout < 0)
306 return ETIMEOUT;
307
308 wdata.fid = fibril_get_id();
309 wdata.active = false;
310
311 wdata.to_event.inlist = timeout > 0;
312 wdata.to_event.occurred = false;
313 link_initialize(&wdata.to_event.link);
314
315 wdata.wu_event.inlist = true;
316 link_initialize(&wdata.wu_event.link);
[9ae22ba]317
318 futex_down(&async_futex);
[cadfa8e]319 if (timeout) {
320 gettimeofday(&wdata.to_event.expires, NULL);
321 tv_add(&wdata.to_event.expires, timeout);
322 async_insert_timeout(&wdata);
323 }
324 list_append(&wdata.wu_event.link, &fcv->waiters);
[9ae22ba]325 _fibril_mutex_unlock_unsafe(fm);
326 fibril_switch(FIBRIL_TO_MANAGER);
327 fibril_mutex_lock(fm);
[cadfa8e]328
329 /* async_futex not held after fibril_switch() */
330 futex_down(&async_futex);
331 if (wdata.to_event.inlist)
332 list_remove(&wdata.to_event.link);
333 if (wdata.wu_event.inlist)
334 list_remove(&wdata.wu_event.link);
335 futex_up(&async_futex);
336
337 return wdata.to_event.occurred ? ETIMEOUT : EOK;
338}
339
340void fibril_condvar_wait(fibril_condvar_t *fcv, fibril_mutex_t *fm)
341{
342 int rc;
343
344 rc = fibril_condvar_wait_timeout(fcv, fm, 0);
345 assert(rc == EOK);
[9ae22ba]346}
347
348static void _fibril_condvar_wakeup_common(fibril_condvar_t *fcv, bool once)
349{
350 link_t *tmp;
[cadfa8e]351 awaiter_t *wdp;
[9ae22ba]352
353 futex_down(&async_futex);
354 while (!list_empty(&fcv->waiters)) {
355 tmp = fcv->waiters.next;
[cadfa8e]356 wdp = list_get_instance(tmp, awaiter_t, wu_event.link);
357 list_remove(&wdp->wu_event.link);
358 wdp->wu_event.inlist = false;
359 if (!wdp->active) {
360 wdp->active = true;
361 fibril_add_ready(wdp->fid);
362 optimize_execution_power();
363 if (once)
364 break;
365 }
[9ae22ba]366 }
367 futex_up(&async_futex);
368}
369
370void fibril_condvar_signal(fibril_condvar_t *fcv)
371{
372 _fibril_condvar_wakeup_common(fcv, true);
373}
374
375void fibril_condvar_broadcast(fibril_condvar_t *fcv)
376{
377 _fibril_condvar_wakeup_common(fcv, false);
378}
379
[f3afd24]380/** @}
381 */
Note: See TracBrowser for help on using the repository browser.