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

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

TCP retransmission (WIP). Allow setting timer in timer handler.
Simulate packet drop. Fixes.

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