Ignore:
File:
1 edited

Legend:

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

    r6aeca0d receff5f  
    5050#include <async.h>
    5151
    52 #ifdef FUTEX_UPGRADABLE
    53 #include <rcu.h>
    54 #endif
    55 
    5652/**
    5753 * This futex serializes access to ready_list,
    58  * manager_list and fibril_list.
    59  */
    60 static futex_t fibril_futex = FUTEX_INITIALIZER;
     54 * serialized_list and manager_list.
     55 */
     56static atomic_t fibril_futex = FUTEX_INITIALIZER;
    6157
    6258static LIST_INITIALIZE(ready_list);
     59static LIST_INITIALIZE(serialized_list);
    6360static LIST_INITIALIZE(manager_list);
    64 static LIST_INITIALIZE(fibril_list);
     61
     62/** Number of threads that are executing a manager fibril. */
     63static int threads_in_manager;
     64
     65/**
     66 * Number of threads that are executing a manager fibril
     67 * and are serialized. Protected by async_futex.
     68 */
     69static int serialized_threads;
     70
     71/** Fibril-local count of serialization. If > 0, we must not preempt */
     72static fibril_local int serialization_count;
    6573
    6674/** Function that spans the whole life-cycle of a fibril.
     
    7482{
    7583        fibril_t *fibril = __tcb_get()->fibril_data;
    76 
    77 #ifdef FUTEX_UPGRADABLE
    78         rcu_register_fibril();
    79 #endif
    8084       
    8185        /* Call the implementing function. */
    8286        fibril->retval = fibril->func(fibril->arg);
    8387       
    84         futex_down(&async_futex);
    8588        fibril_switch(FIBRIL_FROM_DEAD);
    8689        /* Not reached */
     
    113116       
    114117        fibril->waits_for = NULL;
    115 
    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);
    124         list_append(&fibril->all_link, &fibril_list);
    125         futex_up(&fibril_futex);
    126118       
    127119        return fibril;
    128120}
    129121
    130 void fibril_teardown(fibril_t *fibril, bool locked)
    131 {       
    132         if (!locked)
    133                 futex_lock(&fibril_futex);
    134         list_remove(&fibril->all_link);
    135         if (!locked)
    136                 futex_unlock(&fibril_futex);
     122void fibril_teardown(fibril_t *fibril)
     123{
    137124        tls_free(fibril->tcb);
    138125        free(fibril);
     
    141128/** Switch from the current fibril.
    142129 *
    143  * If stype is FIBRIL_TO_MANAGER or FIBRIL_FROM_DEAD, the async_futex must
    144  * be held.
     130 * If calling with FIBRIL_TO_MANAGER parameter, the async_futex should be
     131 * held.
    145132 *
    146133 * @param stype Switch type. One of FIBRIL_PREEMPT, FIBRIL_TO_MANAGER,
     
    154141int fibril_switch(fibril_switch_type_t stype)
    155142{
    156         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 */
     143        int retval = 0;
     144       
     145        futex_down(&fibril_futex);
     146       
     147        if (stype == FIBRIL_PREEMPT && list_empty(&ready_list))
     148                goto ret_0;
     149       
     150        if (stype == FIBRIL_FROM_MANAGER) {
     151                if ((list_empty(&ready_list)) && (list_empty(&serialized_list)))
     152                        goto ret_0;
     153               
     154                /*
     155                 * Do not preempt if there is not enough threads to run the
     156                 * ready fibrils which are not serialized.
     157                 */
     158                if ((list_empty(&serialized_list)) &&
     159                    (threads_in_manager <= serialized_threads)) {
     160                        goto ret_0;
     161                }
     162        }
     163       
     164        /* If we are going to manager and none exists, create it */
     165        if ((stype == FIBRIL_TO_MANAGER) || (stype == FIBRIL_FROM_DEAD)) {
    172166                while (list_empty(&manager_list)) {
    173                         futex_unlock(&fibril_futex);
     167                        futex_up(&fibril_futex);
    174168                        async_create_manager();
    175                         futex_lock(&fibril_futex);
    176                 }
    177                 break;
     169                        futex_down(&fibril_futex);
     170                }
    178171        }
    179172       
     
    183176                /* Save current state */
    184177                if (!context_save(&srcf->ctx)) {
     178                        if (serialization_count)
     179                                srcf->flags &= ~FIBRIL_SERIALIZED;
     180                       
    185181                        if (srcf->clean_after_me) {
    186182                                /*
     
    200196                                        as_area_destroy(stack);
    201197                                }
    202                                 fibril_teardown(srcf->clean_after_me, true);
     198                                fibril_teardown(srcf->clean_after_me);
    203199                                srcf->clean_after_me = NULL;
    204200                        }
    205201                       
    206                         return 1;       /* futex_unlock already done here */
     202                        return 1;       /* futex_up already done here */
    207203                }
    208204               
    209                 /* Put the current fibril into the correct run list */
    210                 switch (stype) {
    211                 case FIBRIL_PREEMPT:
     205                /* Save myself to the correct run list */
     206                if (stype == FIBRIL_PREEMPT)
    212207                        list_append(&srcf->link, &ready_list);
    213                         break;
    214                 case FIBRIL_FROM_MANAGER:
     208                else if (stype == FIBRIL_FROM_MANAGER) {
    215209                        list_append(&srcf->link, &manager_list);
    216                         break;
    217                 default:
    218                         assert(stype == FIBRIL_TO_MANAGER);
    219 
    220                         srcf->switches++;
    221 
     210                        threads_in_manager--;
     211                } else {
    222212                        /*
    223                          * Don't put the current fibril into any list, it should
    224                          * already be somewhere, or it will be lost.
     213                         * If stype == FIBRIL_TO_MANAGER, don't put ourselves to
     214                         * any list, we should already be somewhere, or we will
     215                         * be lost.
    225216                         */
    226                         break;
    227                 }
    228         }
    229        
     217                }
     218        }
     219       
     220        /* Choose a new fibril to run */
    230221        fibril_t *dstf;
    231 
    232         /* Choose a new fibril to run */
    233         switch (stype) {
    234         case FIBRIL_TO_MANAGER:
    235         case FIBRIL_FROM_DEAD:
     222        if ((stype == FIBRIL_TO_MANAGER) || (stype == FIBRIL_FROM_DEAD)) {
    236223                dstf = list_get_instance(list_first(&manager_list), fibril_t,
    237224                    link);
     225                if (serialization_count && stype == FIBRIL_TO_MANAGER) {
     226                        serialized_threads++;
     227                        srcf->flags |= FIBRIL_SERIALIZED;
     228                }
     229                threads_in_manager++;
    238230               
    239231                if (stype == FIBRIL_FROM_DEAD)
    240232                        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 
     233        } else {
     234                if (!list_empty(&serialized_list)) {
     235                        dstf = list_get_instance(list_first(&serialized_list),
     236                            fibril_t, link);
     237                        serialized_threads--;
     238                } else {
     239                        dstf = list_get_instance(list_first(&ready_list),
     240                            fibril_t, link);
     241                }
     242        }
    248243        list_remove(&dstf->link);
    249244       
    250         futex_unlock(&fibril_futex);
    251        
    252 #ifdef FUTEX_UPGRADABLE
    253         if (stype == FIBRIL_FROM_DEAD) {
    254                 rcu_deregister_fibril();
    255         }
    256 #endif
    257        
     245        futex_up(&fibril_futex);
    258246        context_restore(&dstf->ctx);
    259247        /* not reached */
     248       
     249ret_0:
     250        futex_up(&fibril_futex);
     251        return retval;
    260252}
    261253
     
    279271        size_t stack_size = (stksz == FIBRIL_DFLT_STK_SIZE) ?
    280272            stack_size_get() : stksz;
    281         fibril->stack = as_area_create(AS_AREA_ANY, stack_size,
     273        fibril->stack = as_area_create((void *) -1, stack_size,
    282274            AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE | AS_AREA_GUARD |
    283             AS_AREA_LATE_RESERVE, AS_AREA_UNPAGED);
     275            AS_AREA_LATE_RESERVE);
    284276        if (fibril->stack == (void *) -1) {
    285                 fibril_teardown(fibril, false);
     277                fibril_teardown(fibril);
    286278                return 0;
    287279        }
     
    310302       
    311303        as_area_destroy(fibril->stack);
    312         fibril_teardown(fibril, false);
     304        fibril_teardown(fibril);
    313305}
    314306
     
    323315        fibril_t *fibril = (fibril_t *) fid;
    324316       
    325         futex_lock(&fibril_futex);
    326         list_append(&fibril->link, &ready_list);
    327         futex_unlock(&fibril_futex);
     317        futex_down(&fibril_futex);
     318       
     319        if ((fibril->flags & FIBRIL_SERIALIZED))
     320                list_append(&fibril->link, &serialized_list);
     321        else
     322                list_append(&fibril->link, &ready_list);
     323       
     324        futex_up(&fibril_futex);
    328325}
    329326
     
    338335        fibril_t *fibril = (fibril_t *) fid;
    339336       
    340         futex_lock(&fibril_futex);
     337        futex_down(&fibril_futex);
    341338        list_append(&fibril->link, &manager_list);
    342         futex_unlock(&fibril_futex);
     339        futex_up(&fibril_futex);
    343340}
    344341
     
    346343void fibril_remove_manager(void)
    347344{
    348         futex_lock(&fibril_futex);
     345        futex_down(&fibril_futex);
     346       
    349347        if (!list_empty(&manager_list))
    350348                list_remove(list_first(&manager_list));
    351         futex_unlock(&fibril_futex);
     349       
     350        futex_up(&fibril_futex);
    352351}
    353352
     
    362361}
    363362
     363/** Disable preemption
     364 *
     365 * If the fibril wants to send several message in a row and does not want to be
     366 * preempted, it should start async_serialize_start() in the beginning of
     367 * communication and async_serialize_end() in the end. If it is a true
     368 * multithreaded application, it should protect the communication channel by a
     369 * futex as well.
     370 *
     371 */
     372void fibril_inc_sercount(void)
     373{
     374        serialization_count++;
     375}
     376
     377/** Restore the preemption counter to the previous state. */
     378void fibril_dec_sercount(void)
     379{
     380        serialization_count--;
     381}
     382
     383int fibril_get_sercount(void)
     384{
     385        return serialization_count;
     386}
     387
    364388/** @}
    365389 */
Note: See TracChangeset for help on using the changeset viewer.