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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since d54b303 was d54b303, checked in by Adam Hraska <adam.hraska+hos@…>, 13 years ago

Initial version of nop futexes that are upgradable to proper futexes.

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