Subversion Repositories HelenOS

Rev

Rev 2481 | Rev 2483 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright (c) 2006 Ondrej Palkovsky
  3.  * Copyright (c) 2007 Jakub Jermar
  4.  * All rights reserved.
  5.  *
  6.  * Redistribution and use in source and binary forms, with or without
  7.  * modification, are permitted provided that the following conditions
  8.  * are met:
  9.  *
  10.  * - Redistributions of source code must retain the above copyright
  11.  *   notice, this list of conditions and the following disclaimer.
  12.  * - Redistributions in binary form must reproduce the above copyright
  13.  *   notice, this list of conditions and the following disclaimer in the
  14.  *   documentation and/or other materials provided with the distribution.
  15.  * - The name of the author may not be used to endorse or promote products
  16.  *   derived from this software without specific prior written permission.
  17.  *
  18.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  19.  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  20.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  21.  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  22.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  23.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  24.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  25.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  27.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28.  */
  29.  
  30. /** @addtogroup libc
  31.  * @{
  32.  */
  33. /** @file
  34.  */
  35.  
  36. #include <libadt/list.h>
  37. #include <fibril.h>
  38. #include <malloc.h>
  39. #include <unistd.h>
  40. #include <thread.h>
  41. #include <stdio.h>
  42. #include <libarch/faddr.h>
  43. #include <futex.h>
  44. #include <assert.h>
  45. #include <async.h>
  46.  
  47. #ifndef FIBRIL_INITIAL_STACK_PAGES_NO
  48. #define FIBRIL_INITIAL_STACK_PAGES_NO   1
  49. #endif
  50.  
  51. static LIST_INITIALIZE(ready_list);
  52. static LIST_INITIALIZE(serialized_list);
  53. static LIST_INITIALIZE(manager_list);
  54.  
  55. static void fibril_main(void);
  56.  
  57. static atomic_t fibril_futex = FUTEX_INITIALIZER;
  58. /** Number of threads that are in async_serialized mode */
  59. static int serialized_threads;  /* Protected by async_futex */
  60. /** Thread-local count of serialization. If >0, we must not preempt */
  61. static __thread int serialization_count;
  62. /** Counter for fibrils residing in async_manager */
  63. static int fibrils_in_manager;
  64.  
  65. /** Setup fibril information into TCB structure */
  66. fibril_t *fibril_setup(void)
  67. {
  68.     fibril_t *f;
  69.     tcb_t *tcb;
  70.  
  71.     tcb = __make_tls();
  72.     if (!tcb)
  73.         return NULL;
  74.  
  75.     f = malloc(sizeof(*f));
  76.     if (!f) {
  77.         __free_tls(tcb);
  78.         return NULL;
  79.     }
  80.  
  81.     tcb->fibril_data = f;
  82.     f->tcb = tcb;
  83.  
  84.     return f;
  85. }
  86.  
  87. void fibril_teardown(fibril_t *f)
  88. {
  89.     __free_tls(f->tcb);
  90.     free(f);
  91. }
  92.  
  93. /** Function that spans the whole life-cycle of a fibril.
  94.  *
  95.  * Each fibril begins execution in this function.  Then the function
  96.  * implementing the fibril logic is called.  After its return, the return value
  97.  * is saved for a potentional joiner. If the joiner exists, it is woken up. The
  98.  * fibril then switches to another fibril, which cleans up after it.
  99.  */
  100. void fibril_main(void)
  101. {
  102.     fibril_t *f = __tcb_get()->fibril_data;
  103.  
  104.     f->retval = f->func(f->arg);
  105.  
  106.     /*
  107.      * If there is a joiner, wake it up and save our return value.
  108.      */
  109.     if (f->joiner) {
  110.         list_append(&f->joiner->link, &ready_list);
  111.         f->joiner->joinee_retval = f->retval;
  112.     }
  113.  
  114.     fibril_schedule_next_adv(FIBRIL_FROM_DEAD);
  115.     /* not reached */
  116. }
  117.  
  118. /** Schedule next fibril.
  119.  *
  120.  * If calling with FIBRIL_TO_MANAGER parameter, the async_futex should be
  121.  * held.
  122.  *
  123.  * @param stype     One of FIBRIL_SLEEP, FIBRIL_PREEMPT, FIBRIL_TO_MANAGER,
  124.  *          FIBRIL_FROM_MANAGER, FIBRIL_FROM_DEAD. The parameter
  125.  *          describes the circumstances of the switch.
  126.  * @return      Return 0 if there is no ready fibril,
  127.  *          return 1 otherwise.
  128.  */
  129. int fibril_schedule_next_adv(fibril_switch_type_t stype)
  130. {
  131.     fibril_t *srcf, *dstf;
  132.     int retval = 0;
  133.    
  134.     futex_down(&fibril_futex);
  135.  
  136.     if (stype == FIBRIL_PREEMPT && list_empty(&ready_list))
  137.         goto ret_0;
  138.     if (stype == FIBRIL_SLEEP) {
  139.         if (list_empty(&ready_list) && list_empty(&serialized_list))
  140.             goto ret_0;
  141.     }
  142.  
  143.     if (stype == FIBRIL_FROM_MANAGER) {
  144.         if (list_empty(&ready_list) && list_empty(&serialized_list))
  145.             goto ret_0;
  146.         /*
  147.          * Do not preempt if there is not sufficient count of thread
  148.          * managers.
  149.          */
  150.         if (list_empty(&serialized_list) && fibrils_in_manager <=
  151.             serialized_threads) {
  152.             goto ret_0;
  153.         }
  154.     }
  155.     /* If we are going to manager and none exists, create it */
  156.     if (stype == FIBRIL_TO_MANAGER || stype == FIBRIL_FROM_DEAD) {
  157.         while (list_empty(&manager_list)) {
  158.             futex_up(&fibril_futex);
  159.             async_create_manager();
  160.             futex_down(&fibril_futex);
  161.         }
  162.     }
  163.    
  164.     srcf = __tcb_get()->fibril_data;
  165.     if (stype != FIBRIL_FROM_DEAD) {
  166.         /* Save current state */
  167.         if (!context_save(&srcf->ctx)) {
  168.             if (serialization_count)
  169.                 srcf->flags &= ~FIBRIL_SERIALIZED;
  170.             if (srcf->clean_after_me) {
  171.                 /*
  172.                  * Cleanup after the dead fibril from which we
  173.                  * restored context here.
  174.                  */
  175.                 free(srcf->clean_after_me->stack);
  176.                 fibril_teardown(srcf->clean_after_me);
  177.                 srcf->clean_after_me = NULL;
  178.             }
  179.             return 1;   /* futex_up already done here */
  180.         }
  181.  
  182.         /* Save myself to the correct run list */
  183.         if (stype == FIBRIL_PREEMPT)
  184.             list_append(&srcf->link, &ready_list);
  185.         else if (stype == FIBRIL_FROM_MANAGER) {
  186.             list_append(&srcf->link, &manager_list);
  187.             fibrils_in_manager--;
  188.         } else {   
  189.             /*
  190.              * If stype == FIBRIL_TO_MANAGER, don't put ourselves to
  191.              * any list, we should already be somewhere, or we will
  192.              * be lost.
  193.              *
  194.              * The stype == FIBRIL_SLEEP case is similar. The fibril
  195.              * has an external refernce which can be used to wake it
  196.              * up once that time has come.
  197.              */
  198.         }
  199.     }
  200.  
  201.     /* Choose a new fibril to run */
  202.     if (stype == FIBRIL_TO_MANAGER || stype == FIBRIL_FROM_DEAD) {
  203.         dstf = list_get_instance(manager_list.next, fibril_t, link);
  204.         if (serialization_count && stype == FIBRIL_TO_MANAGER) {
  205.             serialized_threads++;
  206.             srcf->flags |= FIBRIL_SERIALIZED;
  207.         }
  208.         fibrils_in_manager++;
  209.  
  210.         if (stype == FIBRIL_FROM_DEAD)
  211.             dstf->clean_after_me = srcf;
  212.     } else {
  213.         if (!list_empty(&serialized_list)) {
  214.             dstf = list_get_instance(serialized_list.next, fibril_t,
  215.                 link);
  216.             serialized_threads--;
  217.         } else {
  218.             dstf = list_get_instance(ready_list.next, fibril_t,
  219.                 link);
  220.         }
  221.     }
  222.     list_remove(&dstf->link);
  223.  
  224.     futex_up(&fibril_futex);
  225.     context_restore(&dstf->ctx);
  226.     /* not reached */
  227.  
  228. ret_0:
  229.     futex_up(&fibril_futex);
  230.     return retval;
  231. }
  232.  
  233. /** Wait for fibril to finish.
  234.  *
  235.  * Each fibril can be only joined by one other fibril. Moreover, the joiner must
  236.  * be from the same thread as the joinee.
  237.  *
  238.  * @param fid       Fibril to join.
  239.  *
  240.  * @return      Value returned by the completed fibril.
  241.  */
  242. int fibril_join(fid_t fid)
  243. {
  244.     fibril_t *f;
  245.     fibril_t *cur;
  246.  
  247.     /* Handle fid = Kernel address -> it is wait for call */
  248.     f = (fibril_t *) fid;
  249.  
  250.     /*
  251.      * The joiner is running so the joinee isn't.
  252.      */
  253.     cur = __tcb_get()->fibril_data;
  254.     f->joiner = cur;
  255.     fibril_schedule_next_adv(FIBRIL_SLEEP);
  256.  
  257.     /*
  258.      * The joinee fills in the return value.
  259.      */
  260.     return cur->joinee_retval;
  261. }
  262.  
  263. /** Create a new fibril.
  264.  *
  265.  * @param func      Implementing function of the new fibril.
  266.  * @param arg       Argument to pass to func.
  267.  *
  268.  * @return      Return 0 on failure or TLS of the new fibril.
  269.  */
  270. fid_t fibril_create(int (*func)(void *), void *arg)
  271. {
  272.     fibril_t *f;
  273.  
  274.     f = fibril_setup();
  275.     if (!f)
  276.         return 0;
  277.     f->stack = (char *) malloc(FIBRIL_INITIAL_STACK_PAGES_NO *
  278.         getpagesize());
  279.  
  280.     if (!f->stack) {
  281.         fibril_teardown(f);
  282.         return 0;
  283.     }
  284.  
  285.     f->arg = arg;
  286.     f->func = func;
  287.     f->clean_after_me = NULL;
  288.     f->joiner = NULL;
  289.     f->joinee_retval = 0;
  290.     f->retval = 0;
  291.     f->flags = 0;
  292.  
  293.     context_save(&f->ctx);
  294.     context_set(&f->ctx, FADDR(fibril_main), f->stack,
  295.         FIBRIL_INITIAL_STACK_PAGES_NO * getpagesize(), f->tcb);
  296.  
  297.     return (fid_t) f;
  298. }
  299.  
  300. /** Add a fibril to the ready list.
  301.  *
  302.  * @param fid       Pinter to the fibril structure of the fibril to be added.
  303.  */
  304. void fibril_add_ready(fid_t fid)
  305. {
  306.     fibril_t *f;
  307.  
  308.     f = (fibril_t *) fid;
  309.     futex_down(&fibril_futex);
  310.     if ((f->flags & FIBRIL_SERIALIZED))
  311.         list_append(&f->link, &serialized_list);
  312.     else
  313.         list_append(&f->link, &ready_list);
  314.     futex_up(&fibril_futex);
  315. }
  316.  
  317. /** Add a fibril to the manager list.
  318.  *
  319.  * @param fid       Pinter to the fibril structure of the fibril to be added.
  320.  */
  321. void fibril_add_manager(fid_t fid)
  322. {
  323.     fibril_t *f;
  324.  
  325.     f = (fibril_t *) fid;
  326.  
  327.     futex_down(&fibril_futex);
  328.     list_append(&f->link, &manager_list);
  329.     futex_up(&fibril_futex);
  330. }
  331.  
  332. /** Remove one manager from the manager list. */
  333. void fibril_remove_manager(void)
  334. {
  335.     futex_down(&fibril_futex);
  336.     if (list_empty(&manager_list)) {
  337.         futex_up(&fibril_futex);
  338.         return;
  339.     }
  340.     list_remove(manager_list.next);
  341.     futex_up(&fibril_futex);
  342. }
  343.  
  344. /** Return fibril id of the currently running fibril.
  345.  *
  346.  * @return      Fibril ID of the currently running pseudo thread.
  347.  */
  348. fid_t fibril_get_id(void)
  349. {
  350.     return (fid_t) __tcb_get()->fibril_data;
  351. }
  352.  
  353. /** Disable preemption
  354.  *
  355.  * If the fibril wants to send several message in a row and does not want to be
  356.  * preempted, it should start async_serialize_start() in the beginning of
  357.  * communication and async_serialize_end() in the end. If it is a true
  358.  * multithreaded application, it should protect the communication channel by a
  359.  * futex as well. Interrupt messages can still be preempted.
  360.  */
  361. void fibril_inc_sercount(void)
  362. {
  363.     serialization_count++;
  364. }
  365.  
  366. /** Restore the preemption counter to the previous state. */
  367. void fibril_dec_sercount(void)
  368. {
  369.     serialization_count--;
  370. }
  371.  
  372. /** @}
  373.  */
  374.  
  375.