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

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

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

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