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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a840fb7 was 36e9cd1, checked in by Martin Decky <martin@…>, 15 years ago

silence compiler warnings (no change in actual functionality)

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