source: mainline/uspace/lib/c/generic/thread/fibril_synch.c@ f4cb6c5f

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

Preallocate waitq handle during initialization

Do not clutter futex_down_composable() with the preallocation of the
wait queue handle and do it single-threadedly in futex_initialize().

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