Subversion Repositories HelenOS

Rev

Rev 2479 | Go to most recent revision | Blame | 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 <psthread.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 PSTHREAD_INITIAL_STACK_PAGES_NO
  48. #define PSTHREAD_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 psthread_main(void);
  56.  
  57. static atomic_t psthread_futex = FUTEX_INITIALIZER;
  58. /** Count of real 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 of threads residing in async_manager */
  63. static int threads_in_manager;
  64.  
  65. /** Setup psthread information into TCB structure */
  66. psthread_data_t *psthread_setup(void)
  67. {
  68.     psthread_data_t *pt;
  69.     tcb_t *tcb;
  70.  
  71.     tcb = __make_tls();
  72.     if (!tcb)
  73.         return NULL;
  74.  
  75.     pt = malloc(sizeof(*pt));
  76.     if (!pt) {
  77.         __free_tls(tcb);
  78.         return NULL;
  79.     }
  80.  
  81.     tcb->pst_data = pt;
  82.     pt->tcb = tcb;
  83.  
  84.     return pt;
  85. }
  86.  
  87. void psthread_teardown(psthread_data_t *pt)
  88. {
  89.     __free_tls(pt->tcb);
  90.     free(pt);
  91. }
  92.  
  93. /** Function that spans the whole life-cycle of a pseudo thread.
  94.  *
  95.  * Each pseudo thread begins execution in this function.
  96.  * Then the function implementing the pseudo thread logic is called.
  97.  * After its return, the return value is saved for a potentional
  98.  * joiner. If the joiner exists, it is woken up. The pseudo thread
  99.  * then switches to another pseudo thread, which cleans up after it.
  100.  */
  101. void psthread_main(void)
  102. {
  103.     psthread_data_t *pt = __tcb_get()->pst_data;
  104.  
  105.     pt->retval = pt->func(pt->arg);
  106.  
  107.     /*
  108.      * If there is a joiner, wake it up and save our return value.
  109.      */
  110.     if (pt->joiner) {
  111.         list_append(&pt->joiner->link, &ready_list);
  112.         pt->joiner->joinee_retval = pt->retval;
  113.     }
  114.  
  115.     psthread_schedule_next_adv(PS_FROM_DEAD);
  116.     /* not reached */
  117. }
  118.  
  119. /** Schedule next userspace pseudo thread.
  120.  *
  121.  * If calling with PS_TO_MANAGER parameter, the async_futex should be
  122.  * held.
  123.  *
  124.  * @param ctype     One of PS_SLEEP, PS_PREEMPT, PS_TO_MANAGER,
  125.  *          PS_FROM_MANAGER, PS_FROM_DEAD. The parameter describes
  126.  *          the circumstances of the switch.
  127.  * @return      Return 0 if there is no ready pseudo thread,
  128.  *          return 1 otherwise.
  129.  */
  130. int psthread_schedule_next_adv(pschange_type ctype)
  131. {
  132.     psthread_data_t *srcpt, *dstpt;
  133.     int retval = 0;
  134.    
  135.     futex_down(&psthread_futex);
  136.  
  137.     if (ctype == PS_PREEMPT && list_empty(&ready_list))
  138.         goto ret_0;
  139.     if (ctype == PS_SLEEP) {
  140.         if (list_empty(&ready_list) && list_empty(&serialized_list))
  141.             goto ret_0;
  142.     }
  143.  
  144.     if (ctype == PS_FROM_MANAGER) {
  145.         if (list_empty(&ready_list) && list_empty(&serialized_list))
  146.             goto ret_0;
  147.         /*
  148.          * Do not preempt if there is not sufficient count of thread
  149.          * managers.
  150.          */
  151.         if (list_empty(&serialized_list) && threads_in_manager <=
  152.             serialized_threads) {
  153.             goto ret_0;
  154.         }
  155.     }
  156.     /* If we are going to manager and none exists, create it */
  157.     if (ctype == PS_TO_MANAGER || ctype == PS_FROM_DEAD) {
  158.         while (list_empty(&manager_list)) {
  159.             futex_up(&psthread_futex);
  160.             async_create_manager();
  161.             futex_down(&psthread_futex);
  162.         }
  163.     }
  164.    
  165.     srcpt = __tcb_get()->pst_data;
  166.     if (ctype != PS_FROM_DEAD) {
  167.         /* Save current state */
  168.         if (!context_save(&srcpt->ctx)) {
  169.             if (serialization_count)
  170.                 srcpt->flags &= ~PSTHREAD_SERIALIZED;
  171.             if (srcpt->clean_after_me) {
  172.                 /*
  173.                  * Cleanup after the dead pseudo thread from
  174.                  * which we restored context here.
  175.                  */
  176.                 free(srcpt->clean_after_me->stack);
  177.                 psthread_teardown(srcpt->clean_after_me);
  178.                 srcpt->clean_after_me = NULL;
  179.             }
  180.             return 1;   /* futex_up already done here */
  181.         }
  182.  
  183.         /* Save myself to the correct run list */
  184.         if (ctype == PS_PREEMPT)
  185.             list_append(&srcpt->link, &ready_list);
  186.         else if (ctype == PS_FROM_MANAGER) {
  187.             list_append(&srcpt->link, &manager_list);
  188.             threads_in_manager--;
  189.         } else {   
  190.             /*
  191.              * If ctype == PS_TO_MANAGER, don't save ourselves to
  192.              * any list, we should already be somewhere, or we will
  193.              * be lost.
  194.              *
  195.              * The ctype == PS_SLEEP case is similar. The pseudo
  196.              * thread has an external refernce which can be used to
  197.              * wake it up once that time has come.
  198.              */
  199.         }
  200.     }
  201.  
  202.     /* Choose new thread to run */
  203.     if (ctype == PS_TO_MANAGER || ctype == PS_FROM_DEAD) {
  204.         dstpt = list_get_instance(manager_list.next, psthread_data_t,
  205.             link);
  206.         if (serialization_count && ctype == PS_TO_MANAGER) {
  207.             serialized_threads++;
  208.             srcpt->flags |= PSTHREAD_SERIALIZED;
  209.         }
  210.         threads_in_manager++;
  211.  
  212.         if (ctype == PS_FROM_DEAD)
  213.             dstpt->clean_after_me = srcpt;
  214.     } else {
  215.         if (!list_empty(&serialized_list)) {
  216.             dstpt = list_get_instance(serialized_list.next,
  217.                 psthread_data_t, link);
  218.             serialized_threads--;
  219.         } else {
  220.             dstpt = list_get_instance(ready_list.next,
  221.                 psthread_data_t, link);
  222.         }
  223.     }
  224.     list_remove(&dstpt->link);
  225.  
  226.     futex_up(&psthread_futex);
  227.     context_restore(&dstpt->ctx);
  228.     /* not reached */
  229.  
  230. ret_0:
  231.     futex_up(&psthread_futex);
  232.     return retval;
  233. }
  234.  
  235. /** Wait for uspace pseudo thread to finish.
  236.  *
  237.  * Each pseudo thread can be only joined by one other pseudo thread. Moreover,
  238.  * the joiner must be from the same thread as the joinee.
  239.  *
  240.  * @param psthrid   Pseudo thread to join.
  241.  *
  242.  * @return      Value returned by the finished thread.
  243.  */
  244. int psthread_join(pstid_t psthrid)
  245. {
  246.     psthread_data_t *pt;
  247.     psthread_data_t *cur;
  248.  
  249.     /* Handle psthrid = Kernel address -> it is wait for call */
  250.     pt = (psthread_data_t *) psthrid;
  251.  
  252.     /*
  253.      * The joiner is running so the joinee isn't.
  254.      */
  255.     cur = __tcb_get()->pst_data;
  256.     pt->joiner = cur;
  257.     psthread_schedule_next_adv(PS_SLEEP);
  258.  
  259.     /*
  260.      * The joinee fills in the return value.
  261.      */
  262.     return cur->joinee_retval;
  263. }
  264.  
  265. /** Create a userspace pseudo thread.
  266.  *
  267.  * @param func      Pseudo thread function.
  268.  * @param arg       Argument to pass to func.
  269.  *
  270.  * @return      Return 0 on failure or TLS of the new pseudo thread.
  271.  */
  272. pstid_t psthread_create(int (*func)(void *), void *arg)
  273. {
  274.     psthread_data_t *pt;
  275.  
  276.     pt = psthread_setup();
  277.     if (!pt)
  278.         return 0;
  279.     pt->stack = (char *) malloc(PSTHREAD_INITIAL_STACK_PAGES_NO *
  280.         getpagesize());
  281.  
  282.     if (!pt->stack) {
  283.         psthread_teardown(pt);
  284.         return 0;
  285.     }
  286.  
  287.     pt->arg= arg;
  288.     pt->func = func;
  289.     pt->clean_after_me = NULL;
  290.     pt->joiner = NULL;
  291.     pt->joinee_retval = 0;
  292.     pt->retval = 0;
  293.     pt->flags = 0;
  294.  
  295.     context_save(&pt->ctx);
  296.     context_set(&pt->ctx, FADDR(psthread_main), pt->stack,
  297.         PSTHREAD_INITIAL_STACK_PAGES_NO * getpagesize(), pt->tcb);
  298.  
  299.     return (pstid_t) pt;
  300. }
  301.  
  302. /** Add a thread to the ready list.
  303.  *
  304.  * @param psthrid       Pinter to the pseudo thread structure of the
  305.  *              pseudo thread to be added.
  306.  */
  307. void psthread_add_ready(pstid_t psthrid)
  308. {
  309.     psthread_data_t *pt;
  310.  
  311.     pt = (psthread_data_t *) psthrid;
  312.     futex_down(&psthread_futex);
  313.     if ((pt->flags & PSTHREAD_SERIALIZED))
  314.         list_append(&pt->link, &serialized_list);
  315.     else
  316.         list_append(&pt->link, &ready_list);
  317.     futex_up(&psthread_futex);
  318. }
  319.  
  320. /** Add a pseudo thread to the manager list.
  321.  *
  322.  * @param psthrid       Pinter to the pseudo thread structure of the
  323.  *              pseudo thread to be added.
  324.  */
  325. void psthread_add_manager(pstid_t psthrid)
  326. {
  327.     psthread_data_t *pt;
  328.  
  329.     pt = (psthread_data_t *) psthrid;
  330.  
  331.     futex_down(&psthread_futex);
  332.     list_append(&pt->link, &manager_list);
  333.     futex_up(&psthread_futex);
  334. }
  335.  
  336. /** Remove one manager from manager list */
  337. void psthread_remove_manager(void)
  338. {
  339.     futex_down(&psthread_futex);
  340.     if (list_empty(&manager_list)) {
  341.         futex_up(&psthread_futex);
  342.         return;
  343.     }
  344.     list_remove(manager_list.next);
  345.     futex_up(&psthread_futex);
  346. }
  347.  
  348. /** Return thread id of the currently running thread.
  349.  *
  350.  * @return      Pseudo thread ID of the currently running pseudo thread.
  351.  */
  352. pstid_t psthread_get_id(void)
  353. {
  354.     return (pstid_t) __tcb_get()->pst_data;
  355. }
  356.  
  357. /** Disable preemption
  358.  *
  359.  * If the thread wants to send several message in a row and does not want to be
  360.  * preempted, it should start async_serialize_start() in the beginning of
  361.  * communication and async_serialize_end() in the end. If it is a true
  362.  * multithreaded application, it should protect the communication channel by a
  363.  * futex as well. Interrupt messages can still be preempted.
  364.  */
  365. void psthread_inc_sercount(void)
  366. {
  367.     serialization_count++;
  368. }
  369.  
  370. /** Restore the preemption counter to the previous state. */
  371. void psthread_dec_sercount(void)
  372. {
  373.     serialization_count--;
  374. }
  375.  
  376. /** @}
  377.  */
  378.  
  379.