source: mainline/uspace/lib/libc/generic/fibril_sync.c@ cadfa8e

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

Experimental support for timeoutable fibril condition variables.

  • Property mode set to 100644
File size: 6.7 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_sync.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->counter = 1;
61 list_initialize(&fm->waiters);
62}
63
64void fibril_mutex_lock(fibril_mutex_t *fm)
65{
66 futex_down(&async_futex);
67 if (fm->counter-- <= 0) {
68 fibril_t *f = (fibril_t *) fibril_get_id();
69 list_append(&f->link, &fm->waiters);
70 fibril_switch(FIBRIL_TO_MANAGER);
71 } else {
72 futex_up(&async_futex);
73 }
74}
75
76bool fibril_mutex_trylock(fibril_mutex_t *fm)
77{
78 bool locked = false;
79
80 futex_down(&async_futex);
81 if (fm->counter > 0) {
82 fm->counter--;
83 locked = true;
84 }
85 futex_up(&async_futex);
86
87 return locked;
88}
89
90static void _fibril_mutex_unlock_unsafe(fibril_mutex_t *fm)
91{
92 assert(fm->counter <= 0);
93 if (fm->counter++ < 0) {
94 link_t *tmp;
95 fibril_t *f;
96
97 assert(!list_empty(&fm->waiters));
98 tmp = fm->waiters.next;
99 f = list_get_instance(tmp, fibril_t, link);
100 list_remove(&f->link);
101 fibril_add_ready((fid_t) f);
102 optimize_execution_power();
103 }
104}
105
106void fibril_mutex_unlock(fibril_mutex_t *fm)
107{
108 futex_down(&async_futex);
109 _fibril_mutex_unlock_unsafe(fm);
110 futex_up(&async_futex);
111}
112
113void fibril_rwlock_initialize(fibril_rwlock_t *frw)
114{
115 frw->writers = 0;
116 frw->readers = 0;
117 list_initialize(&frw->waiters);
118}
119
120void fibril_rwlock_read_lock(fibril_rwlock_t *frw)
121{
122 futex_down(&async_futex);
123 if (frw->writers) {
124 fibril_t *f = (fibril_t *) fibril_get_id();
125 f->flags &= ~FIBRIL_WRITER;
126 list_append(&f->link, &frw->waiters);
127 fibril_switch(FIBRIL_TO_MANAGER);
128 } else {
129 frw->readers++;
130 futex_up(&async_futex);
131 }
132}
133
134void fibril_rwlock_write_lock(fibril_rwlock_t *frw)
135{
136 futex_down(&async_futex);
137 if (frw->writers || frw->readers) {
138 fibril_t *f = (fibril_t *) fibril_get_id();
139 f->flags |= FIBRIL_WRITER;
140 list_append(&f->link, &frw->waiters);
141 fibril_switch(FIBRIL_TO_MANAGER);
142 } else {
143 frw->writers++;
144 futex_up(&async_futex);
145 }
146}
147
148static void _fibril_rwlock_common_unlock(fibril_rwlock_t *frw)
149{
150 futex_down(&async_futex);
151 assert(frw->readers || (frw->writers == 1));
152 if (frw->readers) {
153 if (--frw->readers)
154 goto out;
155 } else {
156 frw->writers--;
157 }
158
159 assert(!frw->readers && !frw->writers);
160
161 while (!list_empty(&frw->waiters)) {
162 link_t *tmp = frw->waiters.next;
163 fibril_t *f = list_get_instance(tmp, fibril_t, link);
164
165 if (f->flags & FIBRIL_WRITER) {
166 if (frw->readers)
167 break;
168 list_remove(&f->link);
169 fibril_add_ready((fid_t) f);
170 frw->writers++;
171 optimize_execution_power();
172 break;
173 } else {
174 list_remove(&f->link);
175 fibril_add_ready((fid_t) f);
176 frw->readers++;
177 optimize_execution_power();
178 }
179 }
180out:
181 futex_up(&async_futex);
182}
183
184void fibril_rwlock_read_unlock(fibril_rwlock_t *frw)
185{
186 _fibril_rwlock_common_unlock(frw);
187}
188
189void fibril_rwlock_write_unlock(fibril_rwlock_t *frw)
190{
191 _fibril_rwlock_common_unlock(frw);
192}
193
194void fibril_condvar_initialize(fibril_condvar_t *fcv)
195{
196 list_initialize(&fcv->waiters);
197}
198
199int
200fibril_condvar_wait_timeout(fibril_condvar_t *fcv, fibril_mutex_t *fm,
201 suseconds_t timeout)
202{
203 awaiter_t wdata;
204
205 if (timeout < 0)
206 return ETIMEOUT;
207
208 wdata.fid = fibril_get_id();
209 wdata.active = false;
210
211 wdata.to_event.inlist = timeout > 0;
212 wdata.to_event.occurred = false;
213 link_initialize(&wdata.to_event.link);
214
215 wdata.wu_event.inlist = true;
216 link_initialize(&wdata.wu_event.link);
217
218 futex_down(&async_futex);
219 if (timeout) {
220 gettimeofday(&wdata.to_event.expires, NULL);
221 tv_add(&wdata.to_event.expires, timeout);
222 async_insert_timeout(&wdata);
223 }
224 list_append(&wdata.wu_event.link, &fcv->waiters);
225 _fibril_mutex_unlock_unsafe(fm);
226 fibril_switch(FIBRIL_TO_MANAGER);
227 fibril_mutex_lock(fm);
228
229 /* async_futex not held after fibril_switch() */
230 futex_down(&async_futex);
231 if (wdata.to_event.inlist)
232 list_remove(&wdata.to_event.link);
233 if (wdata.wu_event.inlist)
234 list_remove(&wdata.wu_event.link);
235 futex_up(&async_futex);
236
237 return wdata.to_event.occurred ? ETIMEOUT : EOK;
238}
239
240void fibril_condvar_wait(fibril_condvar_t *fcv, fibril_mutex_t *fm)
241{
242 int rc;
243
244 rc = fibril_condvar_wait_timeout(fcv, fm, 0);
245 assert(rc == EOK);
246}
247
248static void _fibril_condvar_wakeup_common(fibril_condvar_t *fcv, bool once)
249{
250 link_t *tmp;
251 awaiter_t *wdp;
252
253 futex_down(&async_futex);
254 while (!list_empty(&fcv->waiters)) {
255 tmp = fcv->waiters.next;
256 wdp = list_get_instance(tmp, awaiter_t, wu_event.link);
257 list_remove(&wdp->wu_event.link);
258 wdp->wu_event.inlist = false;
259 if (!wdp->active) {
260 wdp->active = true;
261 fibril_add_ready(wdp->fid);
262 optimize_execution_power();
263 if (once)
264 break;
265 }
266 }
267 futex_up(&async_futex);
268}
269
270void fibril_condvar_signal(fibril_condvar_t *fcv)
271{
272 _fibril_condvar_wakeup_common(fcv, true);
273}
274
275void fibril_condvar_broadcast(fibril_condvar_t *fcv)
276{
277 _fibril_condvar_wakeup_common(fcv, false);
278}
279
280/** @}
281 */
Note: See TracBrowser for help on using the repository browser.