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

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

Detect when printf() printing deadlock deadlocks.

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