Subversion Repositories HelenOS

Rev

Rev 4617 | Rev 4619 | 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
 *
51
 * The current implementation of waiting on a task is not perfect. If somebody
4618 svoboda 52
 * The caller has to make sure that it is not trying to wait
4459 decky 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. */
4617 svoboda 65
    int retval;
4459 decky 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.
4618 svoboda 86
 * @param keys Must be less than or equal to 2.
4459 decky 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
 
4618 svoboda 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
 
4459 decky 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
    }
4618 svoboda 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
    }
4459 decky 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))
4617 svoboda 241
            ipc_answer_1(pr->callid, EOK, ht->retval);
4459 decky 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;
4617 svoboda 289
        ht->retval = -1;
4459 decky 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
    };
4618 svoboda 305
 
4459 decky 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;
4618 svoboda 309
 
310
    if (ht == NULL) {
311
        retval = ENOENT;
312
        goto out;
313
    }
314
 
315
    if (!ht->destroyed) {
4459 decky 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))
4617 svoboda 335
        ipc_answer_1(callid, retval, ht->retval);
4459 decky 336
}
337
 
4618 svoboda 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
 
4617 svoboda 365
int ns_task_retval(ipc_call_t *call)
366
{
367
    task_id_t id;
368
    unsigned long keys[2];
4618 svoboda 369
    int rc;
4617 svoboda 370
 
4618 svoboda 371
    rc = get_id_by_phone(call->in_phone_hash, &id);
372
    if (rc != EOK)
373
        return rc;
4617 svoboda 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
 
4618 svoboda 385
    ht->retval = IPC_GET_ARG1(*call);
4617 svoboda 386
 
387
    return EOK;
388
}
389
 
4618 svoboda 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
 
4459 decky 417
/**
418
 * @}
419
 */