2
* Copyright (c) 2006 Ondrej Palkovsky
3
* Copyright (c) 2007 Jakub Jermar
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
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.
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.
43
#include <libarch/faddr.h>
48
#ifndef FIBRIL_INITIAL_STACK_PAGES_NO
49
#define FIBRIL_INITIAL_STACK_PAGES_NO 1
53
* This futex serializes access to ready_list, serialized_list and manager_list.
55
static atomic_t fibril_futex = FUTEX_INITIALIZER;
57
static LIST_INITIALIZE(ready_list);
58
static LIST_INITIALIZE(serialized_list);
59
static LIST_INITIALIZE(manager_list);
61
static void fibril_main(void);
63
/** Number of threads that are executing a manager fibril. */
64
static int threads_in_manager;
65
/** Number of threads that are executing a manager fibril and are serialized. */
66
static int serialized_threads; /* Protected by async_futex */
67
/** Fibril-local count of serialization. If > 0, we must not preempt */
68
static fibril_local int serialization_count;
70
/** Setup fibril information into TCB structure */
71
fibril_t *fibril_setup(void)
80
f = malloc(sizeof(fibril_t));
92
f->clean_after_me = NULL;
99
void fibril_teardown(fibril_t *f)
105
/** Function that spans the whole life-cycle of a fibril.
107
* Each fibril begins execution in this function. Then the function implementing
108
* the fibril logic is called. After its return, the return value is saved.
109
* The fibril then switches to another fibril, which cleans up after it.
111
void fibril_main(void)
113
fibril_t *f = __tcb_get()->fibril_data;
115
/* Call the implementing function. */
116
f->retval = f->func(f->arg);
118
fibril_switch(FIBRIL_FROM_DEAD);
122
/** Switch from the current fibril.
124
* If calling with FIBRIL_TO_MANAGER parameter, the async_futex should be
127
* @param stype Switch type. One of FIBRIL_PREEMPT, FIBRIL_TO_MANAGER,
128
* FIBRIL_FROM_MANAGER, FIBRIL_FROM_DEAD. The parameter
129
* describes the circumstances of the switch.
130
* @return Return 0 if there is no ready fibril,
131
* return 1 otherwise.
133
int fibril_switch(fibril_switch_type_t stype)
135
fibril_t *srcf, *dstf;
138
futex_down(&fibril_futex);
140
if (stype == FIBRIL_PREEMPT && list_empty(&ready_list))
143
if (stype == FIBRIL_FROM_MANAGER) {
144
if (list_empty(&ready_list) && list_empty(&serialized_list))
147
* Do not preempt if there is not enough threads to run the
148
* ready fibrils which are not serialized.
150
if (list_empty(&serialized_list) &&
151
threads_in_manager <= serialized_threads) {
155
/* If we are going to manager and none exists, create it */
156
if (stype == FIBRIL_TO_MANAGER || stype == FIBRIL_FROM_DEAD) {
157
while (list_empty(&manager_list)) {
158
futex_up(&fibril_futex);
159
async_create_manager();
160
futex_down(&fibril_futex);
164
srcf = __tcb_get()->fibril_data;
165
if (stype != FIBRIL_FROM_DEAD) {
166
/* Save current state */
167
if (!context_save(&srcf->ctx)) {
168
if (serialization_count)
169
srcf->flags &= ~FIBRIL_SERIALIZED;
170
if (srcf->clean_after_me) {
172
* Cleanup after the dead fibril from which we
173
* restored context here.
175
void *stack = srcf->clean_after_me->stack;
178
* This check is necessary because a
179
* thread could have exited like a
180
* normal fibril using the
181
* FIBRIL_FROM_DEAD switch type. In that
182
* case, its fibril will not have the
183
* stack member filled.
187
fibril_teardown(srcf->clean_after_me);
188
srcf->clean_after_me = NULL;
190
return 1; /* futex_up already done here */
193
/* Save myself to the correct run list */
194
if (stype == FIBRIL_PREEMPT)
195
list_append(&srcf->link, &ready_list);
196
else if (stype == FIBRIL_FROM_MANAGER) {
197
list_append(&srcf->link, &manager_list);
198
threads_in_manager--;
201
* If stype == FIBRIL_TO_MANAGER, don't put ourselves to
202
* any list, we should already be somewhere, or we will
208
/* Choose a new fibril to run */
209
if (stype == FIBRIL_TO_MANAGER || stype == FIBRIL_FROM_DEAD) {
210
dstf = list_get_instance(manager_list.next, fibril_t, link);
211
if (serialization_count && stype == FIBRIL_TO_MANAGER) {
212
serialized_threads++;
213
srcf->flags |= FIBRIL_SERIALIZED;
215
threads_in_manager++;
217
if (stype == FIBRIL_FROM_DEAD)
218
dstf->clean_after_me = srcf;
220
if (!list_empty(&serialized_list)) {
221
dstf = list_get_instance(serialized_list.next, fibril_t,
223
serialized_threads--;
225
dstf = list_get_instance(ready_list.next, fibril_t,
229
list_remove(&dstf->link);
231
futex_up(&fibril_futex);
232
context_restore(&dstf->ctx);
236
futex_up(&fibril_futex);
240
/** Create a new fibril.
242
* @param func Implementing function of the new fibril.
243
* @param arg Argument to pass to func.
245
* @return Return 0 on failure or TLS of the new fibril.
247
fid_t fibril_create(int (*func)(void *), void *arg)
254
f->stack = (char *) malloc(FIBRIL_INITIAL_STACK_PAGES_NO *
264
context_save(&f->ctx);
265
context_set(&f->ctx, FADDR(fibril_main), f->stack,
266
FIBRIL_INITIAL_STACK_PAGES_NO * getpagesize(), f->tcb);
271
/** Add a fibril to the ready list.
273
* @param fid Pointer to the fibril structure of the fibril to be
276
void fibril_add_ready(fid_t fid)
280
f = (fibril_t *) fid;
281
futex_down(&fibril_futex);
282
if ((f->flags & FIBRIL_SERIALIZED))
283
list_append(&f->link, &serialized_list);
285
list_append(&f->link, &ready_list);
286
futex_up(&fibril_futex);
289
/** Add a fibril to the manager list.
291
* @param fid Pointer to the fibril structure of the fibril to be
294
void fibril_add_manager(fid_t fid)
298
f = (fibril_t *) fid;
300
futex_down(&fibril_futex);
301
list_append(&f->link, &manager_list);
302
futex_up(&fibril_futex);
305
/** Remove one manager from the manager list. */
306
void fibril_remove_manager(void)
308
futex_down(&fibril_futex);
309
if (list_empty(&manager_list)) {
310
futex_up(&fibril_futex);
313
list_remove(manager_list.next);
314
futex_up(&fibril_futex);
317
/** Return fibril id of the currently running fibril.
319
* @return fibril ID of the currently running fibril.
322
fid_t fibril_get_id(void)
324
return (fid_t) __tcb_get()->fibril_data;
327
/** Disable preemption
329
* If the fibril wants to send several message in a row and does not want to be
330
* preempted, it should start async_serialize_start() in the beginning of
331
* communication and async_serialize_end() in the end. If it is a true
332
* multithreaded application, it should protect the communication channel by a
336
void fibril_inc_sercount(void)
338
serialization_count++;
341
/** Restore the preemption counter to the previous state. */
342
void fibril_dec_sercount(void)
344
serialization_count--;