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

Last change on this file since eec201d was 09ab0a9a, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Fix vertical spacing with new Ccheck revision.

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