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

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

Replace remaining explicit uses of futex_t outside fibril implementation, and get rid of async_futex.

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