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

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

C++: mutex::init should be constexpr

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