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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since abf8bd8 was 899342e, checked in by Jiří Zárevúcky <jiri.zarevucky@…>, 8 years ago

fibril_futex must be locked during context_swap().

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