source: mainline/uspace/lib/c/generic/fibril_synch.c@ 1fa010c

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1fa010c was 1fa010c, checked in by Jakub Jermar <jakub@…>, 15 years ago

Implement simple deadlock detection for fibril mutexes.

The mechanism could be extended to rwlocks for the writer and first reader
cases. The current implementation will only build on ia32. In order to fix it,
we need a generic way to tell the frame pointer given fibril's ctx.

  • Property mode set to 100644
File size: 8.6 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 <async_priv.h>
39#include <adt/list.h>
40#include <futex.h>
41#include <sys/time.h>
42#include <errno.h>
43#include <assert.h>
44#include <stacktrace.h>
45#include <stdlib.h>
46
47static void optimize_execution_power(void)
48{
49 /*
50 * When waking up a worker fibril previously blocked in fibril
51 * synchronization, chances are that there is an idle manager fibril
52 * waiting for IPC, that could start executing the awakened worker
53 * fibril right away. We try to detect this and bring the manager
54 * fibril back to fruitful work.
55 */
56 if (atomic_get(&threads_in_ipc_wait) > 0)
57 ipc_poke();
58}
59
60static bool check_for_deadlock(fibril_owner_info_t *oi)
61{
62 while (oi && oi->owned_by) {
63 if (oi->owned_by == (fibril_t *) fibril_get_id())
64 return true;
65 oi = oi->owned_by->waits_for;
66 }
67
68 return false;
69}
70
71static void print_deadlock(fibril_owner_info_t *oi)
72{
73 fibril_t *f = (fibril_t *) fibril_get_id();
74
75 printf("Deadlock detected: ");
76
77 printf("Fibril %p waits for primitive %p.\n", f, oi);
78 stacktrace_print();
79
80 while (oi && oi->owned_by) {
81 printf(". ");
82 printf("Primitive %p is owned by fibril %p.\n",
83 oi, oi->owned_by);
84 stacktrace_print_fp_pc(oi->owned_by->ctx.ebp,
85 oi->owned_by->ctx.pc);
86 if (oi->owned_by == f)
87 break;
88 printf("Fibril %p waits for primitive %p.\n",
89 oi->owned_by, oi->owned_by->waits_for);
90 oi = oi->owned_by->waits_for;
91 }
92
93 abort();
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 futex_down(&async_futex);
108 if (fm->counter-- <= 0) {
109 awaiter_t wdata;
110
111 wdata.fid = fibril_get_id();
112 wdata.active = false;
113 wdata.wu_event.inlist = true;
114 link_initialize(&wdata.wu_event.link);
115 list_append(&wdata.wu_event.link, &fm->waiters);
116
117 if (check_for_deadlock(&fm->oi))
118 print_deadlock(&fm->oi);
119 f->waits_for = &fm->oi;
120
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 assert(fm->counter <= 0);
146 if (fm->counter++ < 0) {
147 link_t *tmp;
148 awaiter_t *wdp;
149 fibril_t *f;
150
151 assert(!list_empty(&fm->waiters));
152 tmp = fm->waiters.next;
153 wdp = list_get_instance(tmp, awaiter_t, wu_event.link);
154 wdp->active = true;
155 wdp->wu_event.inlist = false;
156
157 f = (fibril_t *) wdp->fid;
158 fm->oi.owned_by = f;
159 f->waits_for = NULL;
160
161 list_remove(&wdp->wu_event.link);
162 fibril_add_ready(wdp->fid);
163 optimize_execution_power();
164 } else {
165 fm->oi.owned_by = NULL;
166 }
167}
168
169void fibril_mutex_unlock(fibril_mutex_t *fm)
170{
171 futex_down(&async_futex);
172 _fibril_mutex_unlock_unsafe(fm);
173 futex_up(&async_futex);
174}
175
176void fibril_rwlock_initialize(fibril_rwlock_t *frw)
177{
178 frw->oi.owned_by = NULL;
179 frw->writers = 0;
180 frw->readers = 0;
181 list_initialize(&frw->waiters);
182}
183
184void fibril_rwlock_read_lock(fibril_rwlock_t *frw)
185{
186 futex_down(&async_futex);
187 if (frw->writers) {
188 fibril_t *f = (fibril_t *) fibril_get_id();
189 awaiter_t wdata;
190
191 wdata.fid = (fid_t) f;
192 wdata.active = false;
193 wdata.wu_event.inlist = true;
194 link_initialize(&wdata.wu_event.link);
195 f->flags &= ~FIBRIL_WRITER;
196 list_append(&wdata.wu_event.link, &frw->waiters);
197 fibril_switch(FIBRIL_TO_MANAGER);
198 } else {
199 frw->readers++;
200 futex_up(&async_futex);
201 }
202}
203
204void fibril_rwlock_write_lock(fibril_rwlock_t *frw)
205{
206 futex_down(&async_futex);
207 if (frw->writers || frw->readers) {
208 fibril_t *f = (fibril_t *) fibril_get_id();
209 awaiter_t wdata;
210
211 wdata.fid = (fid_t) f;
212 wdata.active = false;
213 wdata.wu_event.inlist = true;
214 link_initialize(&wdata.wu_event.link);
215 f->flags |= FIBRIL_WRITER;
216 list_append(&wdata.wu_event.link, &frw->waiters);
217 fibril_switch(FIBRIL_TO_MANAGER);
218 } else {
219 frw->writers++;
220 futex_up(&async_futex);
221 }
222}
223
224static void _fibril_rwlock_common_unlock(fibril_rwlock_t *frw)
225{
226 futex_down(&async_futex);
227 assert(frw->readers || (frw->writers == 1));
228 if (frw->readers) {
229 if (--frw->readers)
230 goto out;
231 } else {
232 frw->writers--;
233 }
234
235 assert(!frw->readers && !frw->writers);
236
237 while (!list_empty(&frw->waiters)) {
238 link_t *tmp = frw->waiters.next;
239 awaiter_t *wdp;
240 fibril_t *f;
241
242 wdp = list_get_instance(tmp, awaiter_t, wu_event.link);
243 f = (fibril_t *) wdp->fid;
244
245 if (f->flags & FIBRIL_WRITER) {
246 if (frw->readers)
247 break;
248 wdp->active = true;
249 wdp->wu_event.inlist = false;
250 list_remove(&wdp->wu_event.link);
251 fibril_add_ready(wdp->fid);
252 frw->writers++;
253 optimize_execution_power();
254 break;
255 } else {
256 wdp->active = true;
257 wdp->wu_event.inlist = false;
258 list_remove(&wdp->wu_event.link);
259 fibril_add_ready(wdp->fid);
260 frw->readers++;
261 optimize_execution_power();
262 }
263 }
264out:
265 futex_up(&async_futex);
266}
267
268void fibril_rwlock_read_unlock(fibril_rwlock_t *frw)
269{
270 _fibril_rwlock_common_unlock(frw);
271}
272
273void fibril_rwlock_write_unlock(fibril_rwlock_t *frw)
274{
275 _fibril_rwlock_common_unlock(frw);
276}
277
278void fibril_condvar_initialize(fibril_condvar_t *fcv)
279{
280 list_initialize(&fcv->waiters);
281}
282
283int
284fibril_condvar_wait_timeout(fibril_condvar_t *fcv, fibril_mutex_t *fm,
285 suseconds_t timeout)
286{
287 awaiter_t wdata;
288
289 if (timeout < 0)
290 return ETIMEOUT;
291
292 wdata.fid = fibril_get_id();
293 wdata.active = false;
294
295 wdata.to_event.inlist = timeout > 0;
296 wdata.to_event.occurred = false;
297 link_initialize(&wdata.to_event.link);
298
299 wdata.wu_event.inlist = true;
300 link_initialize(&wdata.wu_event.link);
301
302 futex_down(&async_futex);
303 if (timeout) {
304 gettimeofday(&wdata.to_event.expires, NULL);
305 tv_add(&wdata.to_event.expires, timeout);
306 async_insert_timeout(&wdata);
307 }
308 list_append(&wdata.wu_event.link, &fcv->waiters);
309 _fibril_mutex_unlock_unsafe(fm);
310 fibril_switch(FIBRIL_TO_MANAGER);
311 fibril_mutex_lock(fm);
312
313 /* async_futex not held after fibril_switch() */
314 futex_down(&async_futex);
315 if (wdata.to_event.inlist)
316 list_remove(&wdata.to_event.link);
317 if (wdata.wu_event.inlist)
318 list_remove(&wdata.wu_event.link);
319 futex_up(&async_futex);
320
321 return wdata.to_event.occurred ? ETIMEOUT : EOK;
322}
323
324void fibril_condvar_wait(fibril_condvar_t *fcv, fibril_mutex_t *fm)
325{
326 int rc;
327
328 rc = fibril_condvar_wait_timeout(fcv, fm, 0);
329 assert(rc == EOK);
330}
331
332static void _fibril_condvar_wakeup_common(fibril_condvar_t *fcv, bool once)
333{
334 link_t *tmp;
335 awaiter_t *wdp;
336
337 futex_down(&async_futex);
338 while (!list_empty(&fcv->waiters)) {
339 tmp = fcv->waiters.next;
340 wdp = list_get_instance(tmp, awaiter_t, wu_event.link);
341 list_remove(&wdp->wu_event.link);
342 wdp->wu_event.inlist = false;
343 if (!wdp->active) {
344 wdp->active = true;
345 fibril_add_ready(wdp->fid);
346 optimize_execution_power();
347 if (once)
348 break;
349 }
350 }
351 futex_up(&async_futex);
352}
353
354void fibril_condvar_signal(fibril_condvar_t *fcv)
355{
356 _fibril_condvar_wakeup_common(fcv, true);
357}
358
359void fibril_condvar_broadcast(fibril_condvar_t *fcv)
360{
361 _fibril_condvar_wakeup_common(fcv, false);
362}
363
364/** @}
365 */
Note: See TracBrowser for help on using the repository browser.