Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/c/generic/fibril.c

    rc170438 r4d11204  
    4949#include <assert.h>
    5050#include <async.h>
     51#include <futex.h>
    5152
    5253#ifdef FUTEX_UPGRADABLE
     
    5657/**
    5758 * This futex serializes access to ready_list,
    58  * manager_list and fibril_list.
     59 * serialized_list, manager_list and fibril_list.
    5960 */
    6061static futex_t fibril_futex = FUTEX_INITIALIZER;
    6162
    6263static LIST_INITIALIZE(ready_list);
     64static LIST_INITIALIZE(serialized_list);
    6365static LIST_INITIALIZE(manager_list);
    6466static LIST_INITIALIZE(fibril_list);
     67
     68/** Number of threads that are executing a manager fibril. */
     69static int threads_in_manager;
     70
     71/**
     72 * Number of threads that are executing a manager fibril
     73 * and are serialized. Protected by async_futex.
     74 */
     75static int serialized_threads;
     76
     77/** Fibril-local count of serialization. If > 0, we must not preempt */
     78static fibril_local int serialization_count;
    6579
    6680/** Function that spans the whole life-cycle of a fibril.
     
    8296        fibril->retval = fibril->func(fibril->arg);
    8397       
    84         futex_down(&async_futex);
    8598        fibril_switch(FIBRIL_FROM_DEAD);
    8699        /* Not reached */
     
    114127        fibril->waits_for = NULL;
    115128
    116         fibril->switches = 0;
    117 
    118         /*
    119          * We are called before __tcb_set(), so we need to use
    120          * futex_down/up() instead of futex_lock/unlock() that
    121          * may attempt to access TLS.
    122          */
    123         futex_down(&fibril_futex);
     129        futex_lock(&fibril_futex);
    124130        list_append(&fibril->all_link, &fibril_list);
    125         futex_up(&fibril_futex);
     131        futex_unlock(&fibril_futex);
    126132       
    127133        return fibril;
     
    141147/** Switch from the current fibril.
    142148 *
    143  * If stype is FIBRIL_TO_MANAGER or FIBRIL_FROM_DEAD, the async_futex must
    144  * be held.
     149 * If calling with FIBRIL_TO_MANAGER parameter, the async_futex should be
     150 * held.
    145151 *
    146152 * @param stype Switch type. One of FIBRIL_PREEMPT, FIBRIL_TO_MANAGER,
     
    154160int fibril_switch(fibril_switch_type_t stype)
    155161{
     162        int retval = 0;
     163       
    156164        futex_lock(&fibril_futex);
    157 
    158         switch (stype) {
    159         case FIBRIL_PREEMPT:
    160         case FIBRIL_FROM_MANAGER:
    161                 if (list_empty(&ready_list)) {
    162                         futex_unlock(&fibril_futex);
    163                         return 0;
    164                 }
    165                 break;
    166         case FIBRIL_TO_MANAGER:
    167         case FIBRIL_FROM_DEAD:
    168                 /* Make sure the async_futex is held. */
    169                 assert((atomic_signed_t) async_futex.val.count <= 0);
    170 
    171                 /* If we are going to manager and none exists, create it */
     165       
     166        if (stype == FIBRIL_PREEMPT && list_empty(&ready_list))
     167                goto ret_0;
     168       
     169        if (stype == FIBRIL_FROM_MANAGER) {
     170                if ((list_empty(&ready_list)) && (list_empty(&serialized_list)))
     171                        goto ret_0;
     172               
     173                /*
     174                 * Do not preempt if there is not enough threads to run the
     175                 * ready fibrils which are not serialized.
     176                 */
     177                if ((list_empty(&serialized_list)) &&
     178                    (threads_in_manager <= serialized_threads)) {
     179                        goto ret_0;
     180                }
     181        }
     182       
     183        /* If we are going to manager and none exists, create it */
     184        if ((stype == FIBRIL_TO_MANAGER) || (stype == FIBRIL_FROM_DEAD)) {
    172185                while (list_empty(&manager_list)) {
    173186                        futex_unlock(&fibril_futex);
     
    175188                        futex_lock(&fibril_futex);
    176189                }
    177                 break;
    178190        }
    179191       
     
    183195                /* Save current state */
    184196                if (!context_save(&srcf->ctx)) {
     197                        if (serialization_count)
     198                                srcf->flags &= ~FIBRIL_SERIALIZED;
     199                       
    185200                        if (srcf->clean_after_me) {
    186201                                /*
     
    207222                }
    208223               
    209                 /* Put the current fibril into the correct run list */
    210                 switch (stype) {
    211                 case FIBRIL_PREEMPT:
     224                /* Save myself to the correct run list */
     225                if (stype == FIBRIL_PREEMPT)
    212226                        list_append(&srcf->link, &ready_list);
    213                         break;
    214                 case FIBRIL_FROM_MANAGER:
     227                else if (stype == FIBRIL_FROM_MANAGER) {
    215228                        list_append(&srcf->link, &manager_list);
    216                         break;
    217                 default:
    218                         assert(stype == FIBRIL_TO_MANAGER);
    219 
    220                         srcf->switches++;
    221 
     229                        threads_in_manager--;
     230                } else {
    222231                        /*
    223                          * Don't put the current fibril into any list, it should
    224                          * already be somewhere, or it will be lost.
     232                         * If stype == FIBRIL_TO_MANAGER, don't put ourselves to
     233                         * any list, we should already be somewhere, or we will
     234                         * be lost.
    225235                         */
    226                         break;
    227                 }
    228         }
    229        
     236                }
     237        }
     238       
     239        /* Choose a new fibril to run */
    230240        fibril_t *dstf;
    231 
    232         /* Choose a new fibril to run */
    233         switch (stype) {
    234         case FIBRIL_TO_MANAGER:
    235         case FIBRIL_FROM_DEAD:
     241        if ((stype == FIBRIL_TO_MANAGER) || (stype == FIBRIL_FROM_DEAD)) {
    236242                dstf = list_get_instance(list_first(&manager_list), fibril_t,
    237243                    link);
     244                if (serialization_count && stype == FIBRIL_TO_MANAGER) {
     245                        serialized_threads++;
     246                        srcf->flags |= FIBRIL_SERIALIZED;
     247                }
     248                threads_in_manager++;
    238249               
    239250                if (stype == FIBRIL_FROM_DEAD)
    240251                        dstf->clean_after_me = srcf;
    241                 break;
    242         default:
    243                 dstf = list_get_instance(list_first(&ready_list), fibril_t,
    244                     link);
    245                 break;
    246         }
    247 
     252        } else {
     253                if (!list_empty(&serialized_list)) {
     254                        dstf = list_get_instance(list_first(&serialized_list),
     255                            fibril_t, link);
     256                        serialized_threads--;
     257                } else {
     258                        dstf = list_get_instance(list_first(&ready_list),
     259                            fibril_t, link);
     260                }
     261        }
    248262        list_remove(&dstf->link);
    249263       
     
    258272        context_restore(&dstf->ctx);
    259273        /* not reached */
     274       
     275ret_0:
     276        futex_unlock(&fibril_futex);
     277        return retval;
    260278}
    261279
     
    324342       
    325343        futex_lock(&fibril_futex);
    326         list_append(&fibril->link, &ready_list);
     344       
     345        if ((fibril->flags & FIBRIL_SERIALIZED))
     346                list_append(&fibril->link, &serialized_list);
     347        else
     348                list_append(&fibril->link, &ready_list);
     349       
    327350        futex_unlock(&fibril_futex);
    328351}
     
    347370{
    348371        futex_lock(&fibril_futex);
     372       
    349373        if (!list_empty(&manager_list))
    350374                list_remove(list_first(&manager_list));
     375       
    351376        futex_unlock(&fibril_futex);
    352377}
     
    362387}
    363388
     389/** Disable preemption
     390 *
     391 * If the fibril wants to send several message in a row and does not want to be
     392 * preempted, it should start async_serialize_start() in the beginning of
     393 * communication and async_serialize_end() in the end. If it is a true
     394 * multithreaded application, it should protect the communication channel by a
     395 * futex as well.
     396 *
     397 */
     398void fibril_inc_sercount(void)
     399{
     400        serialization_count++;
     401}
     402
     403/** Restore the preemption counter to the previous state. */
     404void fibril_dec_sercount(void)
     405{
     406        serialization_count--;
     407}
     408
     409int fibril_get_sercount(void)
     410{
     411        return serialization_count;
     412}
     413
    364414/** @}
    365415 */
Note: See TracChangeset for help on using the changeset viewer.