source: mainline/uspace/lib/c/generic/fibril.c@ 7c4b26c

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

Remove duplicate includes

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