source: mainline/uspace/lib/c/generic/fibril.c@ 204ba47

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

Protect accesses to fibril_list by fibril_futex.

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