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

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

Improve the debugging options for futexes.

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