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

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

Hide libc-internal details of the fibril implementation.

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