source: mainline/uspace/lib/c/generic/fibril.c@ f044e96

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

do not provide general access to kernel headers from uspace, only allow specific headers to be accessed or shared
externalize headers which serve as kernel/uspace API/ABI into a special tree

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