task.c

00001 /*
00002  * Copyright (c) 2009 Martin Decky
00003  * Copyright (c) 2009 Jiri Svoboda
00004  * All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions
00008  * are met:
00009  *
00010  * - Redistributions of source code must retain the above copyright
00011  *   notice, this list of conditions and the following disclaimer.
00012  * - Redistributions in binary form must reproduce the above copyright
00013  *   notice, this list of conditions and the following disclaimer in the
00014  *   documentation and/or other materials provided with the distribution.
00015  * - The name of the author may not be used to endorse or promote products
00016  *   derived from this software without specific prior written permission.
00017  *
00018  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00019  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00020  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00021  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00023  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00024  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00025  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00026  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00027  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00028  */
00029 
00034 #include <ipc/ipc.h>
00035 #include <adt/hash_table.h>
00036 #include <bool.h>
00037 #include <errno.h>
00038 #include <assert.h>
00039 #include <stdio.h>
00040 #include <macros.h>
00041 #include <malloc.h>
00042 #include "task.h"
00043 #include "ns.h"
00044 
00045 #define TASK_HASH_TABLE_CHAINS  256
00046 #define P2I_HASH_TABLE_CHAINS   256
00047 
00048 /* TODO:
00049  *
00050  * As there is currently no convention that each task has to be waited
00051  * for, the NS can leak memory because of the zombie tasks.
00052  *
00053  */
00054 
00056 typedef struct {
00057         link_t link;
00058         
00059         task_id_t id;    
00060         bool finished;   
00061         bool have_rval;  
00062         int retval;      
00063 } hashed_task_t;
00064 
00073 static hash_index_t task_hash(unsigned long key[])
00074 {
00075         assert(key);
00076         return (LOWER32(key[0]) % TASK_HASH_TABLE_CHAINS);
00077 }
00078 
00088 static int task_compare(unsigned long key[], hash_count_t keys, link_t *item)
00089 {
00090         assert(key);
00091         assert(keys <= 2);
00092         assert(item);
00093         
00094         hashed_task_t *ht = hash_table_get_instance(item, hashed_task_t, link);
00095         
00096         if (keys == 2)
00097                 return ((LOWER32(key[1]) == UPPER32(ht->id))
00098                     && (LOWER32(key[0]) == LOWER32(ht->id)));
00099         else
00100                 return (LOWER32(key[0]) == LOWER32(ht->id));
00101 }
00102 
00108 static void task_remove(link_t *item)
00109 {
00110         assert(item);
00111         free(hash_table_get_instance(item, hashed_task_t, link));
00112 }
00113 
00115 static hash_table_operations_t task_hash_table_ops = {
00116         .hash = task_hash,
00117         .compare = task_compare,
00118         .remove_callback = task_remove
00119 };
00120 
00122 static hash_table_t task_hash_table;
00123 
00124 typedef struct {
00125         link_t link;
00126         sysarg_t in_phone_hash;  
00127         task_id_t id;            
00128 } p2i_entry_t;
00129 
00137 static hash_index_t p2i_hash(unsigned long key[])
00138 {
00139         assert(key);
00140         return (key[0] % TASK_HASH_TABLE_CHAINS);
00141 }
00142 
00152 static int p2i_compare(unsigned long key[], hash_count_t keys, link_t *item)
00153 {
00154         assert(key);
00155         assert(keys == 1);
00156         assert(item);
00157         
00158         p2i_entry_t *entry = hash_table_get_instance(item, p2i_entry_t, link);
00159         
00160         return (key[0] == entry->in_phone_hash);
00161 }
00162 
00168 static void p2i_remove(link_t *item)
00169 {
00170         assert(item);
00171         free(hash_table_get_instance(item, p2i_entry_t, link));
00172 }
00173 
00175 static hash_table_operations_t p2i_ops = {
00176         .hash = p2i_hash,
00177         .compare = p2i_compare,
00178         .remove_callback = p2i_remove
00179 };
00180 
00182 static hash_table_t phone_to_id;
00183 
00185 typedef struct {
00186         link_t link;
00187         task_id_t id;         
00188         ipc_callid_t callid;  
00189 } pending_wait_t;
00190 
00191 static link_t pending_wait;
00192 
00193 int task_init(void)
00194 {
00195         if (!hash_table_create(&task_hash_table, TASK_HASH_TABLE_CHAINS,
00196             2, &task_hash_table_ops)) {
00197                 printf(NAME ": No memory available for tasks\n");
00198                 return ENOMEM;
00199         }
00200         
00201         if (!hash_table_create(&phone_to_id, P2I_HASH_TABLE_CHAINS,
00202             1, &p2i_ops)) {
00203                 printf(NAME ": No memory available for tasks\n");
00204                 return ENOMEM;
00205         }
00206         
00207         list_initialize(&pending_wait);
00208         return EOK;
00209 }
00210 
00212 void process_pending_wait(void)
00213 {
00214         link_t *cur;
00215         task_exit_t texit;
00216         
00217 loop:
00218         for (cur = pending_wait.next; cur != &pending_wait; cur = cur->next) {
00219                 pending_wait_t *pr = list_get_instance(cur, pending_wait_t, link);
00220                 
00221                 unsigned long keys[2] = {
00222                         LOWER32(pr->id),
00223                         UPPER32(pr->id)
00224                 };
00225                 
00226                 link_t *link = hash_table_find(&task_hash_table, keys);
00227                 if (!link)
00228                         continue;
00229                 
00230                 hashed_task_t *ht = hash_table_get_instance(link, hashed_task_t, link);
00231                 if (!ht->finished)
00232                         continue;
00233                 
00234                 if (!(pr->callid & IPC_CALLID_NOTIFICATION)) {
00235                         texit = ht->have_rval ? TASK_EXIT_NORMAL :
00236                             TASK_EXIT_UNEXPECTED;
00237                         ipc_answer_2(pr->callid, EOK, texit,
00238                             ht->retval);
00239                 }
00240                 
00241                 hash_table_remove(&task_hash_table, keys, 2);
00242                 list_remove(cur);
00243                 free(pr);
00244                 goto loop;
00245         }
00246 }
00247 
00248 void wait_for_task(task_id_t id, ipc_call_t *call, ipc_callid_t callid)
00249 {
00250         sysarg_t retval;
00251         task_exit_t texit;
00252         
00253         unsigned long keys[2] = {
00254                 LOWER32(id),
00255                 UPPER32(id)
00256         };
00257         
00258         link_t *link = hash_table_find(&task_hash_table, keys);
00259         hashed_task_t *ht = (link != NULL) ?
00260             hash_table_get_instance(link, hashed_task_t, link) : NULL;
00261         
00262         if (ht == NULL) {
00263                 /* No such task exists. */
00264                 ipc_answer_0(callid, ENOENT);
00265                 return;
00266         }
00267         
00268         if (!ht->finished) {
00269                 /* Add to pending list */
00270                 pending_wait_t *pr =
00271                     (pending_wait_t *) malloc(sizeof(pending_wait_t));
00272                 if (!pr) {
00273                         retval = ENOMEM;
00274                         goto out;
00275                 }
00276                 
00277                 link_initialize(&pr->link);
00278                 pr->id = id;
00279                 pr->callid = callid;
00280                 list_append(&pr->link, &pending_wait);
00281                 return;
00282         }
00283         
00284         hash_table_remove(&task_hash_table, keys, 2);
00285         retval = EOK;
00286         
00287 out:
00288         if (!(callid & IPC_CALLID_NOTIFICATION)) {
00289                 texit = ht->have_rval ? TASK_EXIT_NORMAL : TASK_EXIT_UNEXPECTED;
00290                 ipc_answer_2(callid, retval, texit, ht->retval);
00291         }
00292 }
00293 
00294 int ns_task_id_intro(ipc_call_t *call)
00295 {
00296         unsigned long keys[2];
00297         
00298         task_id_t id = MERGE_LOUP32(IPC_GET_ARG1(*call), IPC_GET_ARG2(*call));
00299         keys[0] = call->in_phone_hash;
00300         
00301         link_t *link = hash_table_find(&phone_to_id, keys);
00302         if (link != NULL)
00303                 return EEXISTS;
00304         
00305         p2i_entry_t *entry = (p2i_entry_t *) malloc(sizeof(p2i_entry_t));
00306         if (entry == NULL)
00307                 return ENOMEM;
00308         
00309         hashed_task_t *ht = (hashed_task_t *) malloc(sizeof(hashed_task_t));
00310         if (ht == NULL)
00311                 return ENOMEM;
00312         
00313         /*
00314          * Insert into the phone-to-id map.
00315          */
00316         
00317         link_initialize(&entry->link);
00318         entry->in_phone_hash = call->in_phone_hash;
00319         entry->id = id;
00320         hash_table_insert(&phone_to_id, keys, &entry->link);
00321         
00322         /*
00323          * Insert into the main table.
00324          */
00325         
00326         keys[0] = LOWER32(id);
00327         keys[1] = UPPER32(id);
00328         
00329         link_initialize(&ht->link);
00330         ht->id = id;
00331         ht->finished = false;
00332         ht->have_rval = false;
00333         ht->retval = -1;
00334         hash_table_insert(&task_hash_table, keys, &ht->link);
00335         
00336         return EOK;
00337 }
00338 
00339 static int get_id_by_phone(sysarg_t phone_hash, task_id_t *id)
00340 {
00341         unsigned long keys[1] = {phone_hash};
00342         
00343         link_t *link = hash_table_find(&phone_to_id, keys);
00344         if (link == NULL)
00345                 return ENOENT;
00346         
00347         p2i_entry_t *entry = hash_table_get_instance(link, p2i_entry_t, link);
00348         *id = entry->id;
00349         
00350         return EOK;
00351 }
00352 
00353 int ns_task_retval(ipc_call_t *call)
00354 {
00355         task_id_t id;
00356         int rc = get_id_by_phone(call->in_phone_hash, &id);
00357         if (rc != EOK)
00358                 return rc;
00359         
00360         unsigned long keys[2] = {
00361                 LOWER32(id),
00362                 UPPER32(id)
00363         };
00364         
00365         link_t *link = hash_table_find(&task_hash_table, keys);
00366         hashed_task_t *ht = (link != NULL) ?
00367             hash_table_get_instance(link, hashed_task_t, link) : NULL;
00368         
00369         if ((ht == NULL) || (ht->finished))
00370                 return EINVAL;
00371         
00372         ht->finished = true;
00373         ht->have_rval = true;
00374         ht->retval = IPC_GET_ARG1(*call);
00375         
00376         return EOK;
00377 }
00378 
00379 int ns_task_disconnect(ipc_call_t *call)
00380 {
00381         unsigned long keys[2];
00382         
00383         task_id_t id;
00384         int rc = get_id_by_phone(call->in_phone_hash, &id);
00385         if (rc != EOK)
00386                 return rc;
00387         
00388         /* Delete from phone-to-id map. */
00389         keys[0] = call->in_phone_hash;
00390         hash_table_remove(&phone_to_id, keys, 1);
00391         
00392         /* Mark task as finished. */
00393         keys[0] = LOWER32(id);
00394         keys[1] = UPPER32(id);
00395         
00396         link_t *link = hash_table_find(&task_hash_table, keys);
00397         hashed_task_t *ht =
00398             hash_table_get_instance(link, hashed_task_t, link);
00399         if (ht == NULL)
00400                 return EOK;
00401         
00402         ht->finished = true;
00403         
00404         return EOK;
00405 }
00406 

Generated on Thu Jun 2 07:45:46 2011 for HelenOS/USB by  doxygen 1.4.7