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

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

Make fibril mutexes use the awaiter_t type for sleeping.
Timeouts are not supported.

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