Subversion Repositories HelenOS

Rev

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