source: mainline/uspace/lib/c/generic/fibril.c@ 9696b01

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 9696b01 was c1b979a, checked in by Jiri Svoboda <jiri@…>, 11 years ago

Taskdump printing of fibril stacktraces.

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