Subversion Repositories HelenOS

Rev

Rev 4619 | 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;
4620 svoboda 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. */
4459 decky 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.
4618 svoboda 82
 * @param keys Must be less than or equal to 2.
4459 decky 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
 
4618 svoboda 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
 
4459 decky 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
    }
4618 svoboda 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
    }
4459 decky 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;
4620 svoboda 215
    task_exit_t texit;
4459 decky 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);
4620 svoboda 231
        if (!ht->finished)
4459 decky 232
            continue;
233
 
4620 svoboda 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
 
4459 decky 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;
4620 svoboda 251
    task_exit_t texit;
252
 
4459 decky 253
    unsigned long keys[2] = {
254
        LOWER32(id),
255
        UPPER32(id)
256
    };
4618 svoboda 257
 
4459 decky 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;
4618 svoboda 261
 
262
    if (ht == NULL) {
4619 svoboda 263
        /* No such task exists. */
4618 svoboda 264
        retval = ENOENT;
265
        goto out;
266
    }
267
 
4620 svoboda 268
    if (!ht->finished) {
4459 decky 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:
4620 svoboda 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
    }
4459 decky 291
}
292
 
4618 svoboda 293
int ns_task_id_intro(ipc_call_t *call)
294
{
295
    task_id_t id;
4619 svoboda 296
    unsigned long keys[2];
4618 svoboda 297
    link_t *link;
298
    p2i_entry_t *e;
4619 svoboda 299
    hashed_task_t *ht;
4618 svoboda 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
 
4619 svoboda 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
 
4618 svoboda 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
 
4619 svoboda 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;
4620 svoboda 331
    ht->finished = false;
332
    ht->have_rval = false;
4619 svoboda 333
    ht->retval = -1;
334
    hash_table_insert(&task_hash_table, keys, &ht->link);
335
 
4618 svoboda 336
    return EOK;
337
}
338
 
4617 svoboda 339
int ns_task_retval(ipc_call_t *call)
340
{
341
    task_id_t id;
342
    unsigned long keys[2];
4618 svoboda 343
    int rc;
4617 svoboda 344
 
4618 svoboda 345
    rc = get_id_by_phone(call->in_phone_hash, &id);
346
    if (rc != EOK)
347
        return rc;
4617 svoboda 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
 
4620 svoboda 356
    if ((ht == NULL) || ht->finished)
4617 svoboda 357
        return EINVAL;
358
 
4620 svoboda 359
    ht->finished = true;
360
    ht->have_rval = true;
4618 svoboda 361
    ht->retval = IPC_GET_ARG1(*call);
4617 svoboda 362
 
363
    return EOK;
364
}
365
 
4618 svoboda 366
int ns_task_disconnect(ipc_call_t *call)
367
{
4619 svoboda 368
    unsigned long keys[2];
369
    task_id_t id;
370
    int rc;
4618 svoboda 371
 
4619 svoboda 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. */
4618 svoboda 377
    keys[0] = call->in_phone_hash;
378
    hash_table_remove(&phone_to_id, keys, 1);
4619 svoboda 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);
4620 svoboda 387
    if (ht == NULL)
388
        return EOK;
4619 svoboda 389
 
4620 svoboda 390
    ht->finished = true;
4619 svoboda 391
 
4618 svoboda 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
 
4459 decky 412
/**
413
 * @}
414
 */