source: mainline/uspace/lib/c/generic/fibril.c@ 31399f3

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 31399f3 was 31399f3, checked in by Jiri Svoboda <jiri@…>, 12 years ago

alloc_tls, maketls and free_tls are not compiler/language-builtin names and as such should not start with double underscore.

  • Property mode set to 100644
File size: 9.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/**
53 * This futex serializes access to ready_list,
54 * serialized_list and manager_list.
55 */
56static atomic_t fibril_futex = FUTEX_INITIALIZER;
57
58static LIST_INITIALIZE(ready_list);
59static LIST_INITIALIZE(serialized_list);
60static LIST_INITIALIZE(manager_list);
61
62/** Number of threads that are executing a manager fibril. */
63static int threads_in_manager;
64
65/**
66 * Number of threads that are executing a manager fibril
67 * and are serialized. Protected by async_futex.
68 */
69static int serialized_threads;
70
71/** Fibril-local count of serialization. If > 0, we must not preempt */
72static fibril_local int serialization_count;
73
74/** Function that spans the whole life-cycle of a fibril.
75 *
76 * Each fibril begins execution in this function. Then the function implementing
77 * the fibril logic is called. After its return, the return value is saved.
78 * The fibril then switches to another fibril, which cleans up after it.
79 *
80 */
81static void fibril_main(void)
82{
83 fibril_t *fibril = __tcb_get()->fibril_data;
84
85 /* Call the implementing function. */
86 fibril->retval = fibril->func(fibril->arg);
87
88 fibril_switch(FIBRIL_FROM_DEAD);
89 /* Not reached */
90}
91
92/** Setup fibril information into TCB structure
93 *
94 */
95fibril_t *fibril_setup(void)
96{
97 tcb_t *tcb = tls_make();
98 if (!tcb)
99 return NULL;
100
101 fibril_t *fibril = malloc(sizeof(fibril_t));
102 if (!fibril) {
103 tls_free(tcb);
104 return NULL;
105 }
106
107 tcb->fibril_data = fibril;
108 fibril->tcb = tcb;
109
110 fibril->func = NULL;
111 fibril->arg = NULL;
112 fibril->stack = NULL;
113 fibril->clean_after_me = NULL;
114 fibril->retval = 0;
115 fibril->flags = 0;
116
117 fibril->waits_for = NULL;
118
119 return fibril;
120}
121
122void fibril_teardown(fibril_t *fibril)
123{
124 tls_free(fibril->tcb);
125 free(fibril);
126}
127
128/** Switch from the current fibril.
129 *
130 * If calling with FIBRIL_TO_MANAGER parameter, the async_futex should be
131 * held.
132 *
133 * @param stype Switch type. One of FIBRIL_PREEMPT, FIBRIL_TO_MANAGER,
134 * FIBRIL_FROM_MANAGER, FIBRIL_FROM_DEAD. The parameter
135 * describes the circumstances of the switch.
136 *
137 * @return 0 if there is no ready fibril,
138 * @return 1 otherwise.
139 *
140 */
141int fibril_switch(fibril_switch_type_t stype)
142{
143 int retval = 0;
144
145 futex_down(&fibril_futex);
146
147 if (stype == FIBRIL_PREEMPT && list_empty(&ready_list))
148 goto ret_0;
149
150 if (stype == FIBRIL_FROM_MANAGER) {
151 if ((list_empty(&ready_list)) && (list_empty(&serialized_list)))
152 goto ret_0;
153
154 /*
155 * Do not preempt if there is not enough threads to run the
156 * ready fibrils which are not serialized.
157 */
158 if ((list_empty(&serialized_list)) &&
159 (threads_in_manager <= serialized_threads)) {
160 goto ret_0;
161 }
162 }
163
164 /* If we are going to manager and none exists, create it */
165 if ((stype == FIBRIL_TO_MANAGER) || (stype == FIBRIL_FROM_DEAD)) {
166 while (list_empty(&manager_list)) {
167 futex_up(&fibril_futex);
168 async_create_manager();
169 futex_down(&fibril_futex);
170 }
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 (serialization_count)
179 srcf->flags &= ~FIBRIL_SERIALIZED;
180
181 if (srcf->clean_after_me) {
182 /*
183 * Cleanup after the dead fibril from which we
184 * restored context here.
185 */
186 void *stack = srcf->clean_after_me->stack;
187 if (stack) {
188 /*
189 * This check is necessary because a
190 * thread could have exited like a
191 * normal fibril using the
192 * FIBRIL_FROM_DEAD switch type. In that
193 * case, its fibril will not have the
194 * stack member filled.
195 */
196 as_area_destroy(stack);
197 }
198 fibril_teardown(srcf->clean_after_me);
199 srcf->clean_after_me = NULL;
200 }
201
202 return 1; /* futex_up already done here */
203 }
204
205 /* Save myself to the correct run list */
206 if (stype == FIBRIL_PREEMPT)
207 list_append(&srcf->link, &ready_list);
208 else if (stype == FIBRIL_FROM_MANAGER) {
209 list_append(&srcf->link, &manager_list);
210 threads_in_manager--;
211 } else {
212 /*
213 * If stype == FIBRIL_TO_MANAGER, don't put ourselves to
214 * any list, we should already be somewhere, or we will
215 * be lost.
216 */
217 }
218 }
219
220 /* Choose a new fibril to run */
221 fibril_t *dstf;
222 if ((stype == FIBRIL_TO_MANAGER) || (stype == FIBRIL_FROM_DEAD)) {
223 dstf = list_get_instance(list_first(&manager_list), fibril_t,
224 link);
225 if (serialization_count && stype == FIBRIL_TO_MANAGER) {
226 serialized_threads++;
227 srcf->flags |= FIBRIL_SERIALIZED;
228 }
229 threads_in_manager++;
230
231 if (stype == FIBRIL_FROM_DEAD)
232 dstf->clean_after_me = srcf;
233 } else {
234 if (!list_empty(&serialized_list)) {
235 dstf = list_get_instance(list_first(&serialized_list),
236 fibril_t, link);
237 serialized_threads--;
238 } else {
239 dstf = list_get_instance(list_first(&ready_list),
240 fibril_t, link);
241 }
242 }
243 list_remove(&dstf->link);
244
245 futex_up(&fibril_futex);
246 context_restore(&dstf->ctx);
247 /* not reached */
248
249ret_0:
250 futex_up(&fibril_futex);
251 return retval;
252}
253
254/** Create a new fibril.
255 *
256 * @param func Implementing function of the new fibril.
257 * @param arg Argument to pass to func.
258 *
259 * @return 0 on failure or TLS of the new fibril.
260 *
261 */
262fid_t fibril_create(int (*func)(void *), void *arg)
263{
264 fibril_t *fibril;
265
266 fibril = fibril_setup();
267 if (fibril == NULL)
268 return 0;
269
270 size_t stack_size = stack_size_get();
271 fibril->stack = as_area_create((void *) -1, stack_size,
272 AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE | AS_AREA_GUARD |
273 AS_AREA_LATE_RESERVE);
274 if (fibril->stack == (void *) -1) {
275 fibril_teardown(fibril);
276 return 0;
277 }
278
279 fibril->func = func;
280 fibril->arg = arg;
281
282 context_save(&fibril->ctx);
283 context_set(&fibril->ctx, FADDR(fibril_main), fibril->stack,
284 stack_size, fibril->tcb);
285
286 return (fid_t) fibril;
287}
288
289/** Delete a fibril that has never run.
290 *
291 * Free resources of a fibril that has been created with fibril_create()
292 * but never readied using fibril_add_ready().
293 *
294 * @param fid Pointer to the fibril structure of the fibril to be
295 * added.
296 */
297void fibril_destroy(fid_t fid)
298{
299 fibril_t *fibril = (fibril_t *) fid;
300
301 as_area_destroy(fibril->stack);
302 fibril_teardown(fibril);
303}
304
305/** Add a fibril to the ready list.
306 *
307 * @param fid Pointer to the fibril structure of the fibril to be
308 * added.
309 *
310 */
311void fibril_add_ready(fid_t fid)
312{
313 fibril_t *fibril = (fibril_t *) fid;
314
315 futex_down(&fibril_futex);
316
317 if ((fibril->flags & FIBRIL_SERIALIZED))
318 list_append(&fibril->link, &serialized_list);
319 else
320 list_append(&fibril->link, &ready_list);
321
322 futex_up(&fibril_futex);
323}
324
325/** Add a fibril to the manager list.
326 *
327 * @param fid Pointer to the fibril structure of the fibril to be
328 * added.
329 *
330 */
331void fibril_add_manager(fid_t fid)
332{
333 fibril_t *fibril = (fibril_t *) fid;
334
335 futex_down(&fibril_futex);
336 list_append(&fibril->link, &manager_list);
337 futex_up(&fibril_futex);
338}
339
340/** Remove one manager from the manager list. */
341void fibril_remove_manager(void)
342{
343 futex_down(&fibril_futex);
344
345 if (!list_empty(&manager_list))
346 list_remove(list_first(&manager_list));
347
348 futex_up(&fibril_futex);
349}
350
351/** Return fibril id of the currently running fibril.
352 *
353 * @return fibril ID of the currently running fibril.
354 *
355 */
356fid_t fibril_get_id(void)
357{
358 return (fid_t) __tcb_get()->fibril_data;
359}
360
361/** Disable preemption
362 *
363 * If the fibril wants to send several message in a row and does not want to be
364 * preempted, it should start async_serialize_start() in the beginning of
365 * communication and async_serialize_end() in the end. If it is a true
366 * multithreaded application, it should protect the communication channel by a
367 * futex as well.
368 *
369 */
370void fibril_inc_sercount(void)
371{
372 serialization_count++;
373}
374
375/** Restore the preemption counter to the previous state. */
376void fibril_dec_sercount(void)
377{
378 serialization_count--;
379}
380
381int fibril_get_sercount(void)
382{
383 return serialization_count;
384}
385
386/** @}
387 */
Note: See TracBrowser for help on using the repository browser.