Subversion Repositories HelenOS

Rev

Rev 1964 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright (C) 2006 Ondrej Palkovsky
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  *
  9.  * - Redistributions of source code must retain the above copyright
  10.  *   notice, this list of conditions and the following disclaimer.
  11.  * - Redistributions in binary form must reproduce the above copyright
  12.  *   notice, this list of conditions and the following disclaimer in the
  13.  *   documentation and/or other materials provided with the distribution.
  14.  * - The name of the author may not be used to endorse or promote products
  15.  *   derived from this software without specific prior written permission.
  16.  *
  17.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  18.  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  19.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  20.  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  21.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  22.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27.  */
  28.  
  29.  /** @addtogroup libc
  30.  * @{
  31.  */
  32. /** @file
  33.  */
  34.  
  35. #include <libadt/list.h>
  36. #include <psthread.h>
  37. #include <malloc.h>
  38. #include <unistd.h>
  39. #include <thread.h>
  40. #include <stdio.h>
  41. #include <kernel/arch/faddr.h>
  42. #include <futex.h>
  43. #include <assert.h>
  44. #include <async.h>
  45.  
  46. #ifndef PSTHREAD_INITIAL_STACK_PAGES_NO
  47. #define PSTHREAD_INITIAL_STACK_PAGES_NO 1
  48. #endif
  49.  
  50. static LIST_INITIALIZE(ready_list);
  51. static LIST_INITIALIZE(serialized_list);
  52. static LIST_INITIALIZE(manager_list);
  53.  
  54. static void psthread_exit(void) __attribute__ ((noinline));
  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()
  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 is called on entry to new uspace thread */
  94. void psthread_main(void)
  95. {
  96.     psthread_data_t *pt = __tcb_get()->pst_data;
  97.  
  98.     pt->retval = pt->func(pt->arg);
  99.  
  100.     pt->finished = 1;
  101.     if (pt->waiter)
  102.         list_append(&pt->waiter->link, &ready_list);
  103.  
  104.     psthread_schedule_next_adv(PS_FROM_DEAD);
  105. }
  106.  
  107. /** Schedule next userspace pseudo thread.
  108.  *
  109.  * If calling with PS_TO_MANAGER parameter, the async_futex should be
  110.  * held.
  111.  *
  112.  * @param tomanager If true, we are switching to next ready manager thread
  113.  *                  (if none is found, thread is exited)
  114.  * @param frommanager If true, we are switching from manager thread
  115.  * @return 0 if there is no ready pseudo thread, 1 otherwise.
  116.  */
  117. int psthread_schedule_next_adv(pschange_type ctype)
  118. {
  119.     psthread_data_t *srcpt, *dstpt;
  120.     int retval = 0;
  121.    
  122.     futex_down(&psthread_futex);
  123.  
  124.     if (ctype == PS_PREEMPT && list_empty(&ready_list))
  125.         goto ret_0;
  126.  
  127.     if (ctype == PS_FROM_MANAGER) {
  128.         if (list_empty(&ready_list) && list_empty(&serialized_list))
  129.             goto ret_0;
  130.         /* Do not preempt if there is not sufficient count of thread managers */
  131.         if (list_empty(&serialized_list) && threads_in_manager <= serialized_threads) {
  132.             goto ret_0;
  133.         }
  134.     }
  135.     /* If we are going to manager and none exists, create it */
  136.     if (ctype == PS_TO_MANAGER || ctype == PS_FROM_DEAD) {
  137.         while (list_empty(&manager_list)) {
  138.             futex_up(&psthread_futex);
  139.             async_create_manager();
  140.             futex_down(&psthread_futex);
  141.         }
  142.     }
  143.    
  144.     if (ctype != PS_FROM_DEAD) {
  145.         /* Save current state */
  146.         srcpt = __tcb_get()->pst_data;
  147.         if (!context_save(&srcpt->ctx)) {
  148.             if (serialization_count)
  149.                 srcpt->flags &= ~PSTHREAD_SERIALIZED;
  150.             return 1; // futex_up already done here
  151.         }
  152.  
  153.         /* Save myself to correct run list */
  154.         if (ctype == PS_PREEMPT)
  155.             list_append(&srcpt->link, &ready_list);
  156.         else if (ctype == PS_FROM_MANAGER) {
  157.             list_append(&srcpt->link, &manager_list);
  158.             threads_in_manager--;
  159.         } /* If ctype == PS_TO_MANAGER, don't save ourselves to any list, we should
  160.            * already be somewhere, or we will be lost */
  161.     }
  162.  
  163.     /* Choose new thread to run */
  164.     if (ctype == PS_TO_MANAGER || ctype == PS_FROM_DEAD) {
  165.         dstpt = list_get_instance(manager_list.next,psthread_data_t, link);
  166.         if (serialization_count && ctype == PS_TO_MANAGER) {
  167.             serialized_threads++;
  168.             srcpt->flags |= PSTHREAD_SERIALIZED;
  169.         }
  170.         threads_in_manager++;
  171.     } else {
  172.         if (!list_empty(&serialized_list)) {
  173.             dstpt = list_get_instance(serialized_list.next, psthread_data_t, link);
  174.             serialized_threads--;
  175.         } else
  176.             dstpt = list_get_instance(ready_list.next, psthread_data_t, link);
  177.     }
  178.     list_remove(&dstpt->link);
  179.  
  180.     futex_up(&psthread_futex);
  181.     context_restore(&dstpt->ctx);
  182.  
  183. ret_0:
  184.     futex_up(&psthread_futex);
  185.     return retval;
  186. }
  187.  
  188. /** Wait for uspace pseudo thread to finish.
  189.  *
  190.  * @param psthrid Pseudo thread to wait for.
  191.  *
  192.  * @return Value returned by the finished thread.
  193.  */
  194. int psthread_join(pstid_t psthrid)
  195. {
  196.     volatile psthread_data_t *pt, *mypt;
  197.     volatile int retval;
  198.  
  199.     /* Handle psthrid = Kernel address -> it is wait for call */
  200.     pt = (psthread_data_t *) psthrid;
  201.  
  202.     /* TODO */
  203.     printf("join unsupported\n");
  204.     _exit(1);
  205.  
  206.     retval = pt->retval;
  207.  
  208.     free(pt->stack);
  209.     psthread_teardown((void *)pt);
  210.  
  211.     return retval;
  212. }
  213.  
  214. /**
  215.  * Create a userspace thread
  216.  *
  217.  * @param func Pseudo thread function.
  218.  * @param arg Argument to pass to func.
  219.  *
  220.  * @return 0 on failure, TLS of the new pseudo thread.
  221.  */
  222. pstid_t psthread_create(int (*func)(void *), void *arg)
  223. {
  224.     psthread_data_t *pt;
  225.  
  226.     pt = psthread_setup();
  227.     if (!pt)
  228.         return 0;
  229.     pt->stack = (char *) malloc(PSTHREAD_INITIAL_STACK_PAGES_NO*getpagesize());
  230.  
  231.     if (!pt->stack) {
  232.         psthread_teardown(pt);
  233.         return 0;
  234.     }
  235.  
  236.     pt->arg= arg;
  237.     pt->func = func;
  238.     pt->finished = 0;
  239.     pt->waiter = NULL;
  240.     pt->flags = 0;
  241.  
  242.     context_save(&pt->ctx);
  243.     context_set(&pt->ctx, FADDR(psthread_main), pt->stack, PSTHREAD_INITIAL_STACK_PAGES_NO*getpagesize(),
  244.             pt->tcb);
  245.  
  246.     return (pstid_t )pt;
  247. }
  248.  
  249. /** Add a thread to ready list */
  250. void psthread_add_ready(pstid_t psthrid)
  251. {
  252.     psthread_data_t *pt;
  253.  
  254.     pt = (psthread_data_t *) psthrid;
  255.     futex_down(&psthread_futex);
  256.     if ((pt->flags & PSTHREAD_SERIALIZED))
  257.         list_append(&pt->link, &serialized_list);
  258.     else
  259.         list_append(&pt->link, &ready_list);
  260.     futex_up(&psthread_futex);
  261. }
  262.  
  263. /** Add a thread to manager list */
  264. void psthread_add_manager(pstid_t psthrid)
  265. {
  266.     psthread_data_t *pt;
  267.  
  268.     pt = (psthread_data_t *) psthrid;
  269.  
  270.     futex_down(&psthread_futex);
  271.     list_append(&pt->link, &manager_list);
  272.     futex_up(&psthread_futex);
  273. }
  274.  
  275. /** Remove one manager from manager list */
  276. void psthread_remove_manager()
  277. {
  278.     futex_down(&psthread_futex);
  279.     if (list_empty(&manager_list)) {
  280.         futex_up(&psthread_futex);
  281.         return;
  282.     }
  283.     list_remove(manager_list.next);
  284.     futex_up(&psthread_futex);
  285. }
  286.  
  287. /** Return thread id of current running thread */
  288. pstid_t psthread_get_id(void)
  289. {
  290.     return (pstid_t)__tcb_get()->pst_data;
  291. }
  292.  
  293. /** Disable preemption
  294.  *
  295.  * If the thread wants to send several message in row and does not want
  296.  * to be preempted, it should start async_serialize_start() in the beginning
  297.  * of communication and async_serialize_end() in the end. If it is a
  298.  * true multithreaded application, it should protect the communication channel
  299.  * by a futex as well. Interrupt messages will can still be preempted.
  300.  */
  301. void psthread_inc_sercount(void)
  302. {
  303.     serialization_count++;
  304. }
  305.  
  306. void psthread_dec_sercount(void)
  307. {
  308.     serialization_count--;
  309. }
  310.  
  311.  
  312.  /** @}
  313.  */
  314.  
  315.  
  316.