source: mainline/uspace/lib/c/generic/fibril_synch.c@ 8610c2c

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 8610c2c was 39026d7c, checked in by Jiri Svoboda <jiri@…>, 8 years ago

Replace fibril_usleep() with async_usleep().

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