fibril.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2006 Ondrej Palkovsky
00003  * Copyright (c) 2007 Jakub Jermar
00004  * All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions
00008  * are met:
00009  *
00010  * - Redistributions of source code must retain the above copyright
00011  *   notice, this list of conditions and the following disclaimer.
00012  * - Redistributions in binary form must reproduce the above copyright
00013  *   notice, this list of conditions and the following disclaimer in the
00014  *   documentation and/or other materials provided with the distribution.
00015  * - The name of the author may not be used to endorse or promote products
00016  *   derived from this software without specific prior written permission.
00017  *
00018  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00019  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00020  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00021  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00023  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00024  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00025  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00026  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00027  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00028  */
00029 
00036 #include <adt/list.h>
00037 #include <fibril.h>
00038 #include <thread.h>
00039 #include <tls.h>
00040 #include <malloc.h>
00041 #include <unistd.h>
00042 #include <stdio.h>
00043 #include <arch/barrier.h>
00044 #include <libarch/faddr.h>
00045 #include <futex.h>
00046 #include <assert.h>
00047 #include <async.h>
00048 
00049 #ifndef FIBRIL_INITIAL_STACK_PAGES_NO
00050         #define FIBRIL_INITIAL_STACK_PAGES_NO  1
00051 #endif
00052 
00057 static atomic_t fibril_futex = FUTEX_INITIALIZER;
00058 
00059 static LIST_INITIALIZE(ready_list);
00060 static LIST_INITIALIZE(serialized_list);
00061 static LIST_INITIALIZE(manager_list);
00062 
00064 static int threads_in_manager;
00065 
00070 static int serialized_threads;
00071 
00073 static fibril_local int serialization_count;
00074 
00082 static void fibril_main(void)
00083 {
00084         fibril_t *fibril = __tcb_get()->fibril_data;
00085         
00086         /* Call the implementing function. */
00087         fibril->retval = fibril->func(fibril->arg);
00088         
00089         fibril_switch(FIBRIL_FROM_DEAD);
00090         /* Not reached */
00091 }
00092 
00096 fibril_t *fibril_setup(void)
00097 {
00098         tcb_t *tcb = __make_tls();
00099         if (!tcb)
00100                 return NULL;
00101         
00102         fibril_t *fibril = malloc(sizeof(fibril_t));
00103         if (!fibril) {
00104                 __free_tls(tcb);
00105                 return NULL;
00106         }
00107         
00108         tcb->fibril_data = fibril;
00109         fibril->tcb = tcb;
00110         
00111         fibril->func = NULL;
00112         fibril->arg = NULL;
00113         fibril->stack = NULL;
00114         fibril->clean_after_me = NULL;
00115         fibril->retval = 0;
00116         fibril->flags = 0;
00117         
00118         fibril->waits_for = NULL;
00119         
00120         return fibril;
00121 }
00122 
00123 void fibril_teardown(fibril_t *fibril)
00124 {
00125         __free_tls(fibril->tcb);
00126         free(fibril);
00127 }
00128 
00142 int fibril_switch(fibril_switch_type_t stype)
00143 {
00144         int retval = 0;
00145         
00146         futex_down(&fibril_futex);
00147         
00148         if (stype == FIBRIL_PREEMPT && list_empty(&ready_list))
00149                 goto ret_0;
00150         
00151         if (stype == FIBRIL_FROM_MANAGER) {
00152                 if ((list_empty(&ready_list)) && (list_empty(&serialized_list)))
00153                         goto ret_0;
00154                 
00155                 /*
00156                  * Do not preempt if there is not enough threads to run the
00157                  * ready fibrils which are not serialized.
00158                  */
00159                 if ((list_empty(&serialized_list)) &&
00160                     (threads_in_manager <= serialized_threads)) {
00161                         goto ret_0;
00162                 }
00163         }
00164         
00165         /* If we are going to manager and none exists, create it */
00166         if ((stype == FIBRIL_TO_MANAGER) || (stype == FIBRIL_FROM_DEAD)) {
00167                 while (list_empty(&manager_list)) {
00168                         futex_up(&fibril_futex);
00169                         async_create_manager();
00170                         futex_down(&fibril_futex);
00171                 }
00172         }
00173         
00174         fibril_t *srcf = __tcb_get()->fibril_data;
00175         if (stype != FIBRIL_FROM_DEAD) {
00176                 
00177                 /* Save current state */
00178                 if (!context_save(&srcf->ctx)) {
00179                         if (serialization_count)
00180                                 srcf->flags &= ~FIBRIL_SERIALIZED;
00181                         
00182                         if (srcf->clean_after_me) {
00183                                 /*
00184                                  * Cleanup after the dead fibril from which we
00185                                  * restored context here.
00186                                  */
00187                                 void *stack = srcf->clean_after_me->stack;
00188                                 if (stack) {
00189                                         /*
00190                                          * This check is necessary because a
00191                                          * thread could have exited like a
00192                                          * normal fibril using the
00193                                          * FIBRIL_FROM_DEAD switch type. In that
00194                                          * case, its fibril will not have the
00195                                          * stack member filled.
00196                                          */
00197                                         free(stack);
00198                                 }
00199                                 fibril_teardown(srcf->clean_after_me);
00200                                 srcf->clean_after_me = NULL;
00201                         }
00202                         
00203                         return 1;       /* futex_up already done here */
00204                 }
00205                 
00206                 /* Save myself to the correct run list */
00207                 if (stype == FIBRIL_PREEMPT)
00208                         list_append(&srcf->link, &ready_list);
00209                 else if (stype == FIBRIL_FROM_MANAGER) {
00210                         list_append(&srcf->link, &manager_list);
00211                         threads_in_manager--;
00212                 } else {
00213                         /*
00214                          * If stype == FIBRIL_TO_MANAGER, don't put ourselves to
00215                          * any list, we should already be somewhere, or we will
00216                          * be lost.
00217                          */
00218                 }
00219         }
00220         
00221         /* Choose a new fibril to run */
00222         fibril_t *dstf;
00223         if ((stype == FIBRIL_TO_MANAGER) || (stype == FIBRIL_FROM_DEAD)) {
00224                 dstf = list_get_instance(manager_list.next, fibril_t, link);
00225                 if (serialization_count && stype == FIBRIL_TO_MANAGER) {
00226                         serialized_threads++;
00227                         srcf->flags |= FIBRIL_SERIALIZED;
00228                 }
00229                 threads_in_manager++;
00230                 
00231                 if (stype == FIBRIL_FROM_DEAD) 
00232                         dstf->clean_after_me = srcf;
00233         } else {
00234                 if (!list_empty(&serialized_list)) {
00235                         dstf = list_get_instance(serialized_list.next, fibril_t,
00236                             link);
00237                         serialized_threads--;
00238                 } else {
00239                         dstf = list_get_instance(ready_list.next, fibril_t,
00240                             link);
00241                 }
00242         }
00243         list_remove(&dstf->link);
00244         
00245         futex_up(&fibril_futex);
00246         context_restore(&dstf->ctx);
00247         /* not reached */
00248         
00249 ret_0:
00250         futex_up(&fibril_futex);
00251         return retval;
00252 }
00253 
00262 fid_t fibril_create(int (*func)(void *), void *arg)
00263 {
00264         fibril_t *fibril;
00265         
00266         fibril = fibril_setup();
00267         if (fibril == NULL)
00268                 return 0;
00269         
00270         fibril->stack =
00271             (char *) malloc(FIBRIL_INITIAL_STACK_PAGES_NO * getpagesize());
00272         if (!fibril->stack) {
00273                 fibril_teardown(fibril);
00274                 return 0;
00275         }
00276         
00277         fibril->func = func;
00278         fibril->arg = arg;
00279 
00280         context_save(&fibril->ctx);
00281         context_set(&fibril->ctx, FADDR(fibril_main), fibril->stack,
00282             FIBRIL_INITIAL_STACK_PAGES_NO * getpagesize(), fibril->tcb);
00283 
00284         return (fid_t) fibril;
00285 }
00286 
00293 void fibril_add_ready(fid_t fid)
00294 {
00295         fibril_t *fibril = (fibril_t *) fid;
00296         
00297         futex_down(&fibril_futex);
00298         
00299         if ((fibril->flags & FIBRIL_SERIALIZED))
00300                 list_append(&fibril->link, &serialized_list);
00301         else
00302                 list_append(&fibril->link, &ready_list);
00303         
00304         futex_up(&fibril_futex);
00305 }
00306 
00313 void fibril_add_manager(fid_t fid)
00314 {
00315         fibril_t *fibril = (fibril_t *) fid;
00316         
00317         futex_down(&fibril_futex);
00318         list_append(&fibril->link, &manager_list);
00319         futex_up(&fibril_futex);
00320 }
00321 
00323 void fibril_remove_manager(void)
00324 {
00325         futex_down(&fibril_futex);
00326         
00327         if (!list_empty(&manager_list))
00328                 list_remove(manager_list.next);
00329         
00330         futex_up(&fibril_futex);
00331 }
00332 
00338 fid_t fibril_get_id(void)
00339 {
00340         return (fid_t) __tcb_get()->fibril_data;
00341 }
00342 
00352 void fibril_inc_sercount(void)
00353 {
00354         serialization_count++;
00355 }
00356 
00358 void fibril_dec_sercount(void)
00359 {
00360         serialization_count--;
00361 }
00362 
00363 int fibril_get_sercount(void)
00364 {
00365         return serialization_count;
00366 }
00367 

Generated on Thu Jun 2 07:45:45 2011 for HelenOS/USB by  doxygen 1.4.7