source: mainline/uspace/srv/taskman/task.c@ d4ec49e

Last change on this file since d4ec49e was d4ec49e, checked in by Matthieu Riolo <matthieu.riolo@…>, 6 years ago

taskman: Implement waiting both for retval and exit
Conflicts:

uspace/lib/c/generic/task.c
uspace/lib/c/include/task.h

  • Property mode set to 100644
File size: 9.5 KB
Line 
1/*
2 * copyright (c) 2009 martin decky
3 * copyright (c) 2009 jiri svoboda
4 * copyright (c) 2015 michal koutny
5 * all rights reserved.
6 *
7 * redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * - redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * - redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * - the name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * this software is provided by the author ``as is'' and any express or
20 * implied warranties, including, but not limited to, the implied warranties
21 * of merchantability and fitness for a particular purpose are disclaimed.
22 * in no event shall the author be liable for any direct, indirect,
23 * incidental, special, exemplary, or consequential damages (including, but
24 * not limited to, procurement of substitute goods or services; loss of use,
25 * data, or profits; or business interruption) however caused and on any
26 * theory of liability, whether in contract, strict liability, or tort
27 * (including negligence or otherwise) arising in any way out of the use of
28 * this software, even if advised of the possibility of such damage.
29 */
30
31/**
32 * locking order:
33 * - task_hash_table_lock,
34 * - pending_wait_lock.
35 *
36 * @addtogroup taskman
37 * @{
38 */
39
40#include <adt/hash_table.h>
41#include <assert.h>
42#include <async.h>
43#include <errno.h>
44#include <fibril_synch.h>
45#include <macros.h>
46#include <malloc.h>
47#include <stdbool.h>
48#include <stdio.h>
49#include <task.h>
50#include <types/task.h>
51
52#include "task.h"
53#include "taskman.h"
54
55/** what type of retval from the task we have */
56typedef enum {
57 RVAL_UNSET, /**< unset */
58 RVAL_SET, /**< retval set, e.g. by server */
59 RVAL_SET_EXIT /**< retval set, wait for expected task exit */
60} retval_t;
61
62/** task hash table item. */
63typedef struct {
64 ht_link_t link;
65
66 task_id_t id; /**< task id. */
67 task_exit_t exit; /**< task is done. */
68 retval_t retval_type; /**< task returned a value. */
69 int retval; /**< the return value. */
70} hashed_task_t;
71
72
73static size_t task_key_hash(void *key)
74{
75 return *(task_id_t*)key;
76}
77
78static size_t task_hash(const ht_link_t *item)
79{
80 hashed_task_t *ht = hash_table_get_inst(item, hashed_task_t, link);
81 return ht->id;
82}
83
84static bool task_key_equal(void *key, const ht_link_t *item)
85{
86 hashed_task_t *ht = hash_table_get_inst(item, hashed_task_t, link);
87 return ht->id == *(task_id_t*)key;
88}
89
90/** perform actions after removal of item from the hash table. */
91static void task_remove(ht_link_t *item)
92{
93 free(hash_table_get_inst(item, hashed_task_t, link));
94}
95
96/** operations for task hash table. */
97static hash_table_ops_t task_hash_table_ops = {
98 .hash = task_hash,
99 .key_hash = task_key_hash,
100 .key_equal = task_key_equal,
101 .equal = NULL,
102 .remove_callback = task_remove
103};
104
105/** Task hash table structure. */
106static hash_table_t task_hash_table;
107static FIBRIL_RWLOCK_INITIALIZE(task_hash_table_lock);
108
109/** Pending task wait structure. */
110typedef struct {
111 link_t link;
112 task_id_t id; /**< Task ID who we wait for. */
113 task_id_t waiter_id; /**< Task ID who waits. */
114 ipc_callid_t callid; /**< Call ID waiting for the connection */
115 int flags; /**< Wait flags */
116} pending_wait_t;
117
118static list_t pending_wait;
119static FIBRIL_RWLOCK_INITIALIZE(pending_wait_lock);
120
121int task_init(void)
122{
123 if (!hash_table_create(&task_hash_table, 0, 0, &task_hash_table_ops)) {
124 printf(NAME ": No memory available for tasks\n");
125 return ENOMEM;
126 }
127
128 list_initialize(&pending_wait);
129 return EOK;
130}
131
132/** Process pending wait requests
133 *
134 * Assumes task_hash_table_lock is hold (at least read)
135 */
136void process_pending_wait(void)
137{
138 fibril_rwlock_write_lock(&pending_wait_lock);
139loop:
140 list_foreach(pending_wait, link, pending_wait_t, pr) {
141 ht_link_t *link = hash_table_find(&task_hash_table, &pr->id);
142 if (!link)
143 continue;
144
145 hashed_task_t *ht = hash_table_get_inst(link, hashed_task_t, link);
146 int notify_flags = 0;
147 if (ht->exit != TASK_EXIT_RUNNING) {
148 notify_flags |= TASK_WAIT_EXIT;
149 if (ht->retval_type == RVAL_SET_EXIT) {
150 notify_flags |= TASK_WAIT_RETVAL;
151 }
152 }
153 if (ht->retval_type == RVAL_SET) {
154 notify_flags |= TASK_WAIT_RETVAL;
155 }
156
157 /*
158 * In current implementation you can wait for single retval,
159 * thus it can be never present in rest flags.
160 */
161 int rest = (~notify_flags & pr->flags) & ~TASK_WAIT_RETVAL;
162 rest &= ~TASK_WAIT_BOTH;
163 int match = notify_flags & pr->flags;
164 bool answer = !(pr->callid & IPC_CALLID_NOTIFICATION);
165 printf("%s: %x; %x, %x\n", __func__, pr->flags, rest, match);
166
167 if (match == 0) {
168 if (notify_flags & TASK_WAIT_EXIT) {
169 /* Nothing to wait for anymore */
170 if (answer) {
171 async_answer_0(pr->callid, EINVAL);
172 }
173 } else {
174 /* Maybe later */
175 continue;
176 }
177 } else if (answer) {
178 if ((pr->flags & TASK_WAIT_BOTH) && match == TASK_WAIT_EXIT) {
179 async_answer_1(pr->callid, EINVAL, ht->exit);
180 } else {
181 /* Send both exit status and retval, caller
182 * should know what is valid */
183 async_answer_3(pr->callid, EOK, ht->exit,
184 ht->retval, rest);
185 }
186
187 /* Pending wait has one more chance */
188 if (rest && (pr->flags & TASK_WAIT_BOTH)) {
189 pr->flags = rest | TASK_WAIT_BOTH;
190 continue;
191 }
192 }
193
194
195 list_remove(&pr->link);
196 free(pr);
197 goto loop;
198 }
199 fibril_rwlock_write_unlock(&pending_wait_lock);
200}
201
202void wait_for_task(task_id_t id, int flags, ipc_callid_t callid, ipc_call_t *call)
203{
204 assert(!(flags & TASK_WAIT_BOTH) ||
205 ((flags & TASK_WAIT_RETVAL) && (flags & TASK_WAIT_EXIT)));
206
207 fibril_rwlock_read_lock(&task_hash_table_lock);
208 ht_link_t *link = hash_table_find(&task_hash_table, &id);
209 fibril_rwlock_read_unlock(&task_hash_table_lock);
210
211 hashed_task_t *ht = (link != NULL) ?
212 hash_table_get_inst(link, hashed_task_t, link) : NULL;
213
214 if (ht == NULL) {
215 /* No such task exists. */
216 async_answer_0(callid, ENOENT);
217 return;
218 }
219
220 if (ht->exit != TASK_EXIT_RUNNING) {
221 //TODO are flags BOTH processed correctly here?
222 async_answer_3(callid, EOK, ht->exit, ht->retval, 0);
223 return;
224 }
225
226 /*
227 * Add request to pending list or reuse existing item for a second
228 * wait.
229 */
230 task_id_t waiter_id = call->in_task_id;
231 fibril_rwlock_write_lock(&pending_wait_lock);
232 pending_wait_t *pr = NULL;
233 list_foreach(pending_wait, link, pending_wait_t, it) {
234 if (it->id == id && it->waiter_id == waiter_id) {
235 pr = it;
236 break;
237 }
238 }
239
240 int rc = EOK;
241 bool reuse = false;
242 if (pr == NULL) {
243 pr = malloc(sizeof(pending_wait_t));
244 if (!pr) {
245 rc = ENOMEM;
246 goto finish;
247 }
248
249 link_initialize(&pr->link);
250 pr->id = id;
251 pr->waiter_id = waiter_id;
252 pr->flags = flags;
253 pr->callid = callid;
254
255 list_append(&pr->link, &pending_wait);
256 rc = EOK;
257 } else if (!(pr->flags & TASK_WAIT_BOTH)) {
258 /*
259 * One task can wait for another task only once (per task, not
260 * fibril).
261 */
262 rc = EEXISTS;
263 } else {
264 /*
265 * Reuse pending wait for the second time.
266 */
267 pr->flags &= ~TASK_WAIT_BOTH; // TODO maybe new flags should be set?
268 pr->callid = callid;
269 reuse = true;
270 }
271 printf("%s: %llu: %x, %x, %i\n", __func__, pr->id, flags, pr->flags, reuse);
272
273finish:
274 fibril_rwlock_write_unlock(&pending_wait_lock);
275 // TODO why IPC_CALLID_NOTIFICATION? explain!
276 if (rc != EOK && !(callid & IPC_CALLID_NOTIFICATION))
277 async_answer_0(callid, rc);
278
279}
280
281int task_intro(ipc_call_t *call, bool check_unique)
282{
283 int rc = EOK;
284
285 fibril_rwlock_write_lock(&task_hash_table_lock);
286
287 ht_link_t *link = hash_table_find(&task_hash_table, &call->in_task_id);
288 if (link != NULL) {
289 rc = EEXISTS;
290 goto finish;
291 }
292
293 hashed_task_t *ht = (hashed_task_t *) malloc(sizeof(hashed_task_t));
294 if (ht == NULL) {
295 rc = ENOMEM;
296 goto finish;
297 }
298
299 /*
300 * Insert into the main table.
301 */
302 ht->id = call->in_task_id;
303 ht->exit = TASK_EXIT_RUNNING;
304 ht->retval_type = RVAL_UNSET;
305 ht->retval = -1;
306
307 hash_table_insert(&task_hash_table, &ht->link);
308 printf("%s: %llu\n", __func__, ht->id);
309
310finish:
311 fibril_rwlock_write_unlock(&task_hash_table_lock);
312 return rc;
313}
314
315int task_set_retval(ipc_call_t *call)
316{
317 int rc = EOK;
318 task_id_t id = call->in_task_id;
319
320 fibril_rwlock_write_lock(&task_hash_table_lock);
321 ht_link_t *link = hash_table_find(&task_hash_table, &id);
322
323 hashed_task_t *ht = (link != NULL) ?
324 hash_table_get_inst(link, hashed_task_t, link) : NULL;
325
326 if ((ht == NULL) || (ht->exit != TASK_EXIT_RUNNING)) {
327 rc = EINVAL;
328 goto finish;
329 }
330
331 ht->retval = IPC_GET_ARG1(*call);
332 ht->retval_type = IPC_GET_ARG2(*call) ? RVAL_SET_EXIT : RVAL_SET;
333
334 process_pending_wait();
335
336finish:
337 fibril_rwlock_write_unlock(&task_hash_table_lock);
338 return rc;
339}
340
341void task_terminated(task_id_t id, task_exit_t texit)
342{
343 /* Mark task as finished. */
344 fibril_rwlock_write_lock(&task_hash_table_lock);
345 ht_link_t *link = hash_table_find(&task_hash_table, &id);
346 if (link == NULL) {
347 goto finish;
348 }
349
350 hashed_task_t *ht = hash_table_get_inst(link, hashed_task_t, link);
351
352 /*
353 * If daemon returns a value and then fails/is killed, it's unexpected
354 * termination.
355 */
356 if (ht->retval_type == RVAL_UNSET || texit == TASK_EXIT_UNEXPECTED) {
357 ht->exit = TASK_EXIT_UNEXPECTED;
358 } else {
359 ht->exit = texit;
360 }
361 process_pending_wait();
362
363 hash_table_remove_item(&task_hash_table, &ht->link);
364finish:
365 fibril_rwlock_write_unlock(&task_hash_table_lock);
366}
367
368/**
369 * @}
370 */
Note: See TracBrowser for help on using the repository browser.