Subversion Repositories HelenOS

Rev

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