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

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

Make as_area_create() accept phone to external pager

  • Property mode set to 100644
File size: 8.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
[6c1bb0d]52#ifdef FUTEX_UPGRADABLE
[d54b303]53#include <rcu.h>
54#endif
[bc1f1c2]55
[cc27c8c5]56/**
[596d65c]57 * This futex serializes access to ready_list,
[2654afb]58 * manager_list and fibril_list.
[596d65c]59 */
[927a181e]60static futex_t fibril_futex = FUTEX_INITIALIZER;
[12f91130]61
[bc1f1c2]62static LIST_INITIALIZE(ready_list);
63static LIST_INITIALIZE(manager_list);
[c1b979a]64static LIST_INITIALIZE(fibril_list);
[bc1f1c2]65
[596d65c]66/** Function that spans the whole life-cycle of a fibril.
67 *
68 * Each fibril begins execution in this function. Then the function implementing
69 * the fibril logic is called. After its return, the return value is saved.
70 * The fibril then switches to another fibril, which cleans up after it.
71 *
72 */
73static void fibril_main(void)
[bc1f1c2]74{
[596d65c]75 fibril_t *fibril = __tcb_get()->fibril_data;
[d54b303]76
[6c1bb0d]77#ifdef FUTEX_UPGRADABLE
[d54b303]78 rcu_register_fibril();
79#endif
[596d65c]80
81 /* Call the implementing function. */
82 fibril->retval = fibril->func(fibril->arg);
83
[c721d26]84 futex_down(&async_futex);
[596d65c]85 fibril_switch(FIBRIL_FROM_DEAD);
86 /* Not reached */
87}
[bc1f1c2]88
[596d65c]89/** Setup fibril information into TCB structure
90 *
91 */
92fibril_t *fibril_setup(void)
93{
[31399f3]94 tcb_t *tcb = tls_make();
[bc1f1c2]95 if (!tcb)
96 return NULL;
[596d65c]97
98 fibril_t *fibril = malloc(sizeof(fibril_t));
99 if (!fibril) {
[31399f3]100 tls_free(tcb);
[bc1f1c2]101 return NULL;
102 }
[596d65c]103
104 tcb->fibril_data = fibril;
105 fibril->tcb = tcb;
106
107 fibril->func = NULL;
108 fibril->arg = NULL;
109 fibril->stack = NULL;
110 fibril->clean_after_me = NULL;
111 fibril->retval = 0;
112 fibril->flags = 0;
113
[8cf6709]114 fibril->waits_for = NULL;
[4d11204]115
[c170438]116 fibril->switches = 0;
117
[6d87dce]118 /*
119 * We are called before __tcb_set(), so we need to use
120 * futex_down/up() instead of futex_lock/unlock() that
121 * may attempt to access TLS.
122 */
123 futex_down(&fibril_futex);
[c1b979a]124 list_append(&fibril->all_link, &fibril_list);
[6d87dce]125 futex_up(&fibril_futex);
[8cf6709]126
[596d65c]127 return fibril;
[bc1f1c2]128}
129
[4d11204]130void fibril_teardown(fibril_t *fibril, bool locked)
131{
132 if (!locked)
133 futex_lock(&fibril_futex);
[c1b979a]134 list_remove(&fibril->all_link);
[4d11204]135 if (!locked)
136 futex_unlock(&fibril_futex);
[31399f3]137 tls_free(fibril->tcb);
[596d65c]138 free(fibril);
[bc1f1c2]139}
140
[116d3f6f]141/** Switch from the current fibril.
[bc1f1c2]142 *
[c721d26]143 * If stype is FIBRIL_TO_MANAGER or FIBRIL_FROM_DEAD, the async_futex must
144 * be held.
[bc1f1c2]145 *
[596d65c]146 * @param stype Switch type. One of FIBRIL_PREEMPT, FIBRIL_TO_MANAGER,
147 * FIBRIL_FROM_MANAGER, FIBRIL_FROM_DEAD. The parameter
148 * describes the circumstances of the switch.
149 *
150 * @return 0 if there is no ready fibril,
151 * @return 1 otherwise.
152 *
[bc1f1c2]153 */
[116d3f6f]154int fibril_switch(fibril_switch_type_t stype)
[bc1f1c2]155{
[df7cbc6]156 futex_lock(&fibril_futex);
[c721d26]157
[2654afb]158 switch (stype) {
159 case FIBRIL_PREEMPT:
160 case FIBRIL_FROM_MANAGER:
161 if (list_empty(&ready_list)) {
162 futex_unlock(&fibril_futex);
163 return 0;
[bc1f1c2]164 }
[2654afb]165 break;
166 case FIBRIL_TO_MANAGER:
167 case FIBRIL_FROM_DEAD:
[c721d26]168 /* Make sure the async_futex is held. */
169 assert((atomic_signed_t) async_futex.val.count <= 0);
170
[2654afb]171 /* If we are going to manager and none exists, create it */
[bc1f1c2]172 while (list_empty(&manager_list)) {
[df7cbc6]173 futex_unlock(&fibril_futex);
[bc1f1c2]174 async_create_manager();
[df7cbc6]175 futex_lock(&fibril_futex);
[bc1f1c2]176 }
[2654afb]177 break;
[bc1f1c2]178 }
179
[36e9cd1]180 fibril_t *srcf = __tcb_get()->fibril_data;
[bc1f1c2]181 if (stype != FIBRIL_FROM_DEAD) {
[36e9cd1]182
[bc1f1c2]183 /* Save current state */
184 if (!context_save(&srcf->ctx)) {
185 if (srcf->clean_after_me) {
186 /*
187 * Cleanup after the dead fibril from which we
188 * restored context here.
189 */
[36e9cd1]190 void *stack = srcf->clean_after_me->stack;
[116d3f6f]191 if (stack) {
192 /*
193 * This check is necessary because a
194 * thread could have exited like a
195 * normal fibril using the
196 * FIBRIL_FROM_DEAD switch type. In that
197 * case, its fibril will not have the
198 * stack member filled.
199 */
[1107050]200 as_area_destroy(stack);
[116d3f6f]201 }
[4d11204]202 fibril_teardown(srcf->clean_after_me, true);
[bc1f1c2]203 srcf->clean_after_me = NULL;
204 }
[36e9cd1]205
[df7cbc6]206 return 1; /* futex_unlock already done here */
[bc1f1c2]207 }
[36e9cd1]208
[2654afb]209 /* Put the current fibril into the correct run list */
210 switch (stype) {
211 case FIBRIL_PREEMPT:
[bc1f1c2]212 list_append(&srcf->link, &ready_list);
[2654afb]213 break;
214 case FIBRIL_FROM_MANAGER:
[bc1f1c2]215 list_append(&srcf->link, &manager_list);
[2654afb]216 break;
217 default:
218 assert(stype == FIBRIL_TO_MANAGER);
219
[c170438]220 srcf->switches++;
221
[bc1f1c2]222 /*
[2654afb]223 * Don't put the current fibril into any list, it should
224 * already be somewhere, or it will be lost.
[bc1f1c2]225 */
[2654afb]226 break;
[bc1f1c2]227 }
228 }
[116d3f6f]229
[36e9cd1]230 fibril_t *dstf;
[2654afb]231
232 /* Choose a new fibril to run */
233 switch (stype) {
234 case FIBRIL_TO_MANAGER:
235 case FIBRIL_FROM_DEAD:
[b72efe8]236 dstf = list_get_instance(list_first(&manager_list), fibril_t,
237 link);
[36e9cd1]238
[116d3f6f]239 if (stype == FIBRIL_FROM_DEAD)
[bc1f1c2]240 dstf->clean_after_me = srcf;
[2654afb]241 break;
242 default:
243 dstf = list_get_instance(list_first(&ready_list), fibril_t,
244 link);
245 break;
[bc1f1c2]246 }
[2654afb]247
[bc1f1c2]248 list_remove(&dstf->link);
[36e9cd1]249
[df7cbc6]250 futex_unlock(&fibril_futex);
[d54b303]251
[6c1bb0d]252#ifdef FUTEX_UPGRADABLE
[d54b303]253 if (stype == FIBRIL_FROM_DEAD) {
254 rcu_deregister_fibril();
255 }
256#endif
257
[bc1f1c2]258 context_restore(&dstf->ctx);
259 /* not reached */
260}
261
262/** Create a new fibril.
263 *
[596d65c]264 * @param func Implementing function of the new fibril.
265 * @param arg Argument to pass to func.
[eceff5f]266 * @param stksz Stack size in bytes.
[596d65c]267 *
268 * @return 0 on failure or TLS of the new fibril.
[bc1f1c2]269 *
270 */
[b4df8db]271fid_t fibril_create_generic(int (*func)(void *), void *arg, size_t stksz)
[bc1f1c2]272{
[596d65c]273 fibril_t *fibril;
274
275 fibril = fibril_setup();
276 if (fibril == NULL)
[bc1f1c2]277 return 0;
[596d65c]278
[eceff5f]279 size_t stack_size = (stksz == FIBRIL_DFLT_STK_SIZE) ?
280 stack_size_get() : stksz;
[6aeca0d]281 fibril->stack = as_area_create(AS_AREA_ANY, stack_size,
[1107050]282 AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE | AS_AREA_GUARD |
[6aeca0d]283 AS_AREA_LATE_RESERVE, AS_AREA_UNPAGED);
[1107050]284 if (fibril->stack == (void *) -1) {
[4d11204]285 fibril_teardown(fibril, false);
[bc1f1c2]286 return 0;
287 }
[116d3f6f]288
[596d65c]289 fibril->func = func;
290 fibril->arg = arg;
[7f122e3]291
[596d65c]292 context_save(&fibril->ctx);
293 context_set(&fibril->ctx, FADDR(fibril_main), fibril->stack,
[0aae87a6]294 stack_size, fibril->tcb);
[bc1f1c2]295
[596d65c]296 return (fid_t) fibril;
[bc1f1c2]297}
298
[32d19f7]299/** Delete a fibril that has never run.
300 *
301 * Free resources of a fibril that has been created with fibril_create()
302 * but never readied using fibril_add_ready().
303 *
304 * @param fid Pointer to the fibril structure of the fibril to be
305 * added.
306 */
307void fibril_destroy(fid_t fid)
308{
309 fibril_t *fibril = (fibril_t *) fid;
310
[1107050]311 as_area_destroy(fibril->stack);
[4d11204]312 fibril_teardown(fibril, false);
[32d19f7]313}
314
[bc1f1c2]315/** Add a fibril to the ready list.
316 *
[596d65c]317 * @param fid Pointer to the fibril structure of the fibril to be
318 * added.
319 *
[bc1f1c2]320 */
321void fibril_add_ready(fid_t fid)
322{
[596d65c]323 fibril_t *fibril = (fibril_t *) fid;
324
[df7cbc6]325 futex_lock(&fibril_futex);
[2654afb]326 list_append(&fibril->link, &ready_list);
[df7cbc6]327 futex_unlock(&fibril_futex);
[bc1f1c2]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
[df7cbc6]340 futex_lock(&fibril_futex);
[596d65c]341 list_append(&fibril->link, &manager_list);
[df7cbc6]342 futex_unlock(&fibril_futex);
[bc1f1c2]343}
344
345/** Remove one manager from the manager list. */
346void fibril_remove_manager(void)
347{
[df7cbc6]348 futex_lock(&fibril_futex);
[596d65c]349 if (!list_empty(&manager_list))
[b72efe8]350 list_remove(list_first(&manager_list));
[df7cbc6]351 futex_unlock(&fibril_futex);
[bc1f1c2]352}
353
354/** Return fibril id of the currently running fibril.
355 *
[3562ec82]356 * @return fibril ID of the currently running fibril.
357 *
[bc1f1c2]358 */
359fid_t fibril_get_id(void)
360{
361 return (fid_t) __tcb_get()->fibril_data;
362}
363
364/** @}
365 */
Note: See TracBrowser for help on using the repository browser.