source: mainline/uspace/lib/libc/generic/fibril.c@ 9ae22ba

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 9ae22ba was 3562ec82, checked in by Martin Decky <martin@…>, 16 years ago

remove obsolete comment
cstyle

  • Property mode set to 100644
File size: 8.7 KB
Line 
1/*
2 * Copyright (c) 2006 Ondrej Palkovsky
3 * Copyright (c) 2007 Jakub Jermar
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup libc
31 * @{
32 */
33/** @file
34 */
35
36#include <adt/list.h>
37#include <fibril.h>
38#include <thread.h>
39#include <tls.h>
40#include <malloc.h>
41#include <unistd.h>
42#include <stdio.h>
43#include <libarch/faddr.h>
44#include <futex.h>
45#include <assert.h>
46#include <async.h>
47
48#ifndef FIBRIL_INITIAL_STACK_PAGES_NO
49#define FIBRIL_INITIAL_STACK_PAGES_NO 1
50#endif
51
52/**
53 * This futex serializes access to ready_list, serialized_list and manager_list.
54 */
55static atomic_t fibril_futex = FUTEX_INITIALIZER;
56
57static LIST_INITIALIZE(ready_list);
58static LIST_INITIALIZE(serialized_list);
59static LIST_INITIALIZE(manager_list);
60
61static void fibril_main(void);
62
63/** Number of threads that are executing a manager fibril. */
64static int threads_in_manager;
65/** Number of threads that are executing a manager fibril and are serialized. */
66static int serialized_threads; /* Protected by async_futex */
67/** Thread-local count of serialization. If > 0, we must not preempt */
68static __thread int serialization_count;
69
70/** Setup fibril information into TCB structure */
71fibril_t *fibril_setup(void)
72{
73 fibril_t *f;
74 tcb_t *tcb;
75
76 tcb = __make_tls();
77 if (!tcb)
78 return NULL;
79
80 f = malloc(sizeof(fibril_t));
81 if (!f) {
82 __free_tls(tcb);
83 return NULL;
84 }
85
86 tcb->fibril_data = f;
87 f->tcb = tcb;
88
89 f->func = NULL;
90 f->arg = NULL;
91 f->stack = NULL;
92 f->clean_after_me = NULL;
93 f->retval = 0;
94 f->flags = 0;
95
96 return f;
97}
98
99void fibril_teardown(fibril_t *f)
100{
101 __free_tls(f->tcb);
102 free(f);
103}
104
105/** Function that spans the whole life-cycle of a fibril.
106 *
107 * Each fibril begins execution in this function. Then the function implementing
108 * the fibril logic is called. After its return, the return value is saved.
109 * The fibril then switches to another fibril, which cleans up after it.
110 */
111void fibril_main(void)
112{
113 fibril_t *f = __tcb_get()->fibril_data;
114
115 /* Call the implementing function. */
116 f->retval = f->func(f->arg);
117
118 fibril_switch(FIBRIL_FROM_DEAD);
119 /* not reached */
120}
121
122/** Switch from the current fibril.
123 *
124 * If calling with FIBRIL_TO_MANAGER parameter, the async_futex should be
125 * held.
126 *
127 * @param stype Switch type. One of FIBRIL_PREEMPT, FIBRIL_TO_MANAGER,
128 * FIBRIL_FROM_MANAGER, FIBRIL_FROM_DEAD. The parameter
129 * describes the circumstances of the switch.
130 * @return Return 0 if there is no ready fibril,
131 * return 1 otherwise.
132 */
133int fibril_switch(fibril_switch_type_t stype)
134{
135 fibril_t *srcf, *dstf;
136 int retval = 0;
137
138 futex_down(&fibril_futex);
139
140 if (stype == FIBRIL_PREEMPT && list_empty(&ready_list))
141 goto ret_0;
142
143 if (stype == FIBRIL_FROM_MANAGER) {
144 if (list_empty(&ready_list) && list_empty(&serialized_list))
145 goto ret_0;
146 /*
147 * Do not preempt if there is not enough threads to run the
148 * ready fibrils which are not serialized.
149 */
150 if (list_empty(&serialized_list) &&
151 threads_in_manager <= serialized_threads) {
152 goto ret_0;
153 }
154 }
155 /* If we are going to manager and none exists, create it */
156 if (stype == FIBRIL_TO_MANAGER || stype == FIBRIL_FROM_DEAD) {
157 while (list_empty(&manager_list)) {
158 futex_up(&fibril_futex);
159 async_create_manager();
160 futex_down(&fibril_futex);
161 }
162 }
163
164 srcf = __tcb_get()->fibril_data;
165 if (stype != FIBRIL_FROM_DEAD) {
166 /* Save current state */
167 if (!context_save(&srcf->ctx)) {
168 if (serialization_count)
169 srcf->flags &= ~FIBRIL_SERIALIZED;
170 if (srcf->clean_after_me) {
171 /*
172 * Cleanup after the dead fibril from which we
173 * restored context here.
174 */
175 void *stack = srcf->clean_after_me->stack;
176 if (stack) {
177 /*
178 * This check is necessary because a
179 * thread could have exited like a
180 * normal fibril using the
181 * FIBRIL_FROM_DEAD switch type. In that
182 * case, its fibril will not have the
183 * stack member filled.
184 */
185 free(stack);
186 }
187 fibril_teardown(srcf->clean_after_me);
188 srcf->clean_after_me = NULL;
189 }
190 return 1; /* futex_up already done here */
191 }
192
193 /* Save myself to the correct run list */
194 if (stype == FIBRIL_PREEMPT)
195 list_append(&srcf->link, &ready_list);
196 else if (stype == FIBRIL_FROM_MANAGER) {
197 list_append(&srcf->link, &manager_list);
198 threads_in_manager--;
199 } else {
200 /*
201 * If stype == FIBRIL_TO_MANAGER, don't put ourselves to
202 * any list, we should already be somewhere, or we will
203 * be lost.
204 */
205 }
206 }
207
208 /* Choose a new fibril to run */
209 if (stype == FIBRIL_TO_MANAGER || stype == FIBRIL_FROM_DEAD) {
210 dstf = list_get_instance(manager_list.next, fibril_t, link);
211 if (serialization_count && stype == FIBRIL_TO_MANAGER) {
212 serialized_threads++;
213 srcf->flags |= FIBRIL_SERIALIZED;
214 }
215 threads_in_manager++;
216
217 if (stype == FIBRIL_FROM_DEAD)
218 dstf->clean_after_me = srcf;
219 } else {
220 if (!list_empty(&serialized_list)) {
221 dstf = list_get_instance(serialized_list.next, fibril_t,
222 link);
223 serialized_threads--;
224 } else {
225 dstf = list_get_instance(ready_list.next, fibril_t,
226 link);
227 }
228 }
229 list_remove(&dstf->link);
230
231 futex_up(&fibril_futex);
232 context_restore(&dstf->ctx);
233 /* not reached */
234
235ret_0:
236 futex_up(&fibril_futex);
237 return retval;
238}
239
240/** Create a new fibril.
241 *
242 * @param func Implementing function of the new fibril.
243 * @param arg Argument to pass to func.
244 *
245 * @return Return 0 on failure or TLS of the new fibril.
246 */
247fid_t fibril_create(int (*func)(void *), void *arg)
248{
249 fibril_t *f;
250
251 f = fibril_setup();
252 if (!f)
253 return 0;
254 f->stack = (char *) malloc(FIBRIL_INITIAL_STACK_PAGES_NO *
255 getpagesize());
256 if (!f->stack) {
257 fibril_teardown(f);
258 return 0;
259 }
260
261 f->func = func;
262 f->arg = arg;
263
264 context_save(&f->ctx);
265 context_set(&f->ctx, FADDR(fibril_main), f->stack,
266 FIBRIL_INITIAL_STACK_PAGES_NO * getpagesize(), f->tcb);
267
268 return (fid_t) f;
269}
270
271/** Add a fibril to the ready list.
272 *
273 * @param fid Pointer to the fibril structure of the fibril to be
274 * added.
275 */
276void fibril_add_ready(fid_t fid)
277{
278 fibril_t *f;
279
280 f = (fibril_t *) fid;
281 futex_down(&fibril_futex);
282 if ((f->flags & FIBRIL_SERIALIZED))
283 list_append(&f->link, &serialized_list);
284 else
285 list_append(&f->link, &ready_list);
286 futex_up(&fibril_futex);
287}
288
289/** Add a fibril to the manager list.
290 *
291 * @param fid Pointer to the fibril structure of the fibril to be
292 * added.
293 */
294void fibril_add_manager(fid_t fid)
295{
296 fibril_t *f;
297
298 f = (fibril_t *) fid;
299
300 futex_down(&fibril_futex);
301 list_append(&f->link, &manager_list);
302 futex_up(&fibril_futex);
303}
304
305/** Remove one manager from the manager list. */
306void fibril_remove_manager(void)
307{
308 futex_down(&fibril_futex);
309 if (list_empty(&manager_list)) {
310 futex_up(&fibril_futex);
311 return;
312 }
313 list_remove(manager_list.next);
314 futex_up(&fibril_futex);
315}
316
317/** Return fibril id of the currently running fibril.
318 *
319 * @return fibril ID of the currently running fibril.
320 *
321 */
322fid_t fibril_get_id(void)
323{
324 return (fid_t) __tcb_get()->fibril_data;
325}
326
327/** Disable preemption
328 *
329 * If the fibril wants to send several message in a row and does not want to be
330 * preempted, it should start async_serialize_start() in the beginning of
331 * communication and async_serialize_end() in the end. If it is a true
332 * multithreaded application, it should protect the communication channel by a
333 * futex as well.
334 *
335 */
336void fibril_inc_sercount(void)
337{
338 serialization_count++;
339}
340
341/** Restore the preemption counter to the previous state. */
342void fibril_dec_sercount(void)
343{
344 serialization_count--;
345}
346
347/** @}
348 */
Note: See TracBrowser for help on using the repository browser.