Subversion Repositories HelenOS

Rev

Rev 4513 | Rev 4526 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 4513 Rev 4516
Line 1... Line 1...
1
/*
1
/*
2
 * Copyright (c) 2006 Ondrej Palkovsky
-
 
3
 * Copyright (c) 2007 Jakub Jermar
2
 * Copyright (c) 2009 Jakub Jermar
4
 * All rights reserved.
3
 * All rights reserved.
5
 *
4
 *
6
 * Redistribution and use in source and binary forms, with or without
5
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions
6
 * modification, are permitted provided that the following conditions
8
 * are met:
7
 * are met:
Line 31... Line 30...
31
 * @{
30
 * @{
32
 */
31
 */
33
/** @file
32
/** @file
34
 */
33
 */
35
 
34
 
36
#include <adt/list.h>
35
#include <fibril_sync.h>
37
#include <fibril.h>
36
#include <fibril.h>
38
#include <thread.h>
-
 
39
#include <tls.h>
-
 
40
#include <malloc.h>
37
#include <async.h>
41
#include <unistd.h>
38
#include <adt/list.h>
42
#include <stdio.h>
-
 
43
#include <libarch/faddr.h>
-
 
44
#include <futex.h>
39
#include <futex.h>
45
#include <assert.h>
40
#include <assert.h>
46
#include <async.h>
-
 
47
 
41
 
48
#ifndef FIBRIL_INITIAL_STACK_PAGES_NO
-
 
49
#define FIBRIL_INITIAL_STACK_PAGES_NO   1
-
 
50
#endif
-
 
51
 
-
 
52
/**
-
 
53
 * This futex serializes access to ready_list, serialized_list and manager_list.
-
 
54
 */
-
 
55
static atomic_t fibril_futex = FUTEX_INITIALIZER;
-
 
56
 
-
 
57
static LIST_INITIALIZE(ready_list);
-
 
58
static LIST_INITIALIZE(serialized_list);
-
 
59
static LIST_INITIALIZE(manager_list);
-
 
60
 
-
 
61
static void fibril_main(void);
-
 
62
 
-
 
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
/** Thread-local count of serialization. If > 0, we must not preempt */
-
 
68
static __thread int serialization_count;
-
 
69
 
-
 
70
/** Setup fibril information into TCB structure */
-
 
71
fibril_t *fibril_setup(void)
42
void fibril_mutex_initialize(fibril_mutex_t *fm)
72
{
43
{
73
    fibril_t *f;
-
 
74
    tcb_t *tcb;
-
 
75
 
-
 
76
    tcb = __make_tls();
-
 
77
    if (!tcb)
-
 
78
        return NULL;
-
 
79
 
-
 
80
    f = malloc(sizeof(fibril_t));
-
 
81
    if (!f) {
-
 
82
        __free_tls(tcb);
-
 
83
        return NULL;
-
 
84
    }
-
 
85
 
-
 
86
    tcb->fibril_data = f;
-
 
87
    f->tcb = tcb;
-
 
88
 
-
 
89
    f->func = NULL;
44
    fm->counter = 1;
90
    f->arg = NULL;
-
 
91
    f->stack = NULL;
-
 
92
    f->clean_after_me = NULL;
45
    list_initialize(&fm->waiters);
93
    f->retval = 0;
-
 
94
    f->flags = 0;
-
 
95
 
-
 
96
    return f;
-
 
97
}
46
}
98
 
47
 
99
void fibril_teardown(fibril_t *f)
48
void fibril_mutex_lock(fibril_mutex_t *fm)
100
{
49
{
101
    __free_tls(f->tcb);
-
 
102
    free(f);
-
 
103
}
-
 
104
 
-
 
105
/** Function that spans the whole life-cycle of a fibril.
-
 
106
 *
-
 
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.
-
 
110
 */
-
 
111
void fibril_main(void)
-
 
112
{
-
 
113
    fibril_t *f = __tcb_get()->fibril_data;
-
 
114
 
-
 
115
    /* Call the implementing function. */
-
 
116
    f->retval = f->func(f->arg);
-
 
117
 
-
 
118
    fibril_switch(FIBRIL_FROM_DEAD);
-
 
119
    /* not reached */
-
 
120
}
-
 
121
 
-
 
122
/** Switch from the current fibril.
-
 
123
 *
-
 
124
 * If calling with FIBRIL_TO_MANAGER parameter, the async_futex should be
-
 
125
 * held.
-
 
126
 *
-
 
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.
-
 
132
 */
-
 
133
int fibril_switch(fibril_switch_type_t stype)
-
 
134
{
-
 
135
    fibril_t *srcf, *dstf;
-
 
136
    int retval = 0;
-
 
137
   
-
 
138
    futex_down(&fibril_futex);
50
    futex_down(&async_futex);
139
 
-
 
140
    if (stype == FIBRIL_PREEMPT && list_empty(&ready_list))
-
 
141
        goto ret_0;
-
 
142
 
-
 
143
    if (stype == FIBRIL_FROM_MANAGER) {
-
 
144
        if (list_empty(&ready_list) && list_empty(&serialized_list))
-
 
145
            goto ret_0;
-
 
146
        /*
-
 
147
         * Do not preempt if there is not enough threads to run the
-
 
148
         * ready fibrils which are not serialized.
-
 
149
         */
-
 
150
        if (list_empty(&serialized_list) &&
-
 
151
            threads_in_manager <= serialized_threads) {
-
 
152
            goto ret_0;
-
 
153
        }
-
 
154
    }
-
 
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);
-
 
161
        }
-
 
162
    }
-
 
163
   
-
 
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) {
51
    if (fm->counter-- <= 0) {
171
                /*
-
 
172
                 * Cleanup after the dead fibril from which we
-
 
173
                 * restored context here.
-
 
174
                 */
-
 
175
                void *stack = srcf->clean_after_me->stack;
-
 
176
                if (stack) {
-
 
177
                    /*
-
 
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.
-
 
184
                     */
-
 
185
                    free(stack);
-
 
186
                }
-
 
187
                fibril_teardown(srcf->clean_after_me);
52
        fibril_t *f = (fibril_t *) fibril_get_id();
188
                srcf->clean_after_me = NULL;
-
 
189
            }
-
 
190
            return 1;   /* futex_up already done here */
-
 
191
        }
-
 
192
 
-
 
193
        /* Save myself to the correct run list */
-
 
194
        if (stype == FIBRIL_PREEMPT)
-
 
195
            list_append(&srcf->link, &ready_list);
53
        list_append(&f->link, &fm->waiters);
196
        else if (stype == FIBRIL_FROM_MANAGER) {
54
        fibril_switch(FIBRIL_TO_MANAGER);
197
            list_append(&srcf->link, &manager_list);
-
 
198
            threads_in_manager--;
-
 
199
        } else {   
-
 
200
            /*
-
 
201
             * If stype == FIBRIL_TO_MANAGER, don't put ourselves to
-
 
202
             * any list, we should already be somewhere, or we will
-
 
203
             * be lost.
-
 
204
             */
-
 
205
        }
-
 
206
    }
-
 
207
   
-
 
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;
-
 
214
        }
-
 
215
        threads_in_manager++;
-
 
216
 
-
 
217
        if (stype == FIBRIL_FROM_DEAD)
-
 
218
            dstf->clean_after_me = srcf;
-
 
219
    } else {
55
    } else {
220
        if (!list_empty(&serialized_list)) {
-
 
221
            dstf = list_get_instance(serialized_list.next, fibril_t,
-
 
222
                link);
-
 
223
            serialized_threads--;
56
        futex_up(&async_futex);
224
        } else {
-
 
225
            dstf = list_get_instance(ready_list.next, fibril_t,
-
 
226
                link);
-
 
227
        }
-
 
228
    }
57
    }
229
    list_remove(&dstf->link);
-
 
230
 
-
 
231
    futex_up(&fibril_futex);
-
 
232
    context_restore(&dstf->ctx);
-
 
233
    /* not reached */
-
 
234
 
-
 
235
ret_0:
-
 
236
    futex_up(&fibril_futex);
-
 
237
    return retval;
-
 
238
}
58
}
239
 
59
 
240
/** Create a new fibril.
-
 
241
 *
-
 
242
 * @param func      Implementing function of the new fibril.
-
 
243
 * @param arg       Argument to pass to func.
-
 
244
 *
-
 
245
 * @return      Return 0 on failure or TLS of the new fibril.
-
 
246
 */
-
 
247
fid_t fibril_create(int (*func)(void *), void *arg)
60
bool fibril_mutex_trylock(fibril_mutex_t *fm)
248
{
61
{
249
    fibril_t *f;
62
    bool locked = false;
250
 
63
   
251
    f = fibril_setup();
64
    futex_down(&async_futex);
252
    if (!f)
-
 
253
        return 0;
-
 
254
    f->stack = (char *) malloc(FIBRIL_INITIAL_STACK_PAGES_NO *
-
 
255
        getpagesize());
-
 
256
    if (!f->stack) {
65
    if (fm->counter > 0) {
257
        fibril_teardown(f);
66
        fm->counter--;
258
        return 0;
67
        locked = true;
259
    }
68
    }
-
 
69
    futex_up(&async_futex);
260
   
70
   
261
    f->func = func;
-
 
262
    f->arg = arg;
-
 
263
 
-
 
264
    context_save(&f->ctx);
-
 
265
    context_set(&f->ctx, FADDR(fibril_main), f->stack,
-
 
266
        FIBRIL_INITIAL_STACK_PAGES_NO * getpagesize(), f->tcb);
-
 
267
 
-
 
268
    return (fid_t) f;
71
    return locked;
269
}
72
}
270
 
73
 
271
/** Add a fibril to the ready list.
-
 
272
 *
-
 
273
 * @param fid       Pointer to the fibril structure of the fibril to be
-
 
274
 *          added.
-
 
275
 */
-
 
276
void fibril_add_ready(fid_t fid)
74
void fibril_mutex_unlock(fibril_mutex_t *fm)
277
{
75
{
-
 
76
    futex_down(&async_futex);
-
 
77
    assert(fm->counter <= 0);
-
 
78
    if (fm->counter++ < 0) {
-
 
79
        link_t *tmp;
278
    fibril_t *f;
80
        fibril_t *f;
279
 
81
   
-
 
82
        assert(!list_empty(&fm->waiters));
280
    f = (fibril_t *) fid;
83
        tmp = fm->waiters.next;
281
    futex_down(&fibril_futex);
84
        f = list_get_instance(tmp, fibril_t, link);
282
    if ((f->flags & FIBRIL_SERIALIZED))
85
        list_remove(&f->link);
283
        list_append(&f->link, &serialized_list);
86
        fibril_add_ready((fid_t) f);
284
    else
87
    }
285
        list_append(&f->link, &ready_list);
-
 
286
    futex_up(&fibril_futex);
88
    futex_up(&async_futex);
287
}
89
}
288
 
90
 
289
/** Add a fibril to the manager list.
-
 
290
 *
-
 
291
 * @param fid       Pointer to the fibril structure of the fibril to be
-
 
292
 *          added.
-
 
293
 */
-
 
294
void fibril_add_manager(fid_t fid)
91
void fibril_rwlock_initialize(fibril_rwlock_t *frw)
295
{
92
{
296
    fibril_t *f;
-
 
297
 
-
 
298
    f = (fibril_t *) fid;
-
 
299
 
-
 
300
    futex_down(&fibril_futex);
-
 
301
    list_append(&f->link, &manager_list);
-
 
302
    futex_up(&fibril_futex);
93
    fibril_mutex_initialize(&frw->fm);
303
}
94
}
304
 
95
 
305
/** Remove one manager from the manager list. */
-
 
306
void fibril_remove_manager(void)
96
void fibril_rwlock_read_lock(fibril_rwlock_t *frw)
307
{
97
{
308
    futex_down(&fibril_futex);
98
    fibril_mutex_lock(&frw->fm);
309
    if (list_empty(&manager_list)) {
-
 
310
        futex_up(&fibril_futex);
-
 
311
        return;
-
 
312
    }
-
 
313
    list_remove(manager_list.next);
-
 
314
    futex_up(&fibril_futex);
-
 
315
}
99
}
316
 
100
 
317
/** Return fibril id of the currently running fibril.
101
void fibril_rwlock_write_lock(fibril_rwlock_t *frw)
318
 *
-
 
319
 * @return      Fibril ID of the currently running fibril.
-
 
320
 */
-
 
321
fid_t fibril_get_id(void)
-
 
322
{
102
{
323
    return (fid_t) __tcb_get()->fibril_data;
103
    fibril_mutex_lock(&frw->fm);
324
}
104
}
325
 
105
 
326
/** Disable preemption
-
 
327
 *
-
 
328
 * If the fibril wants to send several message in a row and does not want to be
-
 
329
 * preempted, it should start async_serialize_start() in the beginning of
-
 
330
 * communication and async_serialize_end() in the end. If it is a true
-
 
331
 * multithreaded application, it should protect the communication channel by a
-
 
332
 * futex as well. Interrupt messages can still be preempted.
-
 
333
 */
-
 
334
void fibril_inc_sercount(void)
106
void fibril_rwlock_read_unlock(fibril_rwlock_t *frw)
335
{
107
{
336
    serialization_count++;
108
    fibril_mutex_unlock(&frw->fm);
337
}
109
}
338
 
110
 
339
/** Restore the preemption counter to the previous state. */
-
 
340
void fibril_dec_sercount(void)
111
void fibril_rwlock_write_unlock(fibril_rwlock_t *frw)
341
{
112
{
342
    serialization_count--;
113
    fibril_mutex_unlock(&frw->fm);
343
}
114
}
344
 
115
 
345
/** @}
116
/** @}
346
 */
117
 */