Subversion Repositories HelenOS

Rev

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

Rev 2479 Rev 2481
Line 1... Line 1...
1
/*
1
/*
2
 * Copyright (c) 2006 Ondrej Palkovsky
2
 * Copyright (c) 2006 Ondrej Palkovsky
-
 
3
 * Copyright (c) 2007 Jakub Jermar
3
 * All rights reserved.
4
 * All rights reserved.
4
 *
5
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions
7
 * modification, are permitted provided that the following conditions
7
 * are met:
8
 * are met:
Line 42... Line 43...
42
#include <futex.h>
43
#include <futex.h>
43
#include <assert.h>
44
#include <assert.h>
44
#include <async.h>
45
#include <async.h>
45
 
46
 
46
#ifndef PSTHREAD_INITIAL_STACK_PAGES_NO
47
#ifndef PSTHREAD_INITIAL_STACK_PAGES_NO
47
#define PSTHREAD_INITIAL_STACK_PAGES_NO 1
48
#define PSTHREAD_INITIAL_STACK_PAGES_NO 1
48
#endif
49
#endif
49
 
50
 
50
static LIST_INITIALIZE(ready_list);
51
static LIST_INITIALIZE(ready_list);
51
static LIST_INITIALIZE(serialized_list);
52
static LIST_INITIALIZE(serialized_list);
52
static LIST_INITIALIZE(manager_list);
53
static LIST_INITIALIZE(manager_list);
53
 
54
 
54
static void psthread_main(void);
55
static void psthread_main(void);
55
 
56
 
56
static atomic_t psthread_futex = FUTEX_INITIALIZER;
57
static atomic_t psthread_futex = FUTEX_INITIALIZER;
57
/** Count of real threads that are in async_serialized mode */
58
/** Count of real threads that are in async_serialized mode */
58
static int serialized_threads; /* Protected by async_futex */
59
static int serialized_threads;  /* Protected by async_futex */
59
/** Thread-local count of serialization. If >0, we must not preempt */
60
/** Thread-local count of serialization. If >0, we must not preempt */
60
static __thread int serialization_count;
61
static __thread int serialization_count;
61
/** Counter of threads residing in async_manager */
62
/** Counter of threads residing in async_manager */
62
static int threads_in_manager;
63
static int threads_in_manager;
63
 
64
 
64
/** Setup psthread information into TCB structure */
65
/** Setup psthread information into TCB structure */
65
psthread_data_t * psthread_setup()
66
psthread_data_t *psthread_setup(void)
66
{
67
{
67
    psthread_data_t *pt;
68
    psthread_data_t *pt;
68
    tcb_t *tcb;
69
    tcb_t *tcb;
69
 
70
 
70
    tcb = __make_tls();
71
    tcb = __make_tls();
Line 87... Line 88...
87
{
88
{
88
    __free_tls(pt->tcb);
89
    __free_tls(pt->tcb);
89
    free(pt);
90
    free(pt);
90
}
91
}
91
 
92
 
92
/** Function that is called on entry to new pseudo thread */
93
/** Function that spans the whole life-cycle of a pseudo thread.
-
 
94
 *
-
 
95
 * Each pseudo thread begins execution in this function.
-
 
96
 * Then the function implementing the pseudo thread logic is called.
-
 
97
 * After its return, the return value is saved for a potentional
-
 
98
 * joiner. If the joiner exists, it is woken up. The pseudo thread
-
 
99
 * then switches to another pseudo thread, which cleans up after it.
-
 
100
 */
93
void psthread_main(void)
101
void psthread_main(void)
94
{
102
{
95
    psthread_data_t *pt = __tcb_get()->pst_data;
103
    psthread_data_t *pt = __tcb_get()->pst_data;
96
 
104
 
97
    pt->retval = pt->func(pt->arg);
105
    pt->retval = pt->func(pt->arg);
98
 
106
 
-
 
107
    /*
-
 
108
     * If there is a joiner, wake it up and save our return value.
99
    pt->finished = 1;
109
     */
100
    if (pt->waiter)
110
    if (pt->joiner) {
101
        list_append(&pt->waiter->link, &ready_list);
111
        list_append(&pt->joiner->link, &ready_list);
-
 
112
        pt->joiner->joinee_retval = pt->retval;
-
 
113
    }
102
 
114
 
103
    psthread_schedule_next_adv(PS_FROM_DEAD);
115
    psthread_schedule_next_adv(PS_FROM_DEAD);
-
 
116
    /* not reached */
104
}
117
}
105
 
118
 
106
/** Schedule next userspace pseudo thread.
119
/** Schedule next userspace pseudo thread.
107
 *
120
 *
108
 * If calling with PS_TO_MANAGER parameter, the async_futex should be
121
 * If calling with PS_TO_MANAGER parameter, the async_futex should be
109
 * held.
122
 * held.
110
 *
123
 *
-
 
124
 * @param ctype     One of PS_SLEEP, PS_PREEMPT, PS_TO_MANAGER,
-
 
125
 *          PS_FROM_MANAGER, PS_FROM_DEAD. The parameter describes
111
 * @param ctype Type of switch.
126
 *          the circumstances of the switch.
112
 * @return 0 if there is no ready pseudo thread, 1 otherwise.
127
 * @return      Return 0 if there is no ready pseudo thread,
-
 
128
 *          return 1 otherwise.
113
 */
129
 */
114
int psthread_schedule_next_adv(pschange_type ctype)
130
int psthread_schedule_next_adv(pschange_type ctype)
115
{
131
{
116
    psthread_data_t *srcpt, *dstpt;
132
    psthread_data_t *srcpt, *dstpt;
117
    int retval = 0;
133
    int retval = 0;
118
   
134
   
119
    futex_down(&psthread_futex);
135
    futex_down(&psthread_futex);
120
 
136
 
121
    if (ctype == PS_PREEMPT && list_empty(&ready_list))
137
    if (ctype == PS_PREEMPT && list_empty(&ready_list))
122
        goto ret_0;
138
        goto ret_0;
-
 
139
    if (ctype == PS_SLEEP) {
-
 
140
        if (list_empty(&ready_list) && list_empty(&serialized_list))
-
 
141
            goto ret_0;
-
 
142
    }
123
 
143
 
124
    if (ctype == PS_FROM_MANAGER) {
144
    if (ctype == PS_FROM_MANAGER) {
125
        if (list_empty(&ready_list) && list_empty(&serialized_list))
145
        if (list_empty(&ready_list) && list_empty(&serialized_list))
126
            goto ret_0;
146
            goto ret_0;
-
 
147
        /*
127
        /* Do not preempt if there is not sufficient count of thread managers */
148
         * Do not preempt if there is not sufficient count of thread
-
 
149
         * managers.
-
 
150
         */
128
        if (list_empty(&serialized_list) && threads_in_manager <= serialized_threads) {
151
        if (list_empty(&serialized_list) && threads_in_manager <=
-
 
152
            serialized_threads) {
129
            goto ret_0;
153
            goto ret_0;
130
        }
154
        }
131
    }
155
    }
132
    /* If we are going to manager and none exists, create it */
156
    /* If we are going to manager and none exists, create it */
133
    if (ctype == PS_TO_MANAGER || ctype == PS_FROM_DEAD) {
157
    if (ctype == PS_TO_MANAGER || ctype == PS_FROM_DEAD) {
Line 136... Line 160...
136
            async_create_manager();
160
            async_create_manager();
137
            futex_down(&psthread_futex);
161
            futex_down(&psthread_futex);
138
        }
162
        }
139
    }
163
    }
140
   
164
   
-
 
165
    srcpt = __tcb_get()->pst_data;
141
    if (ctype != PS_FROM_DEAD) {
166
    if (ctype != PS_FROM_DEAD) {
142
        /* Save current state */
167
        /* Save current state */
143
        srcpt = __tcb_get()->pst_data;
-
 
144
        if (!context_save(&srcpt->ctx)) {
168
        if (!context_save(&srcpt->ctx)) {
145
            if (serialization_count)
169
            if (serialization_count)
146
                srcpt->flags &= ~PSTHREAD_SERIALIZED;
170
                srcpt->flags &= ~PSTHREAD_SERIALIZED;
-
 
171
            if (srcpt->clean_after_me) {
-
 
172
                /*
-
 
173
                 * Cleanup after the dead pseudo thread from
-
 
174
                 * which we restored context here.
-
 
175
                 */
-
 
176
                free(srcpt->clean_after_me->stack);
-
 
177
                psthread_teardown(srcpt->clean_after_me);
-
 
178
                srcpt->clean_after_me = NULL;
-
 
179
            }
147
            return 1; // futex_up already done here
180
            return 1;   /* futex_up already done here */
148
        }
181
        }
149
 
182
 
150
        /* Save myself to correct run list */
183
        /* Save myself to the correct run list */
151
        if (ctype == PS_PREEMPT)
184
        if (ctype == PS_PREEMPT)
152
            list_append(&srcpt->link, &ready_list);
185
            list_append(&srcpt->link, &ready_list);
153
        else if (ctype == PS_FROM_MANAGER) {
186
        else if (ctype == PS_FROM_MANAGER) {
154
            list_append(&srcpt->link, &manager_list);
187
            list_append(&srcpt->link, &manager_list);
155
            threads_in_manager--;
188
            threads_in_manager--;
-
 
189
        } else {   
-
 
190
            /*
156
        } /* If ctype == PS_TO_MANAGER, don't save ourselves to any list, we should
191
             * If ctype == PS_TO_MANAGER, don't save ourselves to
157
           * already be somewhere, or we will be lost */
192
             * any list, we should already be somewhere, or we will
158
    } else
193
             * be lost.
-
 
194
             *
-
 
195
             * The ctype == PS_SLEEP case is similar. The pseudo
159
        srcpt = NULL; /* Avoid GCC warning, if ctype == PS_FROM_DEAD, srcpt is not used */
196
             * thread has an external refernce which can be used to
-
 
197
             * wake it up once that time has come.
-
 
198
             */
-
 
199
        }
-
 
200
    }
160
 
201
 
161
    /* Choose new thread to run */
202
    /* Choose new thread to run */
162
    if (ctype == PS_TO_MANAGER || ctype == PS_FROM_DEAD) {
203
    if (ctype == PS_TO_MANAGER || ctype == PS_FROM_DEAD) {
163
        dstpt = list_get_instance(manager_list.next,psthread_data_t, link);
204
        dstpt = list_get_instance(manager_list.next, psthread_data_t,
-
 
205
            link);
164
        if (serialization_count && ctype == PS_TO_MANAGER) {
206
        if (serialization_count && ctype == PS_TO_MANAGER) {
165
            serialized_threads++;
207
            serialized_threads++;
166
            srcpt->flags |= PSTHREAD_SERIALIZED;
208
            srcpt->flags |= PSTHREAD_SERIALIZED;
167
        }
209
        }
168
        threads_in_manager++;
210
        threads_in_manager++;
-
 
211
 
-
 
212
        if (ctype == PS_FROM_DEAD)
-
 
213
            dstpt->clean_after_me = srcpt;
169
    } else {
214
    } else {
170
        if (!list_empty(&serialized_list)) {
215
        if (!list_empty(&serialized_list)) {
171
            dstpt = list_get_instance(serialized_list.next, psthread_data_t, link);
216
            dstpt = list_get_instance(serialized_list.next,
-
 
217
                psthread_data_t, link);
172
            serialized_threads--;
218
            serialized_threads--;
173
        } else
219
        } else {
174
            dstpt = list_get_instance(ready_list.next, psthread_data_t, link);
220
            dstpt = list_get_instance(ready_list.next,
-
 
221
                psthread_data_t, link);
-
 
222
        }
175
    }
223
    }
176
    list_remove(&dstpt->link);
224
    list_remove(&dstpt->link);
177
 
225
 
178
    futex_up(&psthread_futex);
226
    futex_up(&psthread_futex);
179
    context_restore(&dstpt->ctx);
227
    context_restore(&dstpt->ctx);
-
 
228
    /* not reached */
180
 
229
 
181
ret_0:
230
ret_0:
182
    futex_up(&psthread_futex);
231
    futex_up(&psthread_futex);
183
    return retval;
232
    return retval;
184
}
233
}
185
 
234
 
186
/** Wait for uspace pseudo thread to finish.
235
/** Wait for uspace pseudo thread to finish.
187
 *
236
 *
-
 
237
 * Each pseudo thread can be only joined by one other pseudo thread. Moreover,
-
 
238
 * the joiner must be from the same thread as the joinee.
-
 
239
 *
188
 * @param psthrid Pseudo thread to wait for.
240
 * @param psthrid   Pseudo thread to join.
189
 *
241
 *
190
 * @return Value returned by the finished thread.
242
 * @return      Value returned by the finished thread.
191
 */
243
 */
192
int psthread_join(pstid_t psthrid)
244
int psthread_join(pstid_t psthrid)
193
{
245
{
194
    volatile psthread_data_t *pt;
246
    psthread_data_t *pt;
195
    volatile int retval;
247
    psthread_data_t *cur;
196
 
248
 
197
    /* Handle psthrid = Kernel address -> it is wait for call */
249
    /* Handle psthrid = Kernel address -> it is wait for call */
198
    pt = (psthread_data_t *) psthrid;
250
    pt = (psthread_data_t *) psthrid;
199
 
251
 
200
    /* TODO */
252
    /*
201
    printf("join unsupported\n");
253
     * The joiner is running so the joinee isn't.
202
    _exit(1);
-
 
203
 
254
     */
204
    retval = pt->retval;
255
    cur = __tcb_get()->pst_data;
205
 
-
 
206
    free(pt->stack);
256
    pt->joiner = cur;
207
    psthread_teardown((void *)pt);
257
    psthread_schedule_next_adv(PS_SLEEP);
208
 
258
 
-
 
259
    /*
-
 
260
     * The joinee fills in the return value.
-
 
261
     */
209
    return retval;
262
    return cur->joinee_retval;
210
}
263
}
211
 
264
 
212
/** Create a userspace pseudo thread.
265
/** Create a userspace pseudo thread.
213
 *
266
 *
214
 * @param func Pseudo thread function.
267
 * @param func      Pseudo thread function.
215
 * @param arg Argument to pass to func.
268
 * @param arg       Argument to pass to func.
216
 *
269
 *
217
 * @return 0 on failure, TLS of the new pseudo thread.
270
 * @return      Return 0 on failure or TLS of the new pseudo thread.
218
 */
271
 */
219
pstid_t psthread_create(int (*func)(void *), void *arg)
272
pstid_t psthread_create(int (*func)(void *), void *arg)
220
{
273
{
221
    psthread_data_t *pt;
274
    psthread_data_t *pt;
222
 
275
 
223
    pt = psthread_setup();
276
    pt = psthread_setup();
224
    if (!pt)
277
    if (!pt)
225
        return 0;
278
        return 0;
226
    pt->stack = (char *) malloc(PSTHREAD_INITIAL_STACK_PAGES_NO*getpagesize());
279
    pt->stack = (char *) malloc(PSTHREAD_INITIAL_STACK_PAGES_NO *
-
 
280
        getpagesize());
227
 
281
 
228
    if (!pt->stack) {
282
    if (!pt->stack) {
229
        psthread_teardown(pt);
283
        psthread_teardown(pt);
230
        return 0;
284
        return 0;
231
    }
285
    }
232
 
286
 
233
    pt->arg= arg;
287
    pt->arg= arg;
234
    pt->func = func;
288
    pt->func = func;
235
    pt->finished = 0;
289
    pt->clean_after_me = NULL;
236
    pt->waiter = NULL;
290
    pt->joiner = NULL;
-
 
291
    pt->joinee_retval = 0;
-
 
292
    pt->retval = 0;
237
    pt->flags = 0;
293
    pt->flags = 0;
238
 
294
 
239
    context_save(&pt->ctx);
295
    context_save(&pt->ctx);
-
 
296
    context_set(&pt->ctx, FADDR(psthread_main), pt->stack,
240
    context_set(&pt->ctx, FADDR(psthread_main), pt->stack, PSTHREAD_INITIAL_STACK_PAGES_NO*getpagesize(), pt->tcb);
297
        PSTHREAD_INITIAL_STACK_PAGES_NO * getpagesize(), pt->tcb);
241
 
298
 
242
    return (pstid_t )pt;
299
    return (pstid_t) pt;
243
}
300
}
244
 
301
 
245
/** Add a thread to ready list */
302
/** Add a thread to the ready list.
-
 
303
 *
-
 
304
 * @param psthrid       Pinter to the pseudo thread structure of the
-
 
305
 *              pseudo thread to be added.
-
 
306
 */
246
void psthread_add_ready(pstid_t psthrid)
307
void psthread_add_ready(pstid_t psthrid)
247
{
308
{
248
    psthread_data_t *pt;
309
    psthread_data_t *pt;
249
 
310
 
250
    pt = (psthread_data_t *) psthrid;
311
    pt = (psthread_data_t *) psthrid;
Line 254... Line 315...
254
    else
315
    else
255
        list_append(&pt->link, &ready_list);
316
        list_append(&pt->link, &ready_list);
256
    futex_up(&psthread_futex);
317
    futex_up(&psthread_futex);
257
}
318
}
258
 
319
 
259
/** Add a thread to manager list */
320
/** Add a pseudo thread to the manager list.
-
 
321
 *
-
 
322
 * @param psthrid       Pinter to the pseudo thread structure of the
-
 
323
 *              pseudo thread to be added.
-
 
324
 */
260
void psthread_add_manager(pstid_t psthrid)
325
void psthread_add_manager(pstid_t psthrid)
261
{
326
{
262
    psthread_data_t *pt;
327
    psthread_data_t *pt;
263
 
328
 
264
    pt = (psthread_data_t *) psthrid;
329
    pt = (psthread_data_t *) psthrid;
Line 267... Line 332...
267
    list_append(&pt->link, &manager_list);
332
    list_append(&pt->link, &manager_list);
268
    futex_up(&psthread_futex);
333
    futex_up(&psthread_futex);
269
}
334
}
270
 
335
 
271
/** Remove one manager from manager list */
336
/** Remove one manager from manager list */
272
void psthread_remove_manager()
337
void psthread_remove_manager(void)
273
{
338
{
274
    futex_down(&psthread_futex);
339
    futex_down(&psthread_futex);
275
    if (list_empty(&manager_list)) {
340
    if (list_empty(&manager_list)) {
276
        futex_up(&psthread_futex);
341
        futex_up(&psthread_futex);
277
        return;
342
        return;
278
    }
343
    }
279
    list_remove(manager_list.next);
344
    list_remove(manager_list.next);
280
    futex_up(&psthread_futex);
345
    futex_up(&psthread_futex);
281
}
346
}
282
 
347
 
283
/** Return thread id of current running thread */
348
/** Return thread id of the currently running thread.
-
 
349
 *
-
 
350
 * @return      Pseudo thread ID of the currently running pseudo thread.
-
 
351
 */
284
pstid_t psthread_get_id(void)
352
pstid_t psthread_get_id(void)
285
{
353
{
286
    return (pstid_t)__tcb_get()->pst_data;
354
    return (pstid_t) __tcb_get()->pst_data;
287
}
355
}
288
 
356
 
289
/** Disable preemption
357
/** Disable preemption
290
 *
358
 *
291
 * If the thread wants to send several message in row and does not want
359
 * If the thread wants to send several message in a row and does not want to be
292
 * to be preempted, it should start async_serialize_start() in the beginning
360
 * preempted, it should start async_serialize_start() in the beginning of
293
 * of communication and async_serialize_end() in the end. If it is a
361
 * communication and async_serialize_end() in the end. If it is a true
294
 * true multithreaded application, it should protect the communication channel
362
 * multithreaded application, it should protect the communication channel by a
295
 * by a futex as well. Interrupt messages will can still be preempted.
363
 * futex as well. Interrupt messages can still be preempted.
296
 */
364
 */
297
void psthread_inc_sercount(void)
365
void psthread_inc_sercount(void)
298
{
366
{
299
    serialization_count++;
367
    serialization_count++;
300
}
368
}
301
 
369
 
-
 
370
/** Restore the preemption counter to the previous state. */
302
void psthread_dec_sercount(void)
371
void psthread_dec_sercount(void)
303
{
372
{
304
    serialization_count--;
373
    serialization_count--;
305
}
374
}
306
 
375
 
307
/** @}
376
/** @}
308
 */
377
 */
-
 
378