Changeset b9641ee in mainline


Ignore:
Timestamp:
2007-06-27T23:12:25Z (18 years ago)
Author:
Jakub Jermar <jakub@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
bc1f1c2
Parents:
72381f1
Message:
  1. Implement simple joining of pseudo threads. Only one pseudo thread is allowed to join another pseudo

thread. The restriction is that both pseudo threads must be from the same thread (this is to simplify
synchronization). The joiner doesn't free anything from the joinee. It only gets its return value. Not
tested thoroughly yet.

  1. Cleanup of a dead pseudo thread is done by the next scheduled pseudo thread. Not tested thoroughly yet.
Location:
uspace/lib/libc
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/libc/generic/psthread.c

    r72381f1 rb9641ee  
    11/*
    22 * Copyright (c) 2006 Ondrej Palkovsky
     3 * Copyright (c) 2007 Jakub Jermar
    34 * All rights reserved.
    45 *
     
    4546
    4647#ifndef PSTHREAD_INITIAL_STACK_PAGES_NO
    47 #define PSTHREAD_INITIAL_STACK_PAGES_NO 1
     48#define PSTHREAD_INITIAL_STACK_PAGES_NO 1
    4849#endif
    4950
     
    5657static atomic_t psthread_futex = FUTEX_INITIALIZER;
    5758/** Count of real threads that are in async_serialized mode */
    58 static int serialized_threads; /* Protected by async_futex */
     59static int serialized_threads;  /* Protected by async_futex */
    5960/** Thread-local count of serialization. If >0, we must not preempt */
    6061static __thread int serialization_count;
     
    6364
    6465/** Setup psthread information into TCB structure */
    65 psthread_data_t * psthread_setup()
     66psthread_data_t *psthread_setup(void)
    6667{
    6768        psthread_data_t *pt;
     
    9091}
    9192
    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 */
    93101void psthread_main(void)
    94102{
     
    97105        pt->retval = pt->func(pt->arg);
    98106
    99         pt->finished = 1;
    100         if (pt->waiter)
    101                 list_append(&pt->waiter->link, &ready_list);
     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        }
    102114
    103115        psthread_schedule_next_adv(PS_FROM_DEAD);
     116        /* not reached */
    104117}
    105118
     
    109122 * held.
    110123 *
    111  * @param ctype Type of switch.
    112  * @return 0 if there is no ready pseudo thread, 1 otherwise.
     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.
    113129 */
    114130int psthread_schedule_next_adv(pschange_type ctype)
     
    121137        if (ctype == PS_PREEMPT && list_empty(&ready_list))
    122138                goto ret_0;
     139        if (ctype == PS_SLEEP) {
     140                if (list_empty(&ready_list) && list_empty(&serialized_list))
     141                        goto ret_0;
     142        }
    123143
    124144        if (ctype == PS_FROM_MANAGER) {
    125145                if (list_empty(&ready_list) && list_empty(&serialized_list))
    126146                        goto ret_0;
    127                 /* Do not preempt if there is not sufficient count of thread managers */
    128                 if (list_empty(&serialized_list) && threads_in_manager <= serialized_threads) {
     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) {
    129153                        goto ret_0;
    130154                }
     
    139163        }
    140164       
     165        srcpt = __tcb_get()->pst_data;
    141166        if (ctype != PS_FROM_DEAD) {
    142167                /* Save current state */
    143                 srcpt = __tcb_get()->pst_data;
    144168                if (!context_save(&srcpt->ctx)) {
    145169                        if (serialization_count)
    146170                                srcpt->flags &= ~PSTHREAD_SERIALIZED;
    147                         return 1; // futex_up already done here
    148                 }
    149 
    150                 /* Save myself to correct run list */
     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 */
     181                }
     182
     183                /* Save myself to the correct run list */
    151184                if (ctype == PS_PREEMPT)
    152185                        list_append(&srcpt->link, &ready_list);
     
    154187                        list_append(&srcpt->link, &manager_list);
    155188                        threads_in_manager--;
    156                 } /* If ctype == PS_TO_MANAGER, don't save ourselves to any list, we should
    157                    * already be somewhere, or we will be lost */
    158         } else
    159                 srcpt = NULL; /* Avoid GCC warning, if ctype == PS_FROM_DEAD, srcpt is not used */
     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        }
    160201
    161202        /* Choose new thread to run */
    162203        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);
    164206                if (serialization_count && ctype == PS_TO_MANAGER) {
    165207                        serialized_threads++;
     
    167209                }
    168210                threads_in_manager++;
     211
     212                if (ctype == PS_FROM_DEAD)
     213                        dstpt->clean_after_me = srcpt;
    169214        } else {
    170215                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);
    172218                        serialized_threads--;
    173                 } else
    174                         dstpt = list_get_instance(ready_list.next, psthread_data_t, link);
     219                } else {
     220                        dstpt = list_get_instance(ready_list.next,
     221                            psthread_data_t, link);
     222                }
    175223        }
    176224        list_remove(&dstpt->link);
     
    178226        futex_up(&psthread_futex);
    179227        context_restore(&dstpt->ctx);
     228        /* not reached */
    180229
    181230ret_0:
     
    186235/** Wait for uspace pseudo thread to finish.
    187236 *
    188  * @param psthrid Pseudo thread to wait for.
    189  *
    190  * @return Value returned by the finished thread.
     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 *
     240 * @param psthrid       Pseudo thread to join.
     241 *
     242 * @return              Value returned by the finished thread.
    191243 */
    192244int psthread_join(pstid_t psthrid)
    193245{
    194         volatile psthread_data_t *pt;
    195         volatile int retval;
     246        psthread_data_t *pt;
     247        psthread_data_t *cur;
    196248
    197249        /* Handle psthrid = Kernel address -> it is wait for call */
    198250        pt = (psthread_data_t *) psthrid;
    199251
    200         /* TODO */
    201         printf("join unsupported\n");
    202         _exit(1);
    203 
    204         retval = pt->retval;
    205 
    206         free(pt->stack);
    207         psthread_teardown((void *)pt);
    208 
    209         return retval;
     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);
     258
     259        /*
     260         * The joinee fills in the return value.
     261         */
     262        return cur->joinee_retval;
    210263}
    211264
    212265/** Create a userspace pseudo thread.
    213266 *
    214  * @param func Pseudo thread function.
    215  * @param arg Argument to pass to func.
    216  *
    217  * @return 0 on failure, TLS of the new pseudo thread.
     267 * @param func          Pseudo thread function.
     268 * @param arg           Argument to pass to func.
     269 *
     270 * @return              Return 0 on failure or TLS of the new pseudo thread.
    218271 */
    219272pstid_t psthread_create(int (*func)(void *), void *arg)
     
    224277        if (!pt)
    225278                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());
    227281
    228282        if (!pt->stack) {
     
    233287        pt->arg= arg;
    234288        pt->func = func;
    235         pt->finished = 0;
    236         pt->waiter = NULL;
     289        pt->clean_after_me = NULL;
     290        pt->joiner = NULL;
     291        pt->joinee_retval = 0;
     292        pt->retval = 0;
    237293        pt->flags = 0;
    238294
    239295        context_save(&pt->ctx);
    240         context_set(&pt->ctx, FADDR(psthread_main), pt->stack, PSTHREAD_INITIAL_STACK_PAGES_NO*getpagesize(), pt->tcb);
    241 
    242         return (pstid_t )pt;
    243 }
    244 
    245 /** Add a thread to ready list */
     296        context_set(&pt->ctx, FADDR(psthread_main), pt->stack,
     297            PSTHREAD_INITIAL_STACK_PAGES_NO * getpagesize(), pt->tcb);
     298
     299        return (pstid_t) pt;
     300}
     301
     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 */
    246307void psthread_add_ready(pstid_t psthrid)
    247308{
     
    257318}
    258319
    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 */
    260325void psthread_add_manager(pstid_t psthrid)
    261326{
     
    270335
    271336/** Remove one manager from manager list */
    272 void psthread_remove_manager()
     337void psthread_remove_manager(void)
    273338{
    274339        futex_down(&psthread_futex);
     
    281346}
    282347
    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 */
    284352pstid_t psthread_get_id(void)
    285353{
    286         return (pstid_t)__tcb_get()->pst_data;
     354        return (pstid_t) __tcb_get()->pst_data;
    287355}
    288356
    289357/** Disable preemption
    290358 *
    291  * If the thread wants to send several message in row and does not want
    292  * to be preempted, it should start async_serialize_start() in the beginning
    293  * of communication and async_serialize_end() in the end. If it is a
    294  * true multithreaded application, it should protect the communication channel
    295  * by a futex as well. Interrupt messages will can still be preempted.
     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.
    296364 */
    297365void psthread_inc_sercount(void)
     
    300368}
    301369
     370/** Restore the preemption counter to the previous state. */
    302371void psthread_dec_sercount(void)
    303372{
     
    307376/** @}
    308377 */
     378
  • uspace/lib/libc/include/psthread.h

    r72381f1 rb9641ee  
    5050
    5151typedef enum {
     52        PS_SLEEP,
     53        PS_PREEMPT,
    5254        PS_TO_MANAGER,
    5355        PS_FROM_MANAGER,
    54         PS_PREEMPT,
    5556        PS_FROM_DEAD
    5657} pschange_type;
     
    6667        tcb_t *tcb;
    6768
    68         struct psthread_data *waiter;
    69         int finished;
     69        struct psthread_data *clean_after_me;
     70        struct psthread_data *joiner;
     71        int joinee_retval;
    7072        int retval;
    7173        int flags;
     
    8890void psthread_dec_sercount(void);
    8991
    90 static inline int psthread_schedule_next() {
     92static inline int psthread_schedule_next(void) {
    9193        return psthread_schedule_next_adv(PS_PREEMPT);
    9294}
    93 
    9495
    9596#endif
Note: See TracChangeset for help on using the changeset viewer.