source: mainline/uspace/lib/c/generic/fibril_synch.c@ 2965d18

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 2965d18 was 2965d18, checked in by Jiří Zárevúcky <jiri.zarevucky@…>, 7 years ago

Add debug counter for rmutex locks.

  • Property mode set to 100644
File size: 16.3 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 <adt/list.h>
39#include <futex.h>
40#include <sys/time.h>
41#include <errno.h>
42#include <assert.h>
43#include <stacktrace.h>
44#include <stdlib.h>
45#include <stdio.h>
46#include <io/kio.h>
47#include <mem.h>
48#include <context.h>
49
50#include "private/async.h"
51#include "private/fibril.h"
52
53void fibril_rmutex_initialize(fibril_rmutex_t *m)
54{
55 futex_initialize(&m->futex, 1);
56}
57
58/**
59 * Lock restricted mutex.
60 * When a restricted mutex is locked, the fibril may not sleep or create new
61 * threads. Any attempt to do so will abort the program.
62 */
63void fibril_rmutex_lock(fibril_rmutex_t *m)
64{
65 futex_lock(&m->futex);
66 fibril_self()->rmutex_locks++;
67}
68
69bool fibril_rmutex_trylock(fibril_rmutex_t *m)
70{
71 if (futex_trylock(&m->futex)) {
72 fibril_self()->rmutex_locks++;
73 return true;
74 } else {
75 return false;
76 }
77}
78
79void fibril_rmutex_unlock(fibril_rmutex_t *m)
80{
81 fibril_self()->rmutex_locks--;
82 futex_unlock(&m->futex);
83}
84
85static fibril_local bool deadlocked = false;
86
87static futex_t fibril_synch_futex = FUTEX_INITIALIZER;
88
89typedef struct {
90 link_t link;
91 fibril_event_t event;
92 fibril_mutex_t *mutex;
93 fid_t fid;
94} awaiter_t;
95
96#define AWAITER_INIT { .fid = fibril_get_id() }
97
98static void print_deadlock(fibril_owner_info_t *oi)
99{
100 // FIXME: Print to stderr.
101
102 fibril_t *f = (fibril_t *) fibril_get_id();
103
104 if (deadlocked) {
105 kio_printf("Deadlock detected while printing deadlock. Aborting.\n");
106 abort();
107 }
108 deadlocked = true;
109
110 printf("Deadlock detected.\n");
111 stacktrace_print();
112
113 printf("Fibril %p waits for primitive %p.\n", f, oi);
114
115 while (oi && oi->owned_by) {
116 printf("Primitive %p is owned by fibril %p.\n",
117 oi, oi->owned_by);
118 if (oi->owned_by == f)
119 break;
120 stacktrace_print_fp_pc(
121 context_get_fp(&oi->owned_by->ctx),
122 context_get_pc(&oi->owned_by->ctx));
123 printf("Fibril %p waits for primitive %p.\n",
124 oi->owned_by, oi->owned_by->waits_for);
125 oi = oi->owned_by->waits_for;
126 }
127}
128
129
130static void check_fibril_for_deadlock(fibril_owner_info_t *oi, fibril_t *fib)
131{
132 futex_assert_is_locked(&fibril_synch_futex);
133
134 while (oi && oi->owned_by) {
135 if (oi->owned_by == fib) {
136 futex_unlock(&fibril_synch_futex);
137 print_deadlock(oi);
138 abort();
139 }
140 oi = oi->owned_by->waits_for;
141 }
142}
143
144static void check_for_deadlock(fibril_owner_info_t *oi)
145{
146 check_fibril_for_deadlock(oi, fibril_self());
147}
148
149void fibril_mutex_initialize(fibril_mutex_t *fm)
150{
151 fm->oi.owned_by = NULL;
152 fm->counter = 1;
153 list_initialize(&fm->waiters);
154}
155
156void fibril_mutex_lock(fibril_mutex_t *fm)
157{
158 fibril_t *f = (fibril_t *) fibril_get_id();
159
160 futex_lock(&fibril_synch_futex);
161
162 if (fm->counter-- > 0) {
163 fm->oi.owned_by = f;
164 futex_unlock(&fibril_synch_futex);
165 return;
166 }
167
168 awaiter_t wdata = AWAITER_INIT;
169 list_append(&wdata.link, &fm->waiters);
170 check_for_deadlock(&fm->oi);
171 f->waits_for = &fm->oi;
172
173 futex_unlock(&fibril_synch_futex);
174
175 fibril_wait_for(&wdata.event);
176}
177
178bool fibril_mutex_trylock(fibril_mutex_t *fm)
179{
180 bool locked = false;
181
182 futex_lock(&fibril_synch_futex);
183 if (fm->counter > 0) {
184 fm->counter--;
185 fm->oi.owned_by = (fibril_t *) fibril_get_id();
186 locked = true;
187 }
188 futex_unlock(&fibril_synch_futex);
189
190 return locked;
191}
192
193static void _fibril_mutex_unlock_unsafe(fibril_mutex_t *fm)
194{
195 assert(fm->oi.owned_by == (fibril_t *) fibril_get_id());
196
197 if (fm->counter++ < 0) {
198 awaiter_t *wdp = list_pop(&fm->waiters, awaiter_t, link);
199 assert(wdp);
200
201 fibril_t *f = (fibril_t *) wdp->fid;
202 fm->oi.owned_by = f;
203 f->waits_for = NULL;
204
205 fibril_notify(&wdp->event);
206 } else {
207 fm->oi.owned_by = NULL;
208 }
209}
210
211void fibril_mutex_unlock(fibril_mutex_t *fm)
212{
213 futex_lock(&fibril_synch_futex);
214 _fibril_mutex_unlock_unsafe(fm);
215 futex_unlock(&fibril_synch_futex);
216}
217
218bool fibril_mutex_is_locked(fibril_mutex_t *fm)
219{
220 futex_lock(&fibril_synch_futex);
221 bool locked = (fm->oi.owned_by == (fibril_t *) fibril_get_id());
222 futex_unlock(&fibril_synch_futex);
223 return locked;
224}
225
226void fibril_rwlock_initialize(fibril_rwlock_t *frw)
227{
228 frw->oi.owned_by = NULL;
229 frw->writers = 0;
230 frw->readers = 0;
231 list_initialize(&frw->waiters);
232}
233
234void fibril_rwlock_read_lock(fibril_rwlock_t *frw)
235{
236 fibril_t *f = (fibril_t *) fibril_get_id();
237
238 futex_lock(&fibril_synch_futex);
239
240 if (!frw->writers) {
241 /* Consider the first reader the owner. */
242 if (frw->readers++ == 0)
243 frw->oi.owned_by = f;
244 futex_unlock(&fibril_synch_futex);
245 return;
246 }
247
248 f->is_writer = false;
249
250 awaiter_t wdata = AWAITER_INIT;
251 list_append(&wdata.link, &frw->waiters);
252 check_for_deadlock(&frw->oi);
253 f->waits_for = &frw->oi;
254
255 futex_unlock(&fibril_synch_futex);
256
257 fibril_wait_for(&wdata.event);
258}
259
260void fibril_rwlock_write_lock(fibril_rwlock_t *frw)
261{
262 fibril_t *f = (fibril_t *) fibril_get_id();
263
264 futex_lock(&fibril_synch_futex);
265
266 if (!frw->writers && !frw->readers) {
267 frw->oi.owned_by = f;
268 frw->writers++;
269 futex_unlock(&fibril_synch_futex);
270 return;
271 }
272
273 f->is_writer = true;
274
275 awaiter_t wdata = AWAITER_INIT;
276 list_append(&wdata.link, &frw->waiters);
277 check_for_deadlock(&frw->oi);
278 f->waits_for = &frw->oi;
279
280 futex_unlock(&fibril_synch_futex);
281
282 fibril_wait_for(&wdata.event);
283}
284
285static void _fibril_rwlock_common_unlock(fibril_rwlock_t *frw)
286{
287 if (frw->readers) {
288 if (--frw->readers) {
289 if (frw->oi.owned_by == (fibril_t *) fibril_get_id()) {
290 /*
291 * If this reader fibril was considered the
292 * owner of this rwlock, clear the ownership
293 * information even if there are still more
294 * readers.
295 *
296 * This is the limitation of the detection
297 * mechanism rooted in the fact that tracking
298 * all readers would require dynamically
299 * allocated memory for keeping linkage info.
300 */
301 frw->oi.owned_by = NULL;
302 }
303
304 return;
305 }
306 } else {
307 frw->writers--;
308 }
309
310 assert(!frw->readers && !frw->writers);
311
312 frw->oi.owned_by = NULL;
313
314 while (!list_empty(&frw->waiters)) {
315 link_t *tmp = list_first(&frw->waiters);
316 awaiter_t *wdp;
317 fibril_t *f;
318
319 wdp = list_get_instance(tmp, awaiter_t, link);
320 f = (fibril_t *) wdp->fid;
321
322 if (f->is_writer) {
323 if (frw->readers)
324 break;
325 frw->writers++;
326 } else {
327 frw->readers++;
328 }
329
330 f->waits_for = NULL;
331 list_remove(&wdp->link);
332 frw->oi.owned_by = f;
333 fibril_notify(&wdp->event);
334
335 if (frw->writers)
336 break;
337 }
338}
339
340void fibril_rwlock_read_unlock(fibril_rwlock_t *frw)
341{
342 futex_lock(&fibril_synch_futex);
343 assert(frw->readers > 0);
344 _fibril_rwlock_common_unlock(frw);
345 futex_unlock(&fibril_synch_futex);
346}
347
348void fibril_rwlock_write_unlock(fibril_rwlock_t *frw)
349{
350 futex_lock(&fibril_synch_futex);
351 assert(frw->writers == 1);
352 assert(frw->oi.owned_by == fibril_self());
353 _fibril_rwlock_common_unlock(frw);
354 futex_unlock(&fibril_synch_futex);
355}
356
357bool fibril_rwlock_is_read_locked(fibril_rwlock_t *frw)
358{
359 futex_lock(&fibril_synch_futex);
360 bool locked = (frw->readers > 0);
361 futex_unlock(&fibril_synch_futex);
362 return locked;
363}
364
365bool fibril_rwlock_is_write_locked(fibril_rwlock_t *frw)
366{
367 futex_lock(&fibril_synch_futex);
368 assert(frw->writers <= 1);
369 bool locked = (frw->writers > 0) && (frw->oi.owned_by == fibril_self());
370 futex_unlock(&fibril_synch_futex);
371 return locked;
372}
373
374bool fibril_rwlock_is_locked(fibril_rwlock_t *frw)
375{
376 return fibril_rwlock_is_read_locked(frw) ||
377 fibril_rwlock_is_write_locked(frw);
378}
379
380void fibril_condvar_initialize(fibril_condvar_t *fcv)
381{
382 list_initialize(&fcv->waiters);
383}
384
385/**
386 * FIXME: If `timeout` is negative, the function returns ETIMEOUT immediately,
387 * and if `timeout` is 0, the wait never times out.
388 * This is not consistent with other similar APIs.
389 */
390errno_t
391fibril_condvar_wait_timeout(fibril_condvar_t *fcv, fibril_mutex_t *fm,
392 suseconds_t timeout)
393{
394 assert(fibril_mutex_is_locked(fm));
395
396 if (timeout < 0)
397 return ETIMEOUT;
398
399 awaiter_t wdata = AWAITER_INIT;
400 wdata.mutex = fm;
401
402 struct timeval tv;
403 struct timeval *expires = NULL;
404 if (timeout) {
405 getuptime(&tv);
406 tv_add_diff(&tv, timeout);
407 expires = &tv;
408 }
409
410 futex_lock(&fibril_synch_futex);
411 _fibril_mutex_unlock_unsafe(fm);
412 list_append(&wdata.link, &fcv->waiters);
413 futex_unlock(&fibril_synch_futex);
414
415 (void) fibril_wait_timeout(&wdata.event, expires);
416
417 futex_lock(&fibril_synch_futex);
418 bool timed_out = link_in_use(&wdata.link);
419 list_remove(&wdata.link);
420 futex_unlock(&fibril_synch_futex);
421
422 fibril_mutex_lock(fm);
423
424 return timed_out ? ETIMEOUT : EOK;
425}
426
427void fibril_condvar_wait(fibril_condvar_t *fcv, fibril_mutex_t *fm)
428{
429 (void) fibril_condvar_wait_timeout(fcv, fm, 0);
430}
431
432void fibril_condvar_signal(fibril_condvar_t *fcv)
433{
434 futex_lock(&fibril_synch_futex);
435
436 awaiter_t *w = list_pop(&fcv->waiters, awaiter_t, link);
437 if (w != NULL)
438 fibril_notify(&w->event);
439
440 futex_unlock(&fibril_synch_futex);
441}
442
443void fibril_condvar_broadcast(fibril_condvar_t *fcv)
444{
445 futex_lock(&fibril_synch_futex);
446
447 awaiter_t *w;
448 while ((w = list_pop(&fcv->waiters, awaiter_t, link)))
449 fibril_notify(&w->event);
450
451 futex_unlock(&fibril_synch_futex);
452}
453
454/** Timer fibril.
455 *
456 * @param arg Timer
457 */
458static errno_t fibril_timer_func(void *arg)
459{
460 fibril_timer_t *timer = (fibril_timer_t *) arg;
461 errno_t rc;
462
463 fibril_mutex_lock(timer->lockp);
464
465 while (timer->state != fts_cleanup) {
466 switch (timer->state) {
467 case fts_not_set:
468 case fts_fired:
469 fibril_condvar_wait(&timer->cv, timer->lockp);
470 break;
471 case fts_active:
472 rc = fibril_condvar_wait_timeout(&timer->cv,
473 timer->lockp, timer->delay);
474 if (rc == ETIMEOUT && timer->state == fts_active) {
475 timer->state = fts_fired;
476 timer->handler_fid = fibril_get_id();
477 fibril_mutex_unlock(timer->lockp);
478 timer->fun(timer->arg);
479 fibril_mutex_lock(timer->lockp);
480 timer->handler_fid = 0;
481 }
482 break;
483 case fts_cleanup:
484 case fts_clean:
485 assert(false);
486 break;
487 }
488 }
489
490 /* Acknowledge timer fibril has finished cleanup. */
491 timer->state = fts_clean;
492 fibril_condvar_broadcast(&timer->cv);
493 fibril_mutex_unlock(timer->lockp);
494
495 return 0;
496}
497
498/** Create new timer.
499 *
500 * @return New timer on success, @c NULL if out of memory.
501 */
502fibril_timer_t *fibril_timer_create(fibril_mutex_t *lock)
503{
504 fid_t fid;
505 fibril_timer_t *timer;
506
507 timer = calloc(1, sizeof(fibril_timer_t));
508 if (timer == NULL)
509 return NULL;
510
511 fid = fibril_create(fibril_timer_func, (void *) timer);
512 if (fid == 0) {
513 free(timer);
514 return NULL;
515 }
516
517 fibril_mutex_initialize(&timer->lock);
518 fibril_condvar_initialize(&timer->cv);
519
520 timer->fibril = fid;
521 timer->state = fts_not_set;
522 timer->lockp = (lock != NULL) ? lock : &timer->lock;
523
524 fibril_add_ready(fid);
525 return timer;
526}
527
528/** Destroy timer.
529 *
530 * @param timer Timer, must not be active or accessed by other threads.
531 */
532void fibril_timer_destroy(fibril_timer_t *timer)
533{
534 fibril_mutex_lock(timer->lockp);
535 assert(timer->state == fts_not_set || timer->state == fts_fired);
536
537 /* Request timer fibril to terminate. */
538 timer->state = fts_cleanup;
539 fibril_condvar_broadcast(&timer->cv);
540
541 /* Wait for timer fibril to terminate */
542 while (timer->state != fts_clean)
543 fibril_condvar_wait(&timer->cv, timer->lockp);
544 fibril_mutex_unlock(timer->lockp);
545
546 free(timer);
547}
548
549/** Set timer.
550 *
551 * Set timer to execute a callback function after the specified
552 * interval.
553 *
554 * @param timer Timer
555 * @param delay Delay in microseconds
556 * @param fun Callback function
557 * @param arg Argument for @a fun
558 */
559void fibril_timer_set(fibril_timer_t *timer, suseconds_t delay,
560 fibril_timer_fun_t fun, void *arg)
561{
562 fibril_mutex_lock(timer->lockp);
563 fibril_timer_set_locked(timer, delay, fun, arg);
564 fibril_mutex_unlock(timer->lockp);
565}
566
567/** Set locked timer.
568 *
569 * Set timer to execute a callback function after the specified
570 * interval. Must be called when the timer is locked.
571 *
572 * @param timer Timer
573 * @param delay Delay in microseconds
574 * @param fun Callback function
575 * @param arg Argument for @a fun
576 */
577void fibril_timer_set_locked(fibril_timer_t *timer, suseconds_t delay,
578 fibril_timer_fun_t fun, void *arg)
579{
580 assert(fibril_mutex_is_locked(timer->lockp));
581 assert(timer->state == fts_not_set || timer->state == fts_fired);
582 timer->state = fts_active;
583 timer->delay = delay;
584 timer->fun = fun;
585 timer->arg = arg;
586 fibril_condvar_broadcast(&timer->cv);
587}
588
589/** Clear timer.
590 *
591 * Clears (cancels) timer and returns last state of the timer.
592 * This can be one of:
593 * - fts_not_set If the timer has not been set or has been cleared
594 * - fts_active Timer was set but did not fire
595 * - fts_fired Timer fired
596 *
597 * @param timer Timer
598 * @return Last timer state
599 */
600fibril_timer_state_t fibril_timer_clear(fibril_timer_t *timer)
601{
602 fibril_timer_state_t old_state;
603
604 fibril_mutex_lock(timer->lockp);
605 old_state = fibril_timer_clear_locked(timer);
606 fibril_mutex_unlock(timer->lockp);
607
608 return old_state;
609}
610
611/** Clear locked timer.
612 *
613 * Clears (cancels) timer and returns last state of the timer.
614 * This can be one of:
615 * - fts_not_set If the timer has not been set or has been cleared
616 * - fts_active Timer was set but did not fire
617 * - fts_fired Timer fired
618 * Must be called when the timer is locked.
619 *
620 * @param timer Timer
621 * @return Last timer state
622 */
623fibril_timer_state_t fibril_timer_clear_locked(fibril_timer_t *timer)
624{
625 fibril_timer_state_t old_state;
626
627 assert(fibril_mutex_is_locked(timer->lockp));
628
629 while (timer->handler_fid != 0) {
630 if (timer->handler_fid == fibril_get_id()) {
631 printf("Deadlock detected.\n");
632 stacktrace_print();
633 printf("Fibril %p is trying to clear timer %p from "
634 "inside its handler %p.\n",
635 fibril_get_id(), timer, timer->fun);
636 abort();
637 }
638
639 fibril_condvar_wait(&timer->cv, timer->lockp);
640 }
641
642 old_state = timer->state;
643 timer->state = fts_not_set;
644
645 timer->delay = 0;
646 timer->fun = NULL;
647 timer->arg = NULL;
648 fibril_condvar_broadcast(&timer->cv);
649
650 return old_state;
651}
652
653/**
654 * Initialize a semaphore with initial count set to the provided value.
655 *
656 * @param sem Semaphore to initialize.
657 * @param count Initial count. Must not be negative.
658 */
659void fibril_semaphore_initialize(fibril_semaphore_t *sem, long count)
660{
661 /*
662 * Negative count denotes the length of waitlist,
663 * so it makes no sense as an initial value.
664 */
665 assert(count >= 0);
666 sem->count = count;
667 list_initialize(&sem->waiters);
668}
669
670/**
671 * Produce one token.
672 * If there are fibrils waiting for tokens, this operation satisfies
673 * exactly one waiting `fibril_semaphore_down()`.
674 * This operation never blocks the fibril.
675 *
676 * @param sem Semaphore to use.
677 */
678void fibril_semaphore_up(fibril_semaphore_t *sem)
679{
680 futex_lock(&fibril_synch_futex);
681 sem->count++;
682
683 if (sem->count <= 0) {
684 awaiter_t *w = list_pop(&sem->waiters, awaiter_t, link);
685 assert(w);
686 fibril_notify(&w->event);
687 }
688
689 futex_unlock(&fibril_synch_futex);
690}
691
692/**
693 * Consume one token.
694 * If there are no available tokens (count <= 0), this operation blocks until
695 * another fibril produces a token using `fibril_semaphore_up()`.
696 *
697 * @param sem Semaphore to use.
698 */
699void fibril_semaphore_down(fibril_semaphore_t *sem)
700{
701 futex_lock(&fibril_synch_futex);
702 sem->count--;
703
704 if (sem->count >= 0) {
705 futex_unlock(&fibril_synch_futex);
706 return;
707 }
708
709 awaiter_t wdata = AWAITER_INIT;
710 list_append(&wdata.link, &sem->waiters);
711
712 futex_unlock(&fibril_synch_futex);
713
714 fibril_wait_for(&wdata.event);
715}
716
717/** @}
718 */
Note: See TracBrowser for help on using the repository browser.