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

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

The async_futex must also be held during FIBRIL_FROM_DEAD.
Add the missing futex_down() to fibril_main() and async_manager().
Add an assert to fibril_switch().

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