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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since c721d26 was 5a5b087, checked in by Jiri Svoboda <jiri@…>, 10 years ago

Cannot free timer memory from timer fibril. Since timer→lockp can point to user-provided lock which may not exist beyond return from fibril_timer_destroy(), fibril_timer_destroy() must wait for timer fibril to terminate.

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