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