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

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

Use 0 instead of -1 for the default fibril stack size and define
FIBRIL_DFLT_STK_SIZE to represent this special value.

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