source: mainline/uspace/lib/c/generic/fibril_synch.c@ be942bc

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

Add deadlock detection for cases when the rwlock is owned by only one reader.
Associate the rwlock ownership with the first reader so that the detection works
at least partially even if the lock is owned by multiple readers. The ownership
information is not passed onto the remaining readers when the owner unlocks the
lock.

  • Property mode set to 100644
File size: 9.4 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 void print_deadlock(fibril_owner_info_t *oi)
61{
62 fibril_t *f = (fibril_t *) fibril_get_id();
63
[12c38f5]64 printf("Deadlock detected.\n");
65 stacktrace_print();
[1fa010c]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;
[84b7384]74 stacktrace_print_fp_pc(context_get_fp(&oi->owned_by->ctx),
[12c38f5]75 oi->owned_by->ctx.pc);
[1fa010c]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 }
[55bd76c]80}
[1fa010c]81
[55bd76c]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 }
[1fa010c]92}
93
[55bd76c]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);
[55bd76c]115 check_for_deadlock(&fm->oi);
[525df28]116 f->waits_for = &fm->oi;
[f3afd24]117 fibril_switch(FIBRIL_TO_MANAGER);
118 } else {
[525df28]119 fm->oi.owned_by = f;
[f3afd24]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--;
[3e20fd48]131 fm->oi.owned_by = (fibril_t *) fibril_get_id();
[f3afd24]132 locked = true;
133 }
134 futex_up(&async_futex);
135
136 return locked;
137}
138
[9ae22ba]139static void _fibril_mutex_unlock_unsafe(fibril_mutex_t *fm)
[f3afd24]140{
141 assert(fm->counter <= 0);
142 if (fm->counter++ < 0) {
143 link_t *tmp;
[854ad23]144 awaiter_t *wdp;
[525df28]145 fibril_t *f;
[f3afd24]146
147 assert(!list_empty(&fm->waiters));
148 tmp = fm->waiters.next;
[854ad23]149 wdp = list_get_instance(tmp, awaiter_t, wu_event.link);
[bbb01b98]150 wdp->active = true;
[854ad23]151 wdp->wu_event.inlist = false;
[525df28]152
153 f = (fibril_t *) wdp->fid;
154 fm->oi.owned_by = f;
155 f->waits_for = NULL;
156
[854ad23]157 list_remove(&wdp->wu_event.link);
158 fibril_add_ready(wdp->fid);
[8619f25]159 optimize_execution_power();
[3e20fd48]160 } else {
161 fm->oi.owned_by = NULL;
[f3afd24]162 }
[9ae22ba]163}
164
165void fibril_mutex_unlock(fibril_mutex_t *fm)
166{
167 futex_down(&async_futex);
168 _fibril_mutex_unlock_unsafe(fm);
[f3afd24]169 futex_up(&async_futex);
170}
171
172void fibril_rwlock_initialize(fibril_rwlock_t *frw)
173{
[3e20fd48]174 frw->oi.owned_by = NULL;
[92d34f0b]175 frw->writers = 0;
176 frw->readers = 0;
177 list_initialize(&frw->waiters);
[f3afd24]178}
179
180void fibril_rwlock_read_lock(fibril_rwlock_t *frw)
181{
[9414abc]182 fibril_t *f = (fibril_t *) fibril_get_id();
183
[92d34f0b]184 futex_down(&async_futex);
185 if (frw->writers) {
[b69bec5]186 awaiter_t wdata;
187
188 wdata.fid = (fid_t) f;
189 wdata.active = false;
190 wdata.wu_event.inlist = true;
191 link_initialize(&wdata.wu_event.link);
[92d34f0b]192 f->flags &= ~FIBRIL_WRITER;
[b69bec5]193 list_append(&wdata.wu_event.link, &frw->waiters);
[55bd76c]194 check_for_deadlock(&frw->oi);
[649efcd]195 f->waits_for = &frw->oi;
[92d34f0b]196 fibril_switch(FIBRIL_TO_MANAGER);
197 } else {
[9414abc]198 /* Consider the first reader the owner. */
199 if (frw->readers++ == 0)
200 frw->oi.owned_by = f;
[92d34f0b]201 futex_up(&async_futex);
202 }
[f3afd24]203}
204
205void fibril_rwlock_write_lock(fibril_rwlock_t *frw)
206{
[649efcd]207 fibril_t *f = (fibril_t *) fibril_get_id();
208
[92d34f0b]209 futex_down(&async_futex);
210 if (frw->writers || frw->readers) {
[b69bec5]211 awaiter_t wdata;
212
213 wdata.fid = (fid_t) f;
214 wdata.active = false;
215 wdata.wu_event.inlist = true;
216 link_initialize(&wdata.wu_event.link);
[92d34f0b]217 f->flags |= FIBRIL_WRITER;
[b69bec5]218 list_append(&wdata.wu_event.link, &frw->waiters);
[55bd76c]219 check_for_deadlock(&frw->oi);
[649efcd]220 f->waits_for = &frw->oi;
[92d34f0b]221 fibril_switch(FIBRIL_TO_MANAGER);
222 } else {
[649efcd]223 frw->oi.owned_by = f;
[92d34f0b]224 frw->writers++;
225 futex_up(&async_futex);
226 }
227}
228
229static void _fibril_rwlock_common_unlock(fibril_rwlock_t *frw)
230{
231 futex_down(&async_futex);
232 assert(frw->readers || (frw->writers == 1));
233 if (frw->readers) {
[9414abc]234 if (--frw->readers) {
235 if (frw->oi.owned_by == (fibril_t *) fibril_get_id()) {
236 /*
237 * If this reader firbril was considered the
238 * owner of this rwlock, clear the ownership
239 * information even if there are still more
240 * readers.
241 *
242 * This is the limitation of the detection
243 * mechanism rooted in the fact that tracking
244 * all readers would require dynamically
245 * allocated memory for keeping linkage info.
246 */
247 frw->oi.owned_by = NULL;
248 }
[92d34f0b]249 goto out;
[9414abc]250 }
[92d34f0b]251 } else {
252 frw->writers--;
253 }
254
255 assert(!frw->readers && !frw->writers);
256
[649efcd]257 frw->oi.owned_by = NULL;
258
[92d34f0b]259 while (!list_empty(&frw->waiters)) {
260 link_t *tmp = frw->waiters.next;
[b69bec5]261 awaiter_t *wdp;
262 fibril_t *f;
263
264 wdp = list_get_instance(tmp, awaiter_t, wu_event.link);
265 f = (fibril_t *) wdp->fid;
[92d34f0b]266
[649efcd]267 f->waits_for = NULL;
268
[92d34f0b]269 if (f->flags & FIBRIL_WRITER) {
270 if (frw->readers)
271 break;
[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->writers++;
[649efcd]277 frw->oi.owned_by = f;
[8619f25]278 optimize_execution_power();
[92d34f0b]279 break;
280 } else {
[b69bec5]281 wdp->active = true;
282 wdp->wu_event.inlist = false;
283 list_remove(&wdp->wu_event.link);
284 fibril_add_ready(wdp->fid);
[9414abc]285 if (frw->readers++ == 0) {
286 /* Consider the first reader the owner. */
287 frw->oi.owned_by = f;
288 }
[8619f25]289 optimize_execution_power();
[92d34f0b]290 }
291 }
292out:
293 futex_up(&async_futex);
[f3afd24]294}
295
296void fibril_rwlock_read_unlock(fibril_rwlock_t *frw)
297{
[92d34f0b]298 _fibril_rwlock_common_unlock(frw);
[f3afd24]299}
300
301void fibril_rwlock_write_unlock(fibril_rwlock_t *frw)
302{
[92d34f0b]303 _fibril_rwlock_common_unlock(frw);
[f3afd24]304}
305
[9ae22ba]306void fibril_condvar_initialize(fibril_condvar_t *fcv)
307{
308 list_initialize(&fcv->waiters);
309}
310
[cadfa8e]311int
312fibril_condvar_wait_timeout(fibril_condvar_t *fcv, fibril_mutex_t *fm,
313 suseconds_t timeout)
[9ae22ba]314{
[cadfa8e]315 awaiter_t wdata;
316
317 if (timeout < 0)
318 return ETIMEOUT;
319
320 wdata.fid = fibril_get_id();
321 wdata.active = false;
322
323 wdata.to_event.inlist = timeout > 0;
324 wdata.to_event.occurred = false;
325 link_initialize(&wdata.to_event.link);
326
327 wdata.wu_event.inlist = true;
328 link_initialize(&wdata.wu_event.link);
[9ae22ba]329
330 futex_down(&async_futex);
[cadfa8e]331 if (timeout) {
332 gettimeofday(&wdata.to_event.expires, NULL);
333 tv_add(&wdata.to_event.expires, timeout);
334 async_insert_timeout(&wdata);
335 }
336 list_append(&wdata.wu_event.link, &fcv->waiters);
[9ae22ba]337 _fibril_mutex_unlock_unsafe(fm);
338 fibril_switch(FIBRIL_TO_MANAGER);
339 fibril_mutex_lock(fm);
[cadfa8e]340
341 /* async_futex not held after fibril_switch() */
342 futex_down(&async_futex);
343 if (wdata.to_event.inlist)
344 list_remove(&wdata.to_event.link);
345 if (wdata.wu_event.inlist)
346 list_remove(&wdata.wu_event.link);
347 futex_up(&async_futex);
348
349 return wdata.to_event.occurred ? ETIMEOUT : EOK;
350}
351
352void fibril_condvar_wait(fibril_condvar_t *fcv, fibril_mutex_t *fm)
353{
354 int rc;
355
356 rc = fibril_condvar_wait_timeout(fcv, fm, 0);
357 assert(rc == EOK);
[9ae22ba]358}
359
360static void _fibril_condvar_wakeup_common(fibril_condvar_t *fcv, bool once)
361{
362 link_t *tmp;
[cadfa8e]363 awaiter_t *wdp;
[9ae22ba]364
365 futex_down(&async_futex);
366 while (!list_empty(&fcv->waiters)) {
367 tmp = fcv->waiters.next;
[cadfa8e]368 wdp = list_get_instance(tmp, awaiter_t, wu_event.link);
369 list_remove(&wdp->wu_event.link);
370 wdp->wu_event.inlist = false;
371 if (!wdp->active) {
372 wdp->active = true;
373 fibril_add_ready(wdp->fid);
374 optimize_execution_power();
375 if (once)
376 break;
377 }
[9ae22ba]378 }
379 futex_up(&async_futex);
380}
381
382void fibril_condvar_signal(fibril_condvar_t *fcv)
383{
384 _fibril_condvar_wakeup_common(fcv, true);
385}
386
387void fibril_condvar_broadcast(fibril_condvar_t *fcv)
388{
389 _fibril_condvar_wakeup_common(fcv, false);
390}
391
[f3afd24]392/** @}
393 */
Note: See TracBrowser for help on using the repository browser.