source: mainline/uspace/lib/c/generic/fibril_synch.c@ 6e569bf

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

Simplify the interaction between async_futex and fibril_switch().

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