source: mainline/uspace/lib/c/generic/fibril.c@ 8bf1eeb

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 8bf1eeb was 2e7291a, checked in by Jakub Jermar <jakub@…>, 14 years ago

Add fibril_get_sercount().

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