Subversion Repositories HelenOS-historic

Rev

Rev 1351 | Rev 1405 | Go to most recent revision | 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. /**
  30.  * Asynchronous library
  31.  *
  32.  * The aim of this library is facilitating writing programs utilizing
  33.  * the asynchronous nature of Helenos IPC, yet using a normal way
  34.  * of programming.
  35.  *
  36.  * You should be able to write very simple multithreaded programs,
  37.  * the async framework will automatically take care of most synchronization
  38.  * problems.
  39.  *
  40.  * Default semantics:
  41.  * - send() - send asynchronously. If the kernel refuses to send more
  42.  *            messages, [ try to get responses from kernel, if nothing
  43.  *            found, might try synchronous ]
  44.  *
  45.  * Example of use:
  46.  *
  47.  * 1) Multithreaded client application
  48.  *  create_thread(thread1);
  49.  *  create_thread(thread2);
  50.  *  ...
  51.  *  
  52.  *  thread1() {
  53.  *        conn = ipc_connect_me_to();
  54.  *        c1 = send(conn);
  55.  *        c2 = send(conn);
  56.  *        wait_for(c1);
  57.  *        wait_for(c2);
  58.  *  }
  59.  *
  60.  *
  61.  * 2) Multithreaded server application
  62.  * main() {
  63.  *      wait_for_connection(new_connection);
  64.  * }
  65.  *
  66.  *
  67.  * new_connection(int connection) {
  68.  *       accept(connection);
  69.  *       msg = get_msg();
  70.  *       handle(msg);
  71.  *       answer(msg);
  72.  *
  73.  *       msg = get_msg();
  74.  *       ....
  75.  * }
  76.  */
  77. #include <futex.h>
  78. #include <async.h>
  79. #include <psthread.h>
  80. #include <stdio.h>
  81. #include <libadt/hash_table.h>
  82. #include <libadt/list.h>
  83. #include <ipc/ipc.h>
  84. #include <assert.h>
  85. #include <errno.h>
  86.  
  87. static atomic_t conn_futex = FUTEX_INITIALIZER;
  88. static hash_table_t conn_hash_table;
  89.  
  90. typedef struct {
  91.     link_t link;
  92.     ipc_callid_t callid;
  93.     ipc_call_t call;
  94. } msg_t;
  95.  
  96. typedef struct {
  97.     link_t link;
  98.     ipcarg_t in_phone_hash;     /**< Incoming phone hash. */
  99.     link_t msg_queue;              /**< Messages that should be delivered to this thread */
  100.     pstid_t ptid;                /**< Thread associated with this connection */
  101.     int active;                     /**< If this thread is currently active */
  102.     int opened;                    /* If the connection was accepted */
  103.     /* Structures for connection opening packet */
  104.     ipc_callid_t callid;
  105.     ipc_call_t call;
  106. } connection_t;
  107.  
  108. __thread connection_t *PS_connection;
  109.  
  110. /* Hash table functions */
  111.  
  112. #define ASYNC_HASH_TABLE_CHAINS 32
  113.  
  114. static hash_index_t conn_hash(unsigned long *key)
  115. {
  116.     assert(key);
  117.     return ((*key) >> 4) % ASYNC_HASH_TABLE_CHAINS;
  118. }
  119.  
  120. static int conn_compare(unsigned long key[], hash_count_t keys, link_t *item)
  121. {
  122.     connection_t *hs;
  123.  
  124.     hs = hash_table_get_instance(item, connection_t, link);
  125.    
  126.     return key[0] == hs->in_phone_hash;
  127. }
  128.  
  129. static void conn_remove(link_t *item)
  130. {
  131.     free(hash_table_get_instance(item, connection_t, link));
  132. }
  133.  
  134.  
  135. /** Operations for NS hash table. */
  136. static hash_table_operations_t conn_hash_table_ops = {
  137.     .hash = conn_hash,
  138.     .compare = conn_compare,
  139.     .remove_callback = conn_remove
  140. };
  141.  
  142. /** Try to route a call to an appropriate connection thread
  143.  *
  144.  */
  145. static int route_call(ipc_callid_t callid, ipc_call_t *call)
  146. {
  147.     connection_t *conn;
  148.     msg_t *msg;
  149.     link_t *hlp;
  150.     unsigned long key;
  151.  
  152.     futex_down(&conn_futex);
  153.  
  154.     key = call->in_phone_hash;
  155.     hlp = hash_table_find(&conn_hash_table, &key);
  156.     if (!hlp) {
  157.         futex_up(&conn_futex);
  158.         return 0;
  159.     }
  160.     conn = hash_table_get_instance(hlp, connection_t, link);
  161.  
  162.     msg = malloc(sizeof(*msg));
  163.     msg->callid = callid;
  164.     msg->call = *call;
  165.     list_append(&msg->link, &conn->msg_queue);
  166.    
  167.     if (!conn->active) {
  168.         conn->active = 1;
  169.         psthread_add_ready(conn->ptid);
  170.     }
  171.  
  172.     futex_up(&conn_futex);
  173.  
  174.     return 1;
  175. }
  176.  
  177. ipc_callid_t async_get_call(ipc_call_t *call)
  178. {
  179.     msg_t *msg;
  180.     ipc_callid_t callid;
  181.     connection_t *conn;
  182.    
  183.     futex_down(&conn_futex);
  184.  
  185.     conn = PS_connection;
  186.     /* If nothing in queue, wait until something appears */
  187.     if (list_empty(&conn->msg_queue)) {
  188.         conn->active = 0;
  189.         psthread_schedule_next_adv(PS_TO_MANAGER);
  190.     }
  191.    
  192.     msg = list_get_instance(conn->msg_queue.next, msg_t, link);
  193.     list_remove(&msg->link);
  194.     callid = msg->callid;
  195.     *call = msg->call;
  196.     free(msg);
  197.    
  198.     futex_up(&conn_futex);
  199.     return callid;
  200. }
  201.  
  202. void client_connection(ipc_callid_t callid, ipc_call_t *call)
  203. {
  204.     printf("Got connection - no handler.\n");
  205.     _exit(1);
  206. }
  207.  
  208. static int connection_thread(void  *arg)
  209. {
  210.     /* Setup thread local connection pointer */
  211.     PS_connection = (connection_t *)arg;
  212.     client_connection(PS_connection->callid, &PS_connection->call);
  213.  
  214.     futex_down(&conn_futex);
  215.     /* TODO: remove myself from connection hash table */
  216.     futex_up(&conn_futex);
  217.     /* TODO: answer all unanswered messages in queue with
  218.      *       EHANGUP */
  219. }
  220.  
  221. /** Create new thread for a new connection
  222.  *
  223.  * Creates new thread for connection, fills in connection
  224.  * structures and inserts it into the hash table, so that
  225.  * later we can easily do routing of messages to particular
  226.  * threads.
  227.  */
  228. static void new_connection(ipc_callid_t callid, ipc_call_t *call)
  229. {
  230.     pstid_t ptid;
  231.     connection_t *conn;
  232.     unsigned long key;
  233.  
  234.     conn = malloc(sizeof(*conn));
  235.     if (!conn) {
  236.         ipc_answer_fast(callid, ENOMEM, 0, 0);
  237.         return;
  238.     }
  239.     conn->in_phone_hash = IPC_GET_ARG3(*call);
  240.     list_initialize(&conn->msg_queue);
  241.     conn->opened = 0;
  242.     conn->ptid = psthread_create(connection_thread, conn);
  243.     conn->callid = callid;
  244.     conn->call = *call;
  245.     conn->active = 1; /* We will activate it asap */
  246.     list_initialize(&conn->link);
  247.     if (!conn->ptid) {
  248.         free(conn);
  249.         ipc_answer_fast(callid, ENOMEM, 0, 0);
  250.         return;
  251.     }
  252.     key = conn->in_phone_hash;
  253.     futex_down(&conn_futex);
  254.     /* Add connection to hash table */
  255.     hash_table_insert(&conn_hash_table, &key, &conn->link);
  256.     futex_up(&conn_futex);
  257.  
  258.     psthread_add_ready(conn->ptid);
  259. }
  260.  
  261. /** Handle call to a task */
  262. static void handle_call(ipc_callid_t callid, ipc_call_t *call)
  263. {
  264.     if (route_call(callid, call))
  265.         return;
  266.  
  267.     switch (IPC_GET_METHOD(*call)) {
  268.     case IPC_M_INTERRUPT:
  269.         break;
  270.     case IPC_M_CONNECT_ME_TO:
  271.         /* Open new connection with thread etc. */
  272.         new_connection(callid, call);
  273.         break;
  274.     default:
  275.         ipc_answer_fast(callid, EHANGUP, 0, 0);
  276.     }
  277. }
  278.  
  279. /** Endless loop dispatching incoming calls and answers */
  280. int async_manager()
  281. {
  282.     ipc_call_t call;
  283.     ipc_callid_t callid;
  284.  
  285.     while (1) {
  286.         if (psthread_schedule_next_adv(PS_FROM_MANAGER)) {
  287.             futex_up(&conn_futex); /* conn_futex is always held
  288.                         * when entering manager thread
  289.                         */
  290.             continue;
  291.         }
  292.         callid = ipc_wait_cycle(&call,SYNCH_NO_TIMEOUT,SYNCH_BLOCKING);
  293.  
  294.         if (callid & IPC_CALLID_ANSWERED)
  295.             continue;
  296.         handle_call(callid, &call);
  297.     }
  298. }
  299.  
  300. static int async_manager_thread(void *arg)
  301. {
  302.     futex_up(&conn_futex); /* conn_futex is always locked when entering
  303.                 * manager */
  304.     async_manager();
  305. }
  306.  
  307. /** Add one manager to manager list */
  308. void async_create_manager(void)
  309. {
  310.     pstid_t ptid;
  311.  
  312.     ptid = psthread_create(async_manager_thread, NULL);
  313.     psthread_add_manager(ptid);
  314. }
  315.  
  316. /** Remove one manager from manager list */
  317. void async_destroy_manager(void)
  318. {
  319.     psthread_remove_manager();
  320. }
  321.  
  322. /** Initialize internal structures needed for async manager */
  323. int _async_init(void)
  324. {
  325.     if (!hash_table_create(&conn_hash_table, ASYNC_HASH_TABLE_CHAINS, 1, &conn_hash_table_ops)) {
  326.         printf("%s: cannot create hash table\n", "async");
  327.         return ENOMEM;
  328.     }
  329.    
  330. }
  331.