Ignore:
File:
1 edited

Legend:

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

    receff5f r6aeca0d  
    5050#include <async.h>
    5151
     52#ifdef FUTEX_UPGRADABLE
     53#include <rcu.h>
     54#endif
     55
    5256/**
    5357 * This futex serializes access to ready_list,
    54  * serialized_list and manager_list.
    55  */
    56 static atomic_t fibril_futex = FUTEX_INITIALIZER;
     58 * manager_list and fibril_list.
     59 */
     60static futex_t fibril_futex = FUTEX_INITIALIZER;
    5761
    5862static LIST_INITIALIZE(ready_list);
    59 static LIST_INITIALIZE(serialized_list);
    6063static LIST_INITIALIZE(manager_list);
    61 
    62 /** Number of threads that are executing a manager fibril. */
    63 static 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  */
    69 static int serialized_threads;
    70 
    71 /** Fibril-local count of serialization. If > 0, we must not preempt */
    72 static fibril_local int serialization_count;
     64static LIST_INITIALIZE(fibril_list);
    7365
    7466/** Function that spans the whole life-cycle of a fibril.
     
    8274{
    8375        fibril_t *fibril = __tcb_get()->fibril_data;
     76
     77#ifdef FUTEX_UPGRADABLE
     78        rcu_register_fibril();
     79#endif
    8480       
    8581        /* Call the implementing function. */
    8682        fibril->retval = fibril->func(fibril->arg);
    8783       
     84        futex_down(&async_futex);
    8885        fibril_switch(FIBRIL_FROM_DEAD);
    8986        /* Not reached */
     
    116113       
    117114        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);
    118126       
    119127        return fibril;
    120128}
    121129
    122 void fibril_teardown(fibril_t *fibril)
    123 {
     130void 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);
    124137        tls_free(fibril->tcb);
    125138        free(fibril);
     
    128141/** Switch from the current fibril.
    129142 *
    130  * If calling with FIBRIL_TO_MANAGER parameter, the async_futex should be
    131  * held.
     143 * If stype is FIBRIL_TO_MANAGER or FIBRIL_FROM_DEAD, the async_futex must
     144 * be held.
    132145 *
    133146 * @param stype Switch type. One of FIBRIL_PREEMPT, FIBRIL_TO_MANAGER,
     
    141154int fibril_switch(fibril_switch_type_t stype)
    142155{
    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;
     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;
    161164                }
    162         }
    163        
    164         /* If we are going to manager and none exists, create it */
    165         if ((stype == FIBRIL_TO_MANAGER) || (stype == FIBRIL_FROM_DEAD)) {
     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 */
    166172                while (list_empty(&manager_list)) {
    167                         futex_up(&fibril_futex);
     173                        futex_unlock(&fibril_futex);
    168174                        async_create_manager();
    169                         futex_down(&fibril_futex);
     175                        futex_lock(&fibril_futex);
    170176                }
     177                break;
    171178        }
    172179       
     
    176183                /* Save current state */
    177184                if (!context_save(&srcf->ctx)) {
    178                         if (serialization_count)
    179                                 srcf->flags &= ~FIBRIL_SERIALIZED;
    180                        
    181185                        if (srcf->clean_after_me) {
    182186                                /*
     
    196200                                        as_area_destroy(stack);
    197201                                }
    198                                 fibril_teardown(srcf->clean_after_me);
     202                                fibril_teardown(srcf->clean_after_me, true);
    199203                                srcf->clean_after_me = NULL;
    200204                        }
    201205                       
    202                         return 1;       /* futex_up already done here */
     206                        return 1;       /* futex_unlock already done here */
    203207                }
    204208               
    205                 /* Save myself to the correct run list */
    206                 if (stype == FIBRIL_PREEMPT)
     209                /* Put the current fibril into the correct run list */
     210                switch (stype) {
     211                case FIBRIL_PREEMPT:
    207212                        list_append(&srcf->link, &ready_list);
    208                 else if (stype == FIBRIL_FROM_MANAGER) {
     213                        break;
     214                case FIBRIL_FROM_MANAGER:
    209215                        list_append(&srcf->link, &manager_list);
    210                         threads_in_manager--;
    211                 } else {
     216                        break;
     217                default:
     218                        assert(stype == FIBRIL_TO_MANAGER);
     219
     220                        srcf->switches++;
     221
    212222                        /*
    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.
     223                         * Don't put the current fibril into any list, it should
     224                         * already be somewhere, or it will be lost.
    216225                         */
     226                        break;
    217227                }
    218228        }
    219229       
     230        fibril_t *dstf;
     231
    220232        /* Choose a new fibril to run */
    221         fibril_t *dstf;
    222         if ((stype == FIBRIL_TO_MANAGER) || (stype == FIBRIL_FROM_DEAD)) {
     233        switch (stype) {
     234        case FIBRIL_TO_MANAGER:
     235        case FIBRIL_FROM_DEAD:
    223236                dstf = list_get_instance(list_first(&manager_list), fibril_t,
    224237                    link);
    225                 if (serialization_count && stype == FIBRIL_TO_MANAGER) {
    226                         serialized_threads++;
    227                         srcf->flags |= FIBRIL_SERIALIZED;
    228                 }
    229                 threads_in_manager++;
    230238               
    231239                if (stype == FIBRIL_FROM_DEAD)
    232240                        dstf->clean_after_me = srcf;
    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         }
     241                break;
     242        default:
     243                dstf = list_get_instance(list_first(&ready_list), fibril_t,
     244                    link);
     245                break;
     246        }
     247
    243248        list_remove(&dstf->link);
    244249       
    245         futex_up(&fibril_futex);
     250        futex_unlock(&fibril_futex);
     251       
     252#ifdef FUTEX_UPGRADABLE
     253        if (stype == FIBRIL_FROM_DEAD) {
     254                rcu_deregister_fibril();
     255        }
     256#endif
     257       
    246258        context_restore(&dstf->ctx);
    247259        /* not reached */
    248        
    249 ret_0:
    250         futex_up(&fibril_futex);
    251         return retval;
    252260}
    253261
     
    271279        size_t stack_size = (stksz == FIBRIL_DFLT_STK_SIZE) ?
    272280            stack_size_get() : stksz;
    273         fibril->stack = as_area_create((void *) -1, stack_size,
     281        fibril->stack = as_area_create(AS_AREA_ANY, stack_size,
    274282            AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE | AS_AREA_GUARD |
    275             AS_AREA_LATE_RESERVE);
     283            AS_AREA_LATE_RESERVE, AS_AREA_UNPAGED);
    276284        if (fibril->stack == (void *) -1) {
    277                 fibril_teardown(fibril);
     285                fibril_teardown(fibril, false);
    278286                return 0;
    279287        }
     
    302310       
    303311        as_area_destroy(fibril->stack);
    304         fibril_teardown(fibril);
     312        fibril_teardown(fibril, false);
    305313}
    306314
     
    315323        fibril_t *fibril = (fibril_t *) fid;
    316324       
    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);
     325        futex_lock(&fibril_futex);
     326        list_append(&fibril->link, &ready_list);
     327        futex_unlock(&fibril_futex);
    325328}
    326329
     
    335338        fibril_t *fibril = (fibril_t *) fid;
    336339       
    337         futex_down(&fibril_futex);
     340        futex_lock(&fibril_futex);
    338341        list_append(&fibril->link, &manager_list);
    339         futex_up(&fibril_futex);
     342        futex_unlock(&fibril_futex);
    340343}
    341344
     
    343346void fibril_remove_manager(void)
    344347{
    345         futex_down(&fibril_futex);
    346        
     348        futex_lock(&fibril_futex);
    347349        if (!list_empty(&manager_list))
    348350                list_remove(list_first(&manager_list));
    349        
    350         futex_up(&fibril_futex);
     351        futex_unlock(&fibril_futex);
    351352}
    352353
     
    361362}
    362363
    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  */
    372 void fibril_inc_sercount(void)
    373 {
    374         serialization_count++;
    375 }
    376 
    377 /** Restore the preemption counter to the previous state. */
    378 void fibril_dec_sercount(void)
    379 {
    380         serialization_count--;
    381 }
    382 
    383 int fibril_get_sercount(void)
    384 {
    385         return serialization_count;
    386 }
    387 
    388364/** @}
    389365 */
Note: See TracChangeset for help on using the changeset viewer.