Subversion Repositories HelenOS

Rev

Rev 4618 | 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
 */