~galfy/helenos/bird-port-mainline

« back to all changes in this revision

Viewing changes to uspace/srv/ns/task.c

  • Committer: Martin Decky
  • Date: 2009-08-04 11:19:19 UTC
  • Revision ID: martin@uranus.dsrg.hide.ms.mff.cuni.cz-20090804111919-evyclddlr3v5lhmp
Initial import

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 */