source: mainline/uspace/srv/ns/task.c@ bf1733d3

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since bf1733d3 was 1c635d6, checked in by Martin Sucha <sucha14@…>, 11 years ago

Do not hold a task's return value after it has disconnected.

Holding the task's return value meant that if nobody waited
for task's result, it polluted NS's memory. This was apparently
done because of a race between spawning a task and waiting for it.

We solve this problem in another way: ns discards the return value
as soon as the task disconnects from it. This typically happens
when the task finishes its execution. In order to avoid the race,
we send the wait request to ns while spawning the task (i.e. when
we talk to the loader), but before we allow the loaded program
to run.

Fixes #132

  • Property mode set to 100644
File size: 7.9 KB
Line 
1/*
2 * Copyright (c) 2009 Martin Decky
3 * Copyright (c) 2009 Jiri Svoboda
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>
35#include <adt/hash_table.h>
36#include <stdbool.h>
37#include <errno.h>
38#include <assert.h>
39#include <stdio.h>
40#include <macros.h>
41#include <malloc.h>
42#include <types/task.h>
43#include "task.h"
44#include "ns.h"
45
46
47/** Task hash table item. */
48typedef struct {
49 ht_link_t link;
50
51 task_id_t id; /**< Task ID. */
52 bool finished; /**< Task is done. */
53 bool have_rval; /**< Task returned a value. */
54 int retval; /**< The return value. */
55} hashed_task_t;
56
57
58static size_t task_key_hash(void *key)
59{
60 return *(task_id_t*)key;
61}
62
63static size_t task_hash(const ht_link_t *item)
64{
65 hashed_task_t *ht = hash_table_get_inst(item, hashed_task_t, link);
66 return ht->id;
67}
68
69static bool task_key_equal(void *key, const ht_link_t *item)
70{
71 hashed_task_t *ht = hash_table_get_inst(item, hashed_task_t, link);
72 return ht->id == *(task_id_t*)key;
73}
74
75/** Perform actions after removal of item from the hash table. */
76static void task_remove(ht_link_t *item)
77{
78 free(hash_table_get_inst(item, hashed_task_t, link));
79}
80
81/** Operations for task hash table. */
82static hash_table_ops_t task_hash_table_ops = {
83 .hash = task_hash,
84 .key_hash = task_key_hash,
85 .key_equal = task_key_equal,
86 .equal = NULL,
87 .remove_callback = task_remove
88};
89
90/** Task hash table structure. */
91static hash_table_t task_hash_table;
92
93typedef struct {
94 ht_link_t link;
95 sysarg_t in_phone_hash; /**< Incoming phone hash. */
96 task_id_t id; /**< Task ID. */
97} p2i_entry_t;
98
99/* phone-to-id hash table operations */
100
101static size_t p2i_key_hash(void *key)
102{
103 sysarg_t in_phone_hash = *(sysarg_t*)key;
104 return in_phone_hash;
105}
106
107static size_t p2i_hash(const ht_link_t *item)
108{
109 p2i_entry_t *entry = hash_table_get_inst(item, p2i_entry_t, link);
110 return entry->in_phone_hash;
111}
112
113static bool p2i_key_equal(void *key, const ht_link_t *item)
114{
115 sysarg_t in_phone_hash = *(sysarg_t*)key;
116 p2i_entry_t *entry = hash_table_get_inst(item, p2i_entry_t, link);
117
118 return (in_phone_hash == entry->in_phone_hash);
119}
120
121/** Perform actions after removal of item from the hash table.
122 *
123 * @param item Item that was removed from the hash table.
124 *
125 */
126static void p2i_remove(ht_link_t *item)
127{
128 assert(item);
129 free(hash_table_get_inst(item, p2i_entry_t, link));
130}
131
132/** Operations for task hash table. */
133static hash_table_ops_t p2i_ops = {
134 .hash = p2i_hash,
135 .key_hash = p2i_key_hash,
136 .key_equal = p2i_key_equal,
137 .equal = NULL,
138 .remove_callback = p2i_remove
139};
140
141/** Map phone hash to task ID */
142static hash_table_t phone_to_id;
143
144/** Pending task wait structure. */
145typedef struct {
146 link_t link;
147 task_id_t id; /**< Task ID. */
148 ipc_callid_t callid; /**< Call ID waiting for the connection */
149} pending_wait_t;
150
151static list_t pending_wait;
152
153int task_init(void)
154{
155 if (!hash_table_create(&task_hash_table, 0, 0, &task_hash_table_ops)) {
156 printf(NAME ": No memory available for tasks\n");
157 return ENOMEM;
158 }
159
160 if (!hash_table_create(&phone_to_id, 0, 0, &p2i_ops)) {
161 printf(NAME ": No memory available for tasks\n");
162 return ENOMEM;
163 }
164
165 list_initialize(&pending_wait);
166 return EOK;
167}
168
169/** Process pending wait requests */
170void process_pending_wait(void)
171{
172 task_exit_t texit;
173
174loop:
175 list_foreach(pending_wait, link, pending_wait_t, pr) {
176 ht_link_t *link = hash_table_find(&task_hash_table, &pr->id);
177 if (!link)
178 continue;
179
180 hashed_task_t *ht = hash_table_get_inst(link, hashed_task_t, link);
181 if (!ht->finished)
182 continue;
183
184 if (!(pr->callid & IPC_CALLID_NOTIFICATION)) {
185 texit = ht->have_rval ? TASK_EXIT_NORMAL :
186 TASK_EXIT_UNEXPECTED;
187 ipc_answer_2(pr->callid, EOK, texit,
188 ht->retval);
189 }
190
191 list_remove(&pr->link);
192 free(pr);
193 goto loop;
194 }
195}
196
197void wait_for_task(task_id_t id, ipc_call_t *call, ipc_callid_t callid)
198{
199 ht_link_t *link = hash_table_find(&task_hash_table, &id);
200 hashed_task_t *ht = (link != NULL) ?
201 hash_table_get_inst(link, hashed_task_t, link) : NULL;
202
203 if (ht == NULL) {
204 /* No such task exists. */
205 ipc_answer_0(callid, ENOENT);
206 return;
207 }
208
209 if (ht->finished) {
210 task_exit_t texit = ht->have_rval ? TASK_EXIT_NORMAL :
211 TASK_EXIT_UNEXPECTED;
212 ipc_answer_2(callid, EOK, texit, ht->retval);
213 return;
214 }
215
216 /* Add to pending list */
217 pending_wait_t *pr =
218 (pending_wait_t *) malloc(sizeof(pending_wait_t));
219 if (!pr) {
220 if (!(callid & IPC_CALLID_NOTIFICATION))
221 ipc_answer_0(callid, ENOMEM);
222 return;
223 }
224
225 link_initialize(&pr->link);
226 pr->id = id;
227 pr->callid = callid;
228 list_append(&pr->link, &pending_wait);
229}
230
231int ns_task_id_intro(ipc_call_t *call)
232{
233
234 task_id_t id = MERGE_LOUP32(IPC_GET_ARG1(*call), IPC_GET_ARG2(*call));
235
236 ht_link_t *link = hash_table_find(&phone_to_id, &call->in_phone_hash);
237 if (link != NULL)
238 return EEXISTS;
239
240 p2i_entry_t *entry = (p2i_entry_t *) malloc(sizeof(p2i_entry_t));
241 if (entry == NULL)
242 return ENOMEM;
243
244 hashed_task_t *ht = (hashed_task_t *) malloc(sizeof(hashed_task_t));
245 if (ht == NULL)
246 return ENOMEM;
247
248 /*
249 * Insert into the phone-to-id map.
250 */
251
252 entry->in_phone_hash = call->in_phone_hash;
253 entry->id = id;
254 hash_table_insert(&phone_to_id, &entry->link);
255
256 /*
257 * Insert into the main table.
258 */
259
260 ht->id = id;
261 ht->finished = false;
262 ht->have_rval = false;
263 ht->retval = -1;
264 hash_table_insert(&task_hash_table, &ht->link);
265
266 return EOK;
267}
268
269static int get_id_by_phone(sysarg_t phone_hash, task_id_t *id)
270{
271 ht_link_t *link = hash_table_find(&phone_to_id, &phone_hash);
272 if (link == NULL)
273 return ENOENT;
274
275 p2i_entry_t *entry = hash_table_get_inst(link, p2i_entry_t, link);
276 *id = entry->id;
277
278 return EOK;
279}
280
281int ns_task_retval(ipc_call_t *call)
282{
283 task_id_t id;
284 int rc = get_id_by_phone(call->in_phone_hash, &id);
285 if (rc != EOK)
286 return rc;
287
288 ht_link_t *link = hash_table_find(&task_hash_table, &id);
289 hashed_task_t *ht = (link != NULL) ?
290 hash_table_get_inst(link, hashed_task_t, link) : NULL;
291
292 if ((ht == NULL) || (ht->finished))
293 return EINVAL;
294
295 ht->finished = true;
296 ht->have_rval = true;
297 ht->retval = IPC_GET_ARG1(*call);
298
299 process_pending_wait();
300
301 return EOK;
302}
303
304int ns_task_disconnect(ipc_call_t *call)
305{
306 task_id_t id;
307 int rc = get_id_by_phone(call->in_phone_hash, &id);
308 if (rc != EOK)
309 return rc;
310
311 /* Delete from phone-to-id map. */
312 hash_table_remove(&phone_to_id, &call->in_phone_hash);
313
314 /* Mark task as finished. */
315 ht_link_t *link = hash_table_find(&task_hash_table, &id);
316 if (link == NULL)
317 return EOK;
318
319 hashed_task_t *ht = hash_table_get_inst(link, hashed_task_t, link);
320
321 ht->finished = true;
322
323 process_pending_wait();
324 hash_table_remove(&task_hash_table, &id);
325
326 return EOK;
327}
328
329/**
330 * @}
331 */
Note: See TracBrowser for help on using the repository browser.