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

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

Deallocate waitq's used by the loader

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