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

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

Remove support for the abandoned concept of fibril serialization.
Streamline fibril_switch() a little bit.

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