Subversion Repositories HelenOS

Rev

Rev 2479 | Rev 2482 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1113 palkovsky 1
/*
2071 jermar 2
 * Copyright (c) 2006 Ondrej Palkovsky
2481 jermar 3
 * Copyright (c) 2007 Jakub Jermar
1113 palkovsky 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
 
1719 decky 30
/** @addtogroup libc
1653 cejka 31
 * @{
32
 */
33
/** @file
34
 */
35
 
1113 palkovsky 36
#include <libadt/list.h>
37
#include <psthread.h>
38
#include <malloc.h>
39
#include <unistd.h>
40
#include <thread.h>
41
#include <stdio.h>
1781 jermar 42
#include <libarch/faddr.h>
1392 palkovsky 43
#include <futex.h>
44
#include <assert.h>
1407 palkovsky 45
#include <async.h>
1113 palkovsky 46
 
1155 vana 47
#ifndef PSTHREAD_INITIAL_STACK_PAGES_NO
2481 jermar 48
#define PSTHREAD_INITIAL_STACK_PAGES_NO 1
1155 vana 49
#endif
50
 
1113 palkovsky 51
static LIST_INITIALIZE(ready_list);
1610 palkovsky 52
static LIST_INITIALIZE(serialized_list);
1392 palkovsky 53
static LIST_INITIALIZE(manager_list);
1113 palkovsky 54
 
1128 jermar 55
static void psthread_main(void);
1125 jermar 56
 
1392 palkovsky 57
static atomic_t psthread_futex = FUTEX_INITIALIZER;
1610 palkovsky 58
/** Count of real threads that are in async_serialized mode */
2481 jermar 59
static int serialized_threads;  /* Protected by async_futex */
1610 palkovsky 60
/** Thread-local count of serialization. If >0, we must not preempt */
1614 palkovsky 61
static __thread int serialization_count;
1610 palkovsky 62
/** Counter of threads residing in async_manager */
63
static int threads_in_manager;
1392 palkovsky 64
 
1868 jermar 65
/** Setup psthread information into TCB structure */
2481 jermar 66
psthread_data_t *psthread_setup(void)
1129 palkovsky 67
{
68
    psthread_data_t *pt;
1392 palkovsky 69
    tcb_t *tcb;
1129 palkovsky 70
 
1392 palkovsky 71
    tcb = __make_tls();
72
    if (!tcb)
73
        return NULL;
74
 
1129 palkovsky 75
    pt = malloc(sizeof(*pt));
76
    if (!pt) {
1392 palkovsky 77
        __free_tls(tcb);
1129 palkovsky 78
        return NULL;
79
    }
80
 
81
    tcb->pst_data = pt;
82
    pt->tcb = tcb;
83
 
84
    return pt;
85
}
86
 
87
void psthread_teardown(psthread_data_t *pt)
88
{
1392 palkovsky 89
    __free_tls(pt->tcb);
1129 palkovsky 90
    free(pt);
91
}
92
 
2481 jermar 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
 */
1128 jermar 101
void psthread_main(void)
1113 palkovsky 102
{
1129 palkovsky 103
    psthread_data_t *pt = __tcb_get()->pst_data;
104
 
1113 palkovsky 105
    pt->retval = pt->func(pt->arg);
106
 
2481 jermar 107
    /*
108
     * If there is a joiner, wake it up and save our return value.
109
     */
110
    if (pt->joiner) {
111
        list_append(&pt->joiner->link, &ready_list);
112
        pt->joiner->joinee_retval = pt->retval;
113
    }
1113 palkovsky 114
 
1610 palkovsky 115
    psthread_schedule_next_adv(PS_FROM_DEAD);
2481 jermar 116
    /* not reached */
1113 palkovsky 117
}
118
 
1128 jermar 119
/** Schedule next userspace pseudo thread.
120
 *
1427 palkovsky 121
 * If calling with PS_TO_MANAGER parameter, the async_futex should be
122
 * held.
123
 *
2481 jermar 124
 * @param ctype     One of PS_SLEEP, PS_PREEMPT, PS_TO_MANAGER,
125
 *          PS_FROM_MANAGER, PS_FROM_DEAD. The parameter describes
126
 *          the circumstances of the switch.
127
 * @return      Return 0 if there is no ready pseudo thread,
128
 *          return 1 otherwise.
1128 jermar 129
 */
1392 palkovsky 130
int psthread_schedule_next_adv(pschange_type ctype)
1113 palkovsky 131
{
1610 palkovsky 132
    psthread_data_t *srcpt, *dstpt;
1392 palkovsky 133
    int retval = 0;
134
 
135
    futex_down(&psthread_futex);
1113 palkovsky 136
 
1392 palkovsky 137
    if (ctype == PS_PREEMPT && list_empty(&ready_list))
138
        goto ret_0;
2481 jermar 139
    if (ctype == PS_SLEEP) {
140
        if (list_empty(&ready_list) && list_empty(&serialized_list))
141
            goto ret_0;
142
    }
1113 palkovsky 143
 
1610 palkovsky 144
    if (ctype == PS_FROM_MANAGER) {
145
        if (list_empty(&ready_list) && list_empty(&serialized_list))
146
            goto ret_0;
2481 jermar 147
        /*
148
         * Do not preempt if there is not sufficient count of thread
149
         * managers.
150
         */
151
        if (list_empty(&serialized_list) && threads_in_manager <=
152
            serialized_threads) {
1610 palkovsky 153
            goto ret_0;
154
        }
1392 palkovsky 155
    }
1407 palkovsky 156
    /* If we are going to manager and none exists, create it */
1610 palkovsky 157
    if (ctype == PS_TO_MANAGER || ctype == PS_FROM_DEAD) {
158
        while (list_empty(&manager_list)) {
159
            futex_up(&psthread_futex);
160
            async_create_manager();
161
            futex_down(&psthread_futex);
162
        }
1427 palkovsky 163
    }
1610 palkovsky 164
 
2481 jermar 165
    srcpt = __tcb_get()->pst_data;
1610 palkovsky 166
    if (ctype != PS_FROM_DEAD) {
167
        /* Save current state */
168
        if (!context_save(&srcpt->ctx)) {
169
            if (serialization_count)
170
                srcpt->flags &= ~PSTHREAD_SERIALIZED;
2481 jermar 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
            }
180
            return 1;   /* futex_up already done here */
1610 palkovsky 181
        }
1392 palkovsky 182
 
2481 jermar 183
        /* Save myself to the correct run list */
1610 palkovsky 184
        if (ctype == PS_PREEMPT)
185
            list_append(&srcpt->link, &ready_list);
186
        else if (ctype == PS_FROM_MANAGER) {
187
            list_append(&srcpt->link, &manager_list);
188
            threads_in_manager--;
2481 jermar 189
        } else {   
190
            /*
191
             * If ctype == PS_TO_MANAGER, don't save ourselves to
192
             * any list, we should already be somewhere, or we will
193
             * be lost.
194
             *
195
             * The ctype == PS_SLEEP case is similar. The pseudo
196
             * thread has an external refernce which can be used to
197
             * wake it up once that time has come.
198
             */
199
        }
200
    }
1392 palkovsky 201
 
1610 palkovsky 202
    /* Choose new thread to run */
203
    if (ctype == PS_TO_MANAGER || ctype == PS_FROM_DEAD) {
2481 jermar 204
        dstpt = list_get_instance(manager_list.next, psthread_data_t,
205
            link);
1610 palkovsky 206
        if (serialization_count && ctype == PS_TO_MANAGER) {
207
            serialized_threads++;
208
            srcpt->flags |= PSTHREAD_SERIALIZED;
209
        }
210
        threads_in_manager++;
2481 jermar 211
 
212
        if (ctype == PS_FROM_DEAD)
213
            dstpt->clean_after_me = srcpt;
1610 palkovsky 214
    } else {
215
        if (!list_empty(&serialized_list)) {
2481 jermar 216
            dstpt = list_get_instance(serialized_list.next,
217
                psthread_data_t, link);
1610 palkovsky 218
            serialized_threads--;
2481 jermar 219
        } else {
220
            dstpt = list_get_instance(ready_list.next,
221
                psthread_data_t, link);
222
        }
1610 palkovsky 223
    }
224
    list_remove(&dstpt->link);
1113 palkovsky 225
 
1392 palkovsky 226
    futex_up(&psthread_futex);
1610 palkovsky 227
    context_restore(&dstpt->ctx);
2481 jermar 228
    /* not reached */
1392 palkovsky 229
 
230
ret_0:
231
    futex_up(&psthread_futex);
232
    return retval;
1113 palkovsky 233
}
234
 
1128 jermar 235
/** Wait for uspace pseudo thread to finish.
236
 *
2481 jermar 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.
1128 jermar 239
 *
2481 jermar 240
 * @param psthrid   Pseudo thread to join.
241
 *
242
 * @return      Value returned by the finished thread.
1128 jermar 243
 */
244
int psthread_join(pstid_t psthrid)
1113 palkovsky 245
{
2481 jermar 246
    psthread_data_t *pt;
247
    psthread_data_t *cur;
1113 palkovsky 248
 
249
    /* Handle psthrid = Kernel address -> it is wait for call */
250
    pt = (psthread_data_t *) psthrid;
251
 
2481 jermar 252
    /*
253
     * The joiner is running so the joinee isn't.
254
     */
255
    cur = __tcb_get()->pst_data;
256
    pt->joiner = cur;
257
    psthread_schedule_next_adv(PS_SLEEP);
1610 palkovsky 258
 
2481 jermar 259
    /*
260
     * The joinee fills in the return value.
261
     */
262
    return cur->joinee_retval;
1113 palkovsky 263
}
264
 
1868 jermar 265
/** Create a userspace pseudo thread.
1113 palkovsky 266
 *
2481 jermar 267
 * @param func      Pseudo thread function.
268
 * @param arg       Argument to pass to func.
1128 jermar 269
 *
2481 jermar 270
 * @return      Return 0 on failure or TLS of the new pseudo thread.
1113 palkovsky 271
 */
272
pstid_t psthread_create(int (*func)(void *), void *arg)
273
{
274
    psthread_data_t *pt;
275
 
1392 palkovsky 276
    pt = psthread_setup();
277
    if (!pt)
1129 palkovsky 278
        return 0;
2481 jermar 279
    pt->stack = (char *) malloc(PSTHREAD_INITIAL_STACK_PAGES_NO *
280
        getpagesize());
1113 palkovsky 281
 
282
    if (!pt->stack) {
1129 palkovsky 283
        psthread_teardown(pt);
1113 palkovsky 284
        return 0;
285
    }
286
 
287
    pt->arg= arg;
288
    pt->func = func;
2481 jermar 289
    pt->clean_after_me = NULL;
290
    pt->joiner = NULL;
291
    pt->joinee_retval = 0;
292
    pt->retval = 0;
1610 palkovsky 293
    pt->flags = 0;
1113 palkovsky 294
 
295
    context_save(&pt->ctx);
2481 jermar 296
    context_set(&pt->ctx, FADDR(psthread_main), pt->stack,
297
        PSTHREAD_INITIAL_STACK_PAGES_NO * getpagesize(), pt->tcb);
1113 palkovsky 298
 
2481 jermar 299
    return (pstid_t) pt;
1392 palkovsky 300
}
301
 
2481 jermar 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
 */
1392 palkovsky 307
void psthread_add_ready(pstid_t psthrid)
308
{
309
    psthread_data_t *pt;
310
 
311
    pt = (psthread_data_t *) psthrid;
312
    futex_down(&psthread_futex);
1610 palkovsky 313
    if ((pt->flags & PSTHREAD_SERIALIZED))
314
        list_append(&pt->link, &serialized_list);
315
    else
316
        list_append(&pt->link, &ready_list);
1392 palkovsky 317
    futex_up(&psthread_futex);
318
}
1113 palkovsky 319
 
2481 jermar 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
 */
1392 palkovsky 325
void psthread_add_manager(pstid_t psthrid)
326
{
327
    psthread_data_t *pt;
328
 
329
    pt = (psthread_data_t *) psthrid;
330
 
331
    futex_down(&psthread_futex);
332
    list_append(&pt->link, &manager_list);
333
    futex_up(&psthread_futex);
1113 palkovsky 334
}
1392 palkovsky 335
 
336
/** Remove one manager from manager list */
2481 jermar 337
void psthread_remove_manager(void)
1392 palkovsky 338
{
339
    futex_down(&psthread_futex);
340
    if (list_empty(&manager_list)) {
341
        futex_up(&psthread_futex);
342
        return;
343
    }
344
    list_remove(manager_list.next);
345
    futex_up(&psthread_futex);
346
}
1427 palkovsky 347
 
2481 jermar 348
/** Return thread id of the currently running thread.
349
 *
350
 * @return      Pseudo thread ID of the currently running pseudo thread.
351
 */
1427 palkovsky 352
pstid_t psthread_get_id(void)
353
{
2481 jermar 354
    return (pstid_t) __tcb_get()->pst_data;
1427 palkovsky 355
}
1610 palkovsky 356
 
357
/** Disable preemption
358
 *
2481 jermar 359
 * If the thread wants to send several message in a row and does not want to be
360
 * preempted, it should start async_serialize_start() in the beginning of
361
 * communication and async_serialize_end() in the end. If it is a true
362
 * multithreaded application, it should protect the communication channel by a
363
 * futex as well. Interrupt messages can still be preempted.
1610 palkovsky 364
 */
365
void psthread_inc_sercount(void)
366
{
367
    serialization_count++;
368
}
369
 
2481 jermar 370
/** Restore the preemption counter to the previous state. */
1610 palkovsky 371
void psthread_dec_sercount(void)
372
{
373
    serialization_count--;
374
}
1653 cejka 375
 
1719 decky 376
/** @}
1653 cejka 377
 */
2481 jermar 378