source: mainline/uspace/lib/c/generic/fibril_synch.c@ 3e20fd48

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

Record the ownership tracking info for fibril mutexes.

  • Property mode set to 100644
File size: 7.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
45static void optimize_execution_power(void)
46{
47 /*
48 * When waking up a worker fibril previously blocked in fibril
49 * synchronization, chances are that there is an idle manager fibril
50 * waiting for IPC, that could start executing the awakened worker
51 * fibril right away. We try to detect this and bring the manager
52 * fibril back to fruitful work.
53 */
54 if (atomic_get(&threads_in_ipc_wait) > 0)
55 ipc_poke();
56}
57
58void fibril_mutex_initialize(fibril_mutex_t *fm)
59{
60 fm->oi.owned_by = NULL;
61 fm->counter = 1;
62 list_initialize(&fm->waiters);
63}
64
65void fibril_mutex_lock(fibril_mutex_t *fm)
66{
67 futex_down(&async_futex);
68 if (fm->counter-- <= 0) {
69 awaiter_t wdata;
70
71 wdata.fid = fibril_get_id();
72 wdata.active = false;
73 wdata.wu_event.inlist = true;
74 link_initialize(&wdata.wu_event.link);
75 list_append(&wdata.wu_event.link, &fm->waiters);
76 fibril_switch(FIBRIL_TO_MANAGER);
77 } else {
78 fm->oi.owned_by = (fibril_t *) fibril_get_id();
79 futex_up(&async_futex);
80 }
81}
82
83bool fibril_mutex_trylock(fibril_mutex_t *fm)
84{
85 bool locked = false;
86
87 futex_down(&async_futex);
88 if (fm->counter > 0) {
89 fm->counter--;
90 fm->oi.owned_by = (fibril_t *) fibril_get_id();
91 locked = true;
92 }
93 futex_up(&async_futex);
94
95 return locked;
96}
97
98static void _fibril_mutex_unlock_unsafe(fibril_mutex_t *fm)
99{
100 assert(fm->counter <= 0);
101 if (fm->counter++ < 0) {
102 link_t *tmp;
103 awaiter_t *wdp;
104
105 assert(!list_empty(&fm->waiters));
106 tmp = fm->waiters.next;
107 wdp = list_get_instance(tmp, awaiter_t, wu_event.link);
108 wdp->active = true;
109 wdp->wu_event.inlist = false;
110 fm->oi.owned_by = (fibril_t *) wdp->fid;
111 list_remove(&wdp->wu_event.link);
112 fibril_add_ready(wdp->fid);
113 optimize_execution_power();
114 } else {
115 fm->oi.owned_by = NULL;
116 }
117}
118
119void fibril_mutex_unlock(fibril_mutex_t *fm)
120{
121 futex_down(&async_futex);
122 _fibril_mutex_unlock_unsafe(fm);
123 futex_up(&async_futex);
124}
125
126void fibril_rwlock_initialize(fibril_rwlock_t *frw)
127{
128 frw->oi.owned_by = NULL;
129 frw->writers = 0;
130 frw->readers = 0;
131 list_initialize(&frw->waiters);
132}
133
134void fibril_rwlock_read_lock(fibril_rwlock_t *frw)
135{
136 futex_down(&async_futex);
137 if (frw->writers) {
138 fibril_t *f = (fibril_t *) fibril_get_id();
139 awaiter_t wdata;
140
141 wdata.fid = (fid_t) f;
142 wdata.active = false;
143 wdata.wu_event.inlist = true;
144 link_initialize(&wdata.wu_event.link);
145 f->flags &= ~FIBRIL_WRITER;
146 list_append(&wdata.wu_event.link, &frw->waiters);
147 fibril_switch(FIBRIL_TO_MANAGER);
148 } else {
149 frw->readers++;
150 futex_up(&async_futex);
151 }
152}
153
154void fibril_rwlock_write_lock(fibril_rwlock_t *frw)
155{
156 futex_down(&async_futex);
157 if (frw->writers || frw->readers) {
158 fibril_t *f = (fibril_t *) fibril_get_id();
159 awaiter_t wdata;
160
161 wdata.fid = (fid_t) f;
162 wdata.active = false;
163 wdata.wu_event.inlist = true;
164 link_initialize(&wdata.wu_event.link);
165 f->flags |= FIBRIL_WRITER;
166 list_append(&wdata.wu_event.link, &frw->waiters);
167 fibril_switch(FIBRIL_TO_MANAGER);
168 } else {
169 frw->writers++;
170 futex_up(&async_futex);
171 }
172}
173
174static void _fibril_rwlock_common_unlock(fibril_rwlock_t *frw)
175{
176 futex_down(&async_futex);
177 assert(frw->readers || (frw->writers == 1));
178 if (frw->readers) {
179 if (--frw->readers)
180 goto out;
181 } else {
182 frw->writers--;
183 }
184
185 assert(!frw->readers && !frw->writers);
186
187 while (!list_empty(&frw->waiters)) {
188 link_t *tmp = frw->waiters.next;
189 awaiter_t *wdp;
190 fibril_t *f;
191
192 wdp = list_get_instance(tmp, awaiter_t, wu_event.link);
193 f = (fibril_t *) wdp->fid;
194
195 if (f->flags & FIBRIL_WRITER) {
196 if (frw->readers)
197 break;
198 wdp->active = true;
199 wdp->wu_event.inlist = false;
200 list_remove(&wdp->wu_event.link);
201 fibril_add_ready(wdp->fid);
202 frw->writers++;
203 optimize_execution_power();
204 break;
205 } else {
206 wdp->active = true;
207 wdp->wu_event.inlist = false;
208 list_remove(&wdp->wu_event.link);
209 fibril_add_ready(wdp->fid);
210 frw->readers++;
211 optimize_execution_power();
212 }
213 }
214out:
215 futex_up(&async_futex);
216}
217
218void fibril_rwlock_read_unlock(fibril_rwlock_t *frw)
219{
220 _fibril_rwlock_common_unlock(frw);
221}
222
223void fibril_rwlock_write_unlock(fibril_rwlock_t *frw)
224{
225 _fibril_rwlock_common_unlock(frw);
226}
227
228void fibril_condvar_initialize(fibril_condvar_t *fcv)
229{
230 list_initialize(&fcv->waiters);
231}
232
233int
234fibril_condvar_wait_timeout(fibril_condvar_t *fcv, fibril_mutex_t *fm,
235 suseconds_t timeout)
236{
237 awaiter_t wdata;
238
239 if (timeout < 0)
240 return ETIMEOUT;
241
242 wdata.fid = fibril_get_id();
243 wdata.active = false;
244
245 wdata.to_event.inlist = timeout > 0;
246 wdata.to_event.occurred = false;
247 link_initialize(&wdata.to_event.link);
248
249 wdata.wu_event.inlist = true;
250 link_initialize(&wdata.wu_event.link);
251
252 futex_down(&async_futex);
253 if (timeout) {
254 gettimeofday(&wdata.to_event.expires, NULL);
255 tv_add(&wdata.to_event.expires, timeout);
256 async_insert_timeout(&wdata);
257 }
258 list_append(&wdata.wu_event.link, &fcv->waiters);
259 _fibril_mutex_unlock_unsafe(fm);
260 fibril_switch(FIBRIL_TO_MANAGER);
261 fibril_mutex_lock(fm);
262
263 /* async_futex not held after fibril_switch() */
264 futex_down(&async_futex);
265 if (wdata.to_event.inlist)
266 list_remove(&wdata.to_event.link);
267 if (wdata.wu_event.inlist)
268 list_remove(&wdata.wu_event.link);
269 futex_up(&async_futex);
270
271 return wdata.to_event.occurred ? ETIMEOUT : EOK;
272}
273
274void fibril_condvar_wait(fibril_condvar_t *fcv, fibril_mutex_t *fm)
275{
276 int rc;
277
278 rc = fibril_condvar_wait_timeout(fcv, fm, 0);
279 assert(rc == EOK);
280}
281
282static void _fibril_condvar_wakeup_common(fibril_condvar_t *fcv, bool once)
283{
284 link_t *tmp;
285 awaiter_t *wdp;
286
287 futex_down(&async_futex);
288 while (!list_empty(&fcv->waiters)) {
289 tmp = fcv->waiters.next;
290 wdp = list_get_instance(tmp, awaiter_t, wu_event.link);
291 list_remove(&wdp->wu_event.link);
292 wdp->wu_event.inlist = false;
293 if (!wdp->active) {
294 wdp->active = true;
295 fibril_add_ready(wdp->fid);
296 optimize_execution_power();
297 if (once)
298 break;
299 }
300 }
301 futex_up(&async_futex);
302}
303
304void fibril_condvar_signal(fibril_condvar_t *fcv)
305{
306 _fibril_condvar_wakeup_common(fcv, true);
307}
308
309void fibril_condvar_broadcast(fibril_condvar_t *fcv)
310{
311 _fibril_condvar_wakeup_common(fcv, false);
312}
313
314/** @}
315 */
Note: See TracBrowser for help on using the repository browser.