Subversion Repositories HelenOS

Rev

Rev 2483 | Rev 2586 | 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. /** This futex serializes access to ready_list, serialized_list and manage_list.
  52.  */
  53. static atomic_t fibril_futex = FUTEX_INITIALIZER;
  54.  
  55. static LIST_INITIALIZE(ready_list);
  56. static LIST_INITIALIZE(serialized_list);
  57. static LIST_INITIALIZE(manager_list);
  58.  
  59. static void fibril_main(void);
  60.  
  61. /** Number of fibrils that are in async_serialized mode */
  62. static int serialized_fibrils;  /* Protected by async_futex */
  63. /** Thread-local count of serialization. If >0, we must not preempt */
  64. static __thread int serialization_count;
  65. /** Counter for fibrils residing in async_manager */
  66. static int fibrils_in_manager;
  67.  
  68. /** Setup fibril information into TCB structure */
  69. fibril_t *fibril_setup(void)
  70. {
  71.     fibril_t *f;
  72.     tcb_t *tcb;
  73.  
  74.     tcb = __make_tls();
  75.     if (!tcb)
  76.         return NULL;
  77.  
  78.     f = malloc(sizeof(*f));
  79.     if (!f) {
  80.         __free_tls(tcb);
  81.         return NULL;
  82.     }
  83.  
  84.     tcb->fibril_data = f;
  85.     f->tcb = tcb;
  86.  
  87.     return f;
  88. }
  89.  
  90. void fibril_teardown(fibril_t *f)
  91. {
  92.     __free_tls(f->tcb);
  93.     free(f);
  94. }
  95.  
  96. /** Function that spans the whole life-cycle of a fibril.
  97.  *
  98.  * Each fibril begins execution in this function. Then the function implementing
  99.  * the fibril logic is called.  After its return, the return value is saved.
  100.  * The fibril then switches to another fibril, which cleans up after it.
  101.  */
  102. void fibril_main(void)
  103. {
  104.     fibril_t *f = __tcb_get()->fibril_data;
  105.  
  106.     /* Call the implementing function. */
  107.     f->retval = f->func(f->arg);
  108.  
  109.     fibril_schedule_next_adv(FIBRIL_FROM_DEAD);
  110.     /* not reached */
  111. }
  112.  
  113. /** Schedule next fibril.
  114.  *
  115.  * If calling with FIBRIL_TO_MANAGER parameter, the async_futex should be
  116.  * held.
  117.  *
  118.  * @param stype     Switch type. One of FIBRIL_PREEMPT, FIBRIL_TO_MANAGER,
  119.  *          FIBRIL_FROM_MANAGER, FIBRIL_FROM_DEAD. The parameter
  120.  *          describes the circumstances of the switch.
  121.  * @return      Return 0 if there is no ready fibril,
  122.  *          return 1 otherwise.
  123.  */
  124. int fibril_schedule_next_adv(fibril_switch_type_t stype)
  125. {
  126.     fibril_t *srcf, *dstf;
  127.     int retval = 0;
  128.    
  129.     futex_down(&fibril_futex);
  130.  
  131.     if (stype == FIBRIL_PREEMPT && list_empty(&ready_list))
  132.         goto ret_0;
  133.  
  134.     if (stype == FIBRIL_FROM_MANAGER) {
  135.         if (list_empty(&ready_list) && list_empty(&serialized_list))
  136.             goto ret_0;
  137.         /*
  138.          * Do not preempt if there is not sufficient count of fibril
  139.          * managers.
  140.          */
  141.         if (list_empty(&serialized_list) && fibrils_in_manager <=
  142.             serialized_fibrils) {
  143.             goto ret_0;
  144.         }
  145.     }
  146.     /* If we are going to manager and none exists, create it */
  147.     if (stype == FIBRIL_TO_MANAGER || stype == FIBRIL_FROM_DEAD) {
  148.         while (list_empty(&manager_list)) {
  149.             futex_up(&fibril_futex);
  150.             async_create_manager();
  151.             futex_down(&fibril_futex);
  152.         }
  153.     }
  154.    
  155.     srcf = __tcb_get()->fibril_data;
  156.     if (stype != FIBRIL_FROM_DEAD) {
  157.         /* Save current state */
  158.         if (!context_save(&srcf->ctx)) {
  159.             if (serialization_count)
  160.                 srcf->flags &= ~FIBRIL_SERIALIZED;
  161.             if (srcf->clean_after_me) {
  162.                 /*
  163.                  * Cleanup after the dead fibril from which we
  164.                  * restored context here.
  165.                  */
  166.                 free(srcf->clean_after_me->stack);
  167.                 fibril_teardown(srcf->clean_after_me);
  168.                 srcf->clean_after_me = NULL;
  169.             }
  170.             return 1;   /* futex_up already done here */
  171.         }
  172.  
  173.         /* Save myself to the correct run list */
  174.         if (stype == FIBRIL_PREEMPT)
  175.             list_append(&srcf->link, &ready_list);
  176.         else if (stype == FIBRIL_FROM_MANAGER) {
  177.             list_append(&srcf->link, &manager_list);
  178.             fibrils_in_manager--;
  179.         } else {   
  180.             /*
  181.              * If stype == FIBRIL_TO_MANAGER, don't put ourselves to
  182.              * any list, we should already be somewhere, or we will
  183.              * be lost.
  184.              */
  185.         }
  186.     }
  187.  
  188.     /* Choose a new fibril to run */
  189.     if (stype == FIBRIL_TO_MANAGER || stype == FIBRIL_FROM_DEAD) {
  190.         dstf = list_get_instance(manager_list.next, fibril_t, link);
  191.         if (serialization_count && stype == FIBRIL_TO_MANAGER) {
  192.             serialized_fibrils++;
  193.             srcf->flags |= FIBRIL_SERIALIZED;
  194.         }
  195.         fibrils_in_manager++;
  196.  
  197.         if (stype == FIBRIL_FROM_DEAD)
  198.             dstf->clean_after_me = srcf;
  199.     } else {
  200.         if (!list_empty(&serialized_list)) {
  201.             dstf = list_get_instance(serialized_list.next, fibril_t,
  202.                 link);
  203.             serialized_fibrils--;
  204.         } else {
  205.             dstf = list_get_instance(ready_list.next, fibril_t,
  206.                 link);
  207.         }
  208.     }
  209.     list_remove(&dstf->link);
  210.  
  211.     futex_up(&fibril_futex);
  212.     context_restore(&dstf->ctx);
  213.     /* not reached */
  214.  
  215. ret_0:
  216.     futex_up(&fibril_futex);
  217.     return retval;
  218. }
  219.  
  220. /** Create a new fibril.
  221.  *
  222.  * @param func      Implementing function of the new fibril.
  223.  * @param arg       Argument to pass to func.
  224.  *
  225.  * @return      Return 0 on failure or TLS of the new fibril.
  226.  */
  227. fid_t fibril_create(int (*func)(void *), void *arg)
  228. {
  229.     fibril_t *f;
  230.  
  231.     f = fibril_setup();
  232.     if (!f)
  233.         return 0;
  234.     f->stack = (char *) malloc(FIBRIL_INITIAL_STACK_PAGES_NO *
  235.         getpagesize());
  236.  
  237.     if (!f->stack) {
  238.         fibril_teardown(f);
  239.         return 0;
  240.     }
  241.  
  242.     f->arg = arg;
  243.     f->func = func;
  244.     f->clean_after_me = NULL;
  245.     f->retval = 0;
  246.     f->flags = 0;
  247.  
  248.     context_save(&f->ctx);
  249.     context_set(&f->ctx, FADDR(fibril_main), f->stack,
  250.         FIBRIL_INITIAL_STACK_PAGES_NO * getpagesize(), f->tcb);
  251.  
  252.     return (fid_t) f;
  253. }
  254.  
  255. /** Add a fibril to the ready list.
  256.  *
  257.  * @param fid       Pinter to the fibril structure of the fibril to be
  258.  *          added.
  259.  */
  260. void fibril_add_ready(fid_t fid)
  261. {
  262.     fibril_t *f;
  263.  
  264.     f = (fibril_t *) fid;
  265.     futex_down(&fibril_futex);
  266.     if ((f->flags & FIBRIL_SERIALIZED))
  267.         list_append(&f->link, &serialized_list);
  268.     else
  269.         list_append(&f->link, &ready_list);
  270.     futex_up(&fibril_futex);
  271. }
  272.  
  273. /** Add a fibril to the manager list.
  274.  *
  275.  * @param fid       Pinter to the fibril structure of the fibril to be added.
  276.  */
  277. void fibril_add_manager(fid_t fid)
  278. {
  279.     fibril_t *f;
  280.  
  281.     f = (fibril_t *) fid;
  282.  
  283.     futex_down(&fibril_futex);
  284.     list_append(&f->link, &manager_list);
  285.     futex_up(&fibril_futex);
  286. }
  287.  
  288. /** Remove one manager from the manager list. */
  289. void fibril_remove_manager(void)
  290. {
  291.     futex_down(&fibril_futex);
  292.     if (list_empty(&manager_list)) {
  293.         futex_up(&fibril_futex);
  294.         return;
  295.     }
  296.     list_remove(manager_list.next);
  297.     futex_up(&fibril_futex);
  298. }
  299.  
  300. /** Return fibril id of the currently running fibril.
  301.  *
  302.  * @return      Fibril ID of the currently running pseudo thread.
  303.  */
  304. fid_t fibril_get_id(void)
  305. {
  306.     return (fid_t) __tcb_get()->fibril_data;
  307. }
  308.  
  309. /** Disable preemption
  310.  *
  311.  * If the fibril wants to send several message in a row and does not want to be
  312.  * preempted, it should start async_serialize_start() in the beginning of
  313.  * communication and async_serialize_end() in the end. If it is a true
  314.  * multithreaded application, it should protect the communication channel by a
  315.  * futex as well. Interrupt messages can still be preempted.
  316.  */
  317. void fibril_inc_sercount(void)
  318. {
  319.     serialization_count++;
  320. }
  321.  
  322. /** Restore the preemption counter to the previous state. */
  323. void fibril_dec_sercount(void)
  324. {
  325.     serialization_count--;
  326. }
  327.  
  328. /** @}
  329.  */
  330.  
  331.