Subversion Repositories HelenOS

Rev

Rev 4618 | Rev 4620 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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