Subversion Repositories HelenOS

Rev

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