Subversion Repositories HelenOS

Rev

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

  1. /*
  2.  * Copyright (c) 2009 Martin Decky
  3.  * Copyright (c) 2009 Jiri Svoboda
  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 ns
  31.  * @{
  32.  */
  33.  
  34. #include <ipc/ipc.h>
  35. #include <adt/hash_table.h>
  36. #include <bool.h>
  37. #include <errno.h>
  38. #include <assert.h>
  39. #include <stdio.h>
  40. #include <macros.h>
  41. #include "task.h"
  42. #include "ns.h"
  43.  
  44. #define TASK_HASH_TABLE_CHAINS  256
  45. #define P2I_HASH_TABLE_CHAINS  256
  46.  
  47. static int get_id_by_phone(ipcarg_t phone_hash, task_id_t *id);
  48.  
  49. /* TODO:
  50.  *
  51.  * As there is currently no convention that each task has to be waited
  52.  * for, the NS can leak memory because of the zombie tasks.
  53.  *
  54.  */
  55.  
  56. /** Task hash table item. */
  57. typedef struct {
  58.     link_t link;
  59.     task_id_t id;   /**< Task ID. */
  60.     bool finished;  /**< Task is done. */
  61.     bool have_rval; /**< Task returned a value. */
  62.     int retval; /**< The return value. */
  63. } hashed_task_t;
  64.  
  65. /** Compute hash index into task hash table.
  66.  *
  67.  * @param key Pointer keys. However, only the first key (i.e. truncated task
  68.  *            number) is used to compute the hash index.
  69.  *
  70.  * @return Hash index corresponding to key[0].
  71.  *
  72.  */
  73. static hash_index_t task_hash(unsigned long *key)
  74. {
  75.     assert(key);
  76.     return (LOWER32(*key) % TASK_HASH_TABLE_CHAINS);
  77. }
  78.  
  79. /** Compare a key with hashed item.
  80.  *
  81.  * @param key  Array of keys.
  82.  * @param keys Must be less than or equal to 2.
  83.  * @param item Pointer to a hash table item.
  84.  *
  85.  * @return Non-zero if the key matches the item, zero otherwise.
  86.  *
  87.  */
  88. static int task_compare(unsigned long key[], hash_count_t keys, link_t *item)
  89. {
  90.     assert(key);
  91.     assert(keys <= 2);
  92.     assert(item);
  93.    
  94.     hashed_task_t *ht = hash_table_get_instance(item, hashed_task_t, link);
  95.    
  96.     if (keys == 2)
  97.         return ((LOWER32(key[1]) == UPPER32(ht->id))
  98.             && (LOWER32(key[0]) == LOWER32(ht->id)));
  99.     else
  100.         return (LOWER32(key[0]) == LOWER32(ht->id));
  101. }
  102.  
  103. /** Perform actions after removal of item from the hash table.
  104.  *
  105.  * @param item Item that was removed from the hash table.
  106.  *
  107.  */
  108. static void task_remove(link_t *item)
  109. {
  110.     assert(item);
  111.     free(hash_table_get_instance(item, hashed_task_t, link));
  112. }
  113.  
  114. /** Operations for task hash table. */
  115. static hash_table_operations_t task_hash_table_ops = {
  116.     .hash = task_hash,
  117.     .compare = task_compare,
  118.     .remove_callback = task_remove
  119. };
  120.  
  121. /** Task hash table structure. */
  122. static hash_table_t task_hash_table;
  123.  
  124. typedef struct {
  125.     link_t link;
  126.     ipcarg_t phash;    /**< Task ID. */
  127.     task_id_t id;    /**< Task ID. */
  128. } p2i_entry_t;
  129.  
  130. /** Compute hash index into task hash table.
  131.  *
  132.  * @param key Array of keys.
  133.  * @return Hash index corresponding to key[0].
  134.  *
  135.  */
  136. static hash_index_t p2i_hash(unsigned long *key)
  137. {
  138.     assert(key);
  139.     return (*key % TASK_HASH_TABLE_CHAINS);
  140. }
  141.  
  142. /** Compare a key with hashed item.
  143.  *
  144.  * @param key  Array of keys.
  145.  * @param keys Must be less than or equal to 1.
  146.  * @param item Pointer to a hash table item.
  147.  *
  148.  * @return Non-zero if the key matches the item, zero otherwise.
  149.  *
  150.  */
  151. static int p2i_compare(unsigned long key[], hash_count_t keys, link_t *item)
  152. {
  153.     assert(key);
  154.     assert(keys == 1);
  155.     assert(item);
  156.  
  157.     p2i_entry_t *e = hash_table_get_instance(item, p2i_entry_t, link);
  158.  
  159.     return (key[0] == e->phash);
  160. }
  161.  
  162. /** Perform actions after removal of item from the hash table.
  163.  *
  164.  * @param item Item that was removed from the hash table.
  165.  *
  166.  */
  167. static void p2i_remove(link_t *item)
  168. {
  169.     assert(item);
  170.     free(hash_table_get_instance(item, p2i_entry_t, link));
  171. }
  172.  
  173. /** Operations for task hash table. */
  174. static hash_table_operations_t p2i_ops = {
  175.     .hash = p2i_hash,
  176.     .compare = p2i_compare,
  177.     .remove_callback = p2i_remove
  178. };
  179.  
  180. /** Map phone hash to task ID */
  181. static hash_table_t phone_to_id;
  182.  
  183. /** Pending task wait structure. */
  184. typedef struct {
  185.     link_t link;
  186.     task_id_t id;         /**< Task ID. */
  187.     ipc_callid_t callid;  /**< Call ID waiting for the connection */
  188. } pending_wait_t;
  189.  
  190. static link_t pending_wait;
  191.  
  192. int task_init(void)
  193. {
  194.     if (!hash_table_create(&task_hash_table, TASK_HASH_TABLE_CHAINS,
  195.         2, &task_hash_table_ops)) {
  196.         printf(NAME ": No memory available for tasks\n");
  197.         return ENOMEM;
  198.     }
  199.  
  200.     if (!hash_table_create(&phone_to_id, P2I_HASH_TABLE_CHAINS,
  201.         1, &p2i_ops)) {
  202.         printf(NAME ": No memory available for tasks\n");
  203.         return ENOMEM;
  204.     }
  205.    
  206.     list_initialize(&pending_wait);
  207.    
  208.     return EOK;
  209. }
  210.  
  211. /** Process pending wait requests */
  212. void process_pending_wait(void)
  213. {
  214.     link_t *cur;
  215.     task_exit_t texit;
  216.    
  217. loop:
  218.     for (cur = pending_wait.next; cur != &pending_wait; cur = cur->next) {
  219.         pending_wait_t *pr = list_get_instance(cur, pending_wait_t, link);
  220.        
  221.         unsigned long keys[2] = {
  222.             LOWER32(pr->id),
  223.             UPPER32(pr->id)
  224.         };
  225.        
  226.         link_t *link = hash_table_find(&task_hash_table, keys);
  227.         if (!link)
  228.             continue;
  229.        
  230.         hashed_task_t *ht = hash_table_get_instance(link, hashed_task_t, link);
  231.         if (!ht->finished)
  232.             continue;
  233.        
  234.         if (!(pr->callid & IPC_CALLID_NOTIFICATION)) {
  235.             texit = ht->have_rval ? TASK_EXIT_NORMAL :
  236.                 TASK_EXIT_UNEXPECTED;
  237.             ipc_answer_2(pr->callid, EOK, texit,
  238.                 ht->retval);
  239.         }
  240.  
  241.         hash_table_remove(&task_hash_table, keys, 2);
  242.         list_remove(cur);
  243.         free(pr);
  244.         goto loop;
  245.     }
  246. }
  247.  
  248. void wait_for_task(task_id_t id, ipc_call_t *call, ipc_callid_t callid)
  249. {
  250.     ipcarg_t retval;
  251.     task_exit_t texit;
  252.  
  253.     unsigned long keys[2] = {
  254.         LOWER32(id),
  255.         UPPER32(id)
  256.     };
  257.  
  258.     link_t *link = hash_table_find(&task_hash_table, keys);
  259.     hashed_task_t *ht = (link != NULL) ?
  260.         hash_table_get_instance(link, hashed_task_t, link) : NULL;
  261.  
  262.     if (ht == NULL) {
  263.         /* No such task exists. */
  264.         retval = ENOENT;
  265.         goto out;
  266.     }
  267.  
  268.     if (!ht->finished) {
  269.         /* Add to pending list */
  270.         pending_wait_t *pr =
  271.             (pending_wait_t *) malloc(sizeof(pending_wait_t));
  272.         if (!pr) {
  273.             retval = ENOMEM;
  274.             goto out;
  275.         }
  276.        
  277.         pr->id = id;
  278.         pr->callid = callid;
  279.         list_append(&pr->link, &pending_wait);
  280.         return;
  281.     }
  282.    
  283.     hash_table_remove(&task_hash_table, keys, 2);
  284.     retval = EOK;
  285.    
  286. out:
  287.     if (!(callid & IPC_CALLID_NOTIFICATION)) {
  288.         texit = ht->have_rval ? TASK_EXIT_NORMAL : TASK_EXIT_UNEXPECTED;
  289.         ipc_answer_2(callid, retval, texit, ht->retval);
  290.     }
  291. }
  292.  
  293. int ns_task_id_intro(ipc_call_t *call)
  294. {
  295.     task_id_t id;
  296.     unsigned long keys[2];
  297.     link_t *link;
  298.     p2i_entry_t *e;
  299.     hashed_task_t *ht;
  300.  
  301.     id = MERGE_LOUP32(IPC_GET_ARG1(*call), IPC_GET_ARG2(*call));
  302.  
  303.     keys[0] = call->in_phone_hash;
  304.  
  305.     link = hash_table_find(&phone_to_id, keys);
  306.     if (link != NULL)
  307.         return EEXISTS;
  308.  
  309.     e = (p2i_entry_t *) malloc(sizeof(p2i_entry_t));
  310.     if (e == NULL)
  311.         return ENOMEM;
  312.  
  313.     ht = (hashed_task_t *) malloc(sizeof(hashed_task_t));
  314.     if (ht == NULL)
  315.         return ENOMEM;
  316.  
  317.     /* Insert to phone-to-id map. */
  318.  
  319.     link_initialize(&e->link);
  320.     e->phash = call->in_phone_hash;
  321.     e->id = id;
  322.     hash_table_insert(&phone_to_id, keys, &e->link);
  323.  
  324.     /* Insert to main table. */
  325.  
  326.     keys[0] = LOWER32(id);
  327.     keys[1] = UPPER32(id);
  328.  
  329.     link_initialize(&ht->link);
  330.     ht->id = id;
  331.     ht->finished = false;
  332.     ht->have_rval = false;
  333.     ht->retval = -1;
  334.     hash_table_insert(&task_hash_table, keys, &ht->link);
  335.  
  336.     return EOK;
  337. }
  338.  
  339. int ns_task_retval(ipc_call_t *call)
  340. {
  341.     task_id_t id;
  342.     unsigned long keys[2];
  343.     int rc;
  344.  
  345.     rc = get_id_by_phone(call->in_phone_hash, &id);
  346.     if (rc != EOK)
  347.         return rc;
  348.  
  349.     keys[0] = LOWER32(id);
  350.     keys[1] = UPPER32(id);
  351.    
  352.     link_t *link = hash_table_find(&task_hash_table, keys);
  353.     hashed_task_t *ht = (link != NULL) ?
  354.         hash_table_get_instance(link, hashed_task_t, link) : NULL;
  355.    
  356.     if ((ht == NULL) || ht->finished)
  357.         return EINVAL;
  358.  
  359.     ht->finished = true;
  360.     ht->have_rval = true;
  361.     ht->retval = IPC_GET_ARG1(*call);
  362.  
  363.     return EOK;
  364. }
  365.  
  366. int ns_task_disconnect(ipc_call_t *call)
  367. {
  368.     unsigned long keys[2];
  369.     task_id_t id;
  370.     int rc;
  371.  
  372.     rc = get_id_by_phone(call->in_phone_hash, &id);
  373.     if (rc != EOK)
  374.         return rc;
  375.  
  376.     /* Delete from phone-to-id map. */
  377.     keys[0] = call->in_phone_hash;
  378.     hash_table_remove(&phone_to_id, keys, 1);
  379.  
  380.     /* Mark task as finished. */
  381.     keys[0] = LOWER32(id);
  382.     keys[1] = UPPER32(id);
  383.  
  384.     link_t *link = hash_table_find(&task_hash_table, keys);
  385.     hashed_task_t *ht =
  386.         hash_table_get_instance(link, hashed_task_t, link);
  387.     if (ht == NULL)
  388.         return EOK;
  389.  
  390.     ht->finished = true;
  391.  
  392.     return EOK;
  393. }
  394.  
  395. static int get_id_by_phone(ipcarg_t phone_hash, task_id_t *id)
  396. {
  397.     unsigned long keys[1];
  398.     link_t *link;
  399.     p2i_entry_t *e;
  400.  
  401.     keys[0] = phone_hash;
  402.     link = hash_table_find(&phone_to_id, keys);
  403.     if (link == NULL)
  404.         return ENOENT;
  405.  
  406.     e = hash_table_get_instance(link, p2i_entry_t, link);
  407.     *id = e->id;
  408.  
  409.     return EOK;
  410. }
  411.  
  412. /**
  413.  * @}
  414.  */
  415.