source: mainline/uspace/lib/libc/generic/fibril.c@ 0dd0f71f

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 0dd0f71f was 253227a, checked in by Martin Decky <martin@…>, 17 years ago

empty line remove

  • 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 <libadt/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/** This futex serializes access to ready_list, serialized_list and manage_list.
53 */
54static atomic_t fibril_futex = FUTEX_INITIALIZER;
55
56static LIST_INITIALIZE(ready_list);
57static LIST_INITIALIZE(serialized_list);
58static LIST_INITIALIZE(manager_list);
59
60static void fibril_main(void);
61
62/** Number of fibrils that are in async_serialized mode */
63static int serialized_fibrils; /* Protected by async_futex */
64/** Thread-local count of serialization. If >0, we must not preempt */
65static __thread int serialization_count;
66/** Counter for fibrils residing in async_manager */
67static int fibrils_in_manager;
68
69/** Setup fibril information into TCB structure */
70fibril_t *fibril_setup(void)
71{
72 fibril_t *f;
73 tcb_t *tcb;
74
75 tcb = __make_tls();
76 if (!tcb)
77 return NULL;
78
79 f = malloc(sizeof(fibril_t));
80 if (!f) {
81 __free_tls(tcb);
82 return NULL;
83 }
84
85 tcb->fibril_data = f;
86 f->tcb = tcb;
87
88 f->func = NULL;
89 f->arg = NULL;
90 f->stack = NULL;
91 f->clean_after_me = NULL;
92 f->retval = 0;
93 f->flags = 0;
94
95 return f;
96}
97
98void fibril_teardown(fibril_t *f)
99{
100 __free_tls(f->tcb);
101 free(f);
102}
103
104/** Function that spans the whole life-cycle of a fibril.
105 *
106 * Each fibril begins execution in this function. Then the function implementing
107 * the fibril logic is called. After its return, the return value is saved.
108 * The fibril then switches to another fibril, which cleans up after it.
109 */
110void fibril_main(void)
111{
112 fibril_t *f = __tcb_get()->fibril_data;
113
114 /* Call the implementing function. */
115 f->retval = f->func(f->arg);
116
117 fibril_switch(FIBRIL_FROM_DEAD);
118 /* not reached */
119}
120
121/** Switch from the current fibril.
122 *
123 * If calling with FIBRIL_TO_MANAGER parameter, the async_futex should be
124 * held.
125 *
126 * @param stype Switch type. One of FIBRIL_PREEMPT, FIBRIL_TO_MANAGER,
127 * FIBRIL_FROM_MANAGER, FIBRIL_FROM_DEAD. The parameter
128 * describes the circumstances of the switch.
129 * @return Return 0 if there is no ready fibril,
130 * return 1 otherwise.
131 */
132int fibril_switch(fibril_switch_type_t stype)
133{
134 fibril_t *srcf, *dstf;
135 int retval = 0;
136
137 futex_down(&fibril_futex);
138
139 if (stype == FIBRIL_PREEMPT && list_empty(&ready_list))
140 goto ret_0;
141
142 if (stype == FIBRIL_FROM_MANAGER) {
143 if (list_empty(&ready_list) && list_empty(&serialized_list))
144 goto ret_0;
145 /*
146 * Do not preempt if there is not sufficient count of fibril
147 * managers.
148 */
149 if (list_empty(&serialized_list) &&
150 fibrils_in_manager <= serialized_fibrils) {
151 goto ret_0;
152 }
153 }
154 /* If we are going to manager and none exists, create it */
155 if (stype == FIBRIL_TO_MANAGER || stype == FIBRIL_FROM_DEAD) {
156 while (list_empty(&manager_list)) {
157 futex_up(&fibril_futex);
158 async_create_manager();
159 futex_down(&fibril_futex);
160 }
161 }
162
163 srcf = __tcb_get()->fibril_data;
164 if (stype != FIBRIL_FROM_DEAD) {
165 /* Save current state */
166 if (!context_save(&srcf->ctx)) {
167 if (serialization_count)
168 srcf->flags &= ~FIBRIL_SERIALIZED;
169 if (srcf->clean_after_me) {
170 /*
171 * Cleanup after the dead fibril from which we
172 * restored context here.
173 */
174 void *stack = srcf->clean_after_me->stack;
175 if (stack) {
176 /*
177 * This check is necessary because a
178 * thread could have exited like a
179 * normal fibril using the
180 * FIBRIL_FROM_DEAD switch type. In that
181 * case, its fibril will not have the
182 * stack member filled.
183 */
184 free(stack);
185 }
186 fibril_teardown(srcf->clean_after_me);
187 srcf->clean_after_me = NULL;
188 }
189 return 1; /* futex_up already done here */
190 }
191
192 /* Save myself to the correct run list */
193 if (stype == FIBRIL_PREEMPT)
194 list_append(&srcf->link, &ready_list);
195 else if (stype == FIBRIL_FROM_MANAGER) {
196 list_append(&srcf->link, &manager_list);
197 fibrils_in_manager--;
198 } else {
199 /*
200 * If stype == FIBRIL_TO_MANAGER, don't put ourselves to
201 * any list, we should already be somewhere, or we will
202 * be lost.
203 */
204 }
205 }
206
207 /* Choose a new fibril to run */
208 if (stype == FIBRIL_TO_MANAGER || stype == FIBRIL_FROM_DEAD) {
209 dstf = list_get_instance(manager_list.next, fibril_t, link);
210 if (serialization_count && stype == FIBRIL_TO_MANAGER) {
211 serialized_fibrils++;
212 srcf->flags |= FIBRIL_SERIALIZED;
213 }
214 fibrils_in_manager++;
215
216 if (stype == FIBRIL_FROM_DEAD)
217 dstf->clean_after_me = srcf;
218 } else {
219 if (!list_empty(&serialized_list)) {
220 dstf = list_get_instance(serialized_list.next, fibril_t,
221 link);
222 serialized_fibrils--;
223 } else {
224 dstf = list_get_instance(ready_list.next, fibril_t,
225 link);
226 }
227 }
228 list_remove(&dstf->link);
229
230 futex_up(&fibril_futex);
231 context_restore(&dstf->ctx);
232 /* not reached */
233
234ret_0:
235 futex_up(&fibril_futex);
236 return retval;
237}
238
239/** Create a new fibril.
240 *
241 * @param func Implementing function of the new fibril.
242 * @param arg Argument to pass to func.
243 *
244 * @return Return 0 on failure or TLS of the new fibril.
245 */
246fid_t fibril_create(int (*func)(void *), void *arg)
247{
248 fibril_t *f;
249
250 f = fibril_setup();
251 if (!f)
252 return 0;
253 f->stack = (char *) malloc(FIBRIL_INITIAL_STACK_PAGES_NO *
254 getpagesize());
255 if (!f->stack) {
256 fibril_teardown(f);
257 return 0;
258 }
259
260 f->func = func;
261 f->arg = arg;
262
263 context_save(&f->ctx);
264 context_set(&f->ctx, FADDR(fibril_main), f->stack,
265 FIBRIL_INITIAL_STACK_PAGES_NO * getpagesize(), f->tcb);
266
267 return (fid_t) f;
268}
269
270/** Add a fibril to the ready list.
271 *
272 * @param fid Pinter to the fibril structure of the fibril to be
273 * added.
274 */
275void fibril_add_ready(fid_t fid)
276{
277 fibril_t *f;
278
279 f = (fibril_t *) fid;
280 futex_down(&fibril_futex);
281 if ((f->flags & FIBRIL_SERIALIZED))
282 list_append(&f->link, &serialized_list);
283 else
284 list_append(&f->link, &ready_list);
285 futex_up(&fibril_futex);
286}
287
288/** Add a fibril to the manager list.
289 *
290 * @param fid Pinter to the fibril structure of the fibril to be added.
291 */
292void fibril_add_manager(fid_t fid)
293{
294 fibril_t *f;
295
296 f = (fibril_t *) fid;
297
298 futex_down(&fibril_futex);
299 list_append(&f->link, &manager_list);
300 futex_up(&fibril_futex);
301}
302
303/** Remove one manager from the manager list. */
304void fibril_remove_manager(void)
305{
306 futex_down(&fibril_futex);
307 if (list_empty(&manager_list)) {
308 futex_up(&fibril_futex);
309 return;
310 }
311 list_remove(manager_list.next);
312 futex_up(&fibril_futex);
313}
314
315/** Return fibril id of the currently running fibril.
316 *
317 * @return Fibril ID of the currently running pseudo thread.
318 */
319fid_t fibril_get_id(void)
320{
321 return (fid_t) __tcb_get()->fibril_data;
322}
323
324/** Disable preemption
325 *
326 * If the fibril wants to send several message in a row and does not want to be
327 * preempted, it should start async_serialize_start() in the beginning of
328 * communication and async_serialize_end() in the end. If it is a true
329 * multithreaded application, it should protect the communication channel by a
330 * futex as well. Interrupt messages can still be preempted.
331 */
332void fibril_inc_sercount(void)
333{
334 serialization_count++;
335}
336
337/** Restore the preemption counter to the previous state. */
338void fibril_dec_sercount(void)
339{
340 serialization_count--;
341}
342
343/** @}
344 */
Note: See TracBrowser for help on using the repository browser.