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
RevLine 
[1be7bee]1/*
[2f44fafd]2 * copyright (c) 2009 martin decky
3 * copyright (c) 2009 jiri svoboda
4 * copyright (c) 2015 michal koutny
5 * all rights reserved.
[1be7bee]6 *
[2f44fafd]7 * redistribution and use in source and binary forms, with or without
[1be7bee]8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
[2f44fafd]11 * - redistributions of source code must retain the above copyright
[1be7bee]12 * notice, this list of conditions and the following disclaimer.
[2f44fafd]13 * - redistributions in binary form must reproduce the above copyright
[1be7bee]14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
[2f44fafd]16 * - the name of the author may not be used to endorse or promote products
[1be7bee]17 * derived from this software without specific prior written permission.
18 *
[2f44fafd]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.
[1be7bee]29 */
30
[2f44fafd]31/**
32 * locking order:
33 * - task_hash_table_lock,
34 * - pending_wait_lock.
35 *
36 * @addtogroup taskman
[1be7bee]37 * @{
38 */
39
40#include <adt/hash_table.h>
41#include <assert.h>
42#include <async.h>
43#include <errno.h>
[2f44fafd]44#include <fibril_synch.h>
[1be7bee]45#include <macros.h>
46#include <malloc.h>
47#include <stdbool.h>
48#include <stdio.h>
[2f44fafd]49#include <task.h>
[1be7bee]50#include <types/task.h>
51
52#include "task.h"
53#include "taskman.h"
54
[2f44fafd]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;
[1be7bee]61
[2f44fafd]62/** task hash table item. */
[1be7bee]63typedef struct {
64 ht_link_t link;
65
[2f44fafd]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. */
[1be7bee]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
[2f44fafd]90/** perform actions after removal of item from the hash table. */
[1be7bee]91static void task_remove(ht_link_t *item)
92{
93 free(hash_table_get_inst(item, hashed_task_t, link));
94}
95
[2f44fafd]96/** operations for task hash table. */
[1be7bee]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;
[2f44fafd]107static FIBRIL_RWLOCK_INITIALIZE(task_hash_table_lock);
[1be7bee]108
109/** Pending task wait structure. */
110typedef struct {
111 link_t link;
[d4ec49e]112 task_id_t id; /**< Task ID who we wait for. */
113 task_id_t waiter_id; /**< Task ID who waits. */
[1be7bee]114 ipc_callid_t callid; /**< Call ID waiting for the connection */
[2f44fafd]115 int flags; /**< Wait flags */
[1be7bee]116} pending_wait_t;
117
118static list_t pending_wait;
[2f44fafd]119static FIBRIL_RWLOCK_INITIALIZE(pending_wait_lock);
[1be7bee]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
[2f44fafd]132/** Process pending wait requests
133 *
134 * Assumes task_hash_table_lock is hold (at least read)
135 */
[1be7bee]136void process_pending_wait(void)
137{
[2f44fafd]138 fibril_rwlock_write_lock(&pending_wait_lock);
[1be7bee]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);
[2f44fafd]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 }
[1be7bee]152 }
[2f44fafd]153 if (ht->retval_type == RVAL_SET) {
154 notify_flags |= TASK_WAIT_RETVAL;
155 }
156
[d4ec49e]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;
[2f44fafd]163 int match = notify_flags & pr->flags;
164 bool answer = !(pr->callid & IPC_CALLID_NOTIFICATION);
[d4ec49e]165 printf("%s: %x; %x, %x\n", __func__, pr->flags, rest, match);
[2f44fafd]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) {
[d4ec49e]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 }
[2f44fafd]192 }
193
[1be7bee]194
195 list_remove(&pr->link);
196 free(pr);
197 goto loop;
198 }
[2f44fafd]199 fibril_rwlock_write_unlock(&pending_wait_lock);
[1be7bee]200}
201
202void wait_for_task(task_id_t id, int flags, ipc_callid_t callid, ipc_call_t *call)
203{
[d4ec49e]204 assert(!(flags & TASK_WAIT_BOTH) ||
205 ((flags & TASK_WAIT_RETVAL) && (flags & TASK_WAIT_EXIT)));
206
[2f44fafd]207 fibril_rwlock_read_lock(&task_hash_table_lock);
[1be7bee]208 ht_link_t *link = hash_table_find(&task_hash_table, &id);
[2f44fafd]209 fibril_rwlock_read_unlock(&task_hash_table_lock);
210
[1be7bee]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
[62273d1]220 if (ht->exit != TASK_EXIT_RUNNING) {
[d4ec49e]221 //TODO are flags BOTH processed correctly here?
222 async_answer_3(callid, EOK, ht->exit, ht->retval, 0);
[1be7bee]223 return;
224 }
225
[d4ec49e]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 }
[1be7bee]238 }
[d4ec49e]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 }
[1be7bee]248
[d4ec49e]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);
[2f44fafd]272
[d4ec49e]273finish:
[2f44fafd]274 fibril_rwlock_write_unlock(&pending_wait_lock);
[d4ec49e]275 // TODO why IPC_CALLID_NOTIFICATION? explain!
276 if (rc != EOK && !(callid & IPC_CALLID_NOTIFICATION))
277 async_answer_0(callid, rc);
278
[1be7bee]279}
280
[2f44fafd]281int task_intro(ipc_call_t *call, bool check_unique)
[1be7bee]282{
[2f44fafd]283 int rc = EOK;
284
285 fibril_rwlock_write_lock(&task_hash_table_lock);
286
[62273d1]287 ht_link_t *link = hash_table_find(&task_hash_table, &call->in_task_id);
[2f44fafd]288 if (link != NULL) {
289 rc = EEXISTS;
290 goto finish;
291 }
[1be7bee]292
293 hashed_task_t *ht = (hashed_task_t *) malloc(sizeof(hashed_task_t));
[2f44fafd]294 if (ht == NULL) {
295 rc = ENOMEM;
296 goto finish;
297 }
[62273d1]298
[1be7bee]299 /*
300 * Insert into the main table.
301 */
[62273d1]302 ht->id = call->in_task_id;
303 ht->exit = TASK_EXIT_RUNNING;
[2f44fafd]304 ht->retval_type = RVAL_UNSET;
[1be7bee]305 ht->retval = -1;
[2f44fafd]306
[1be7bee]307 hash_table_insert(&task_hash_table, &ht->link);
[d4ec49e]308 printf("%s: %llu\n", __func__, ht->id);
[1be7bee]309
[2f44fafd]310finish:
311 fibril_rwlock_write_unlock(&task_hash_table_lock);
312 return rc;
[1be7bee]313}
314
315int task_set_retval(ipc_call_t *call)
316{
[2f44fafd]317 int rc = EOK;
[1be7bee]318 task_id_t id = call->in_task_id;
319
[d4ec49e]320 fibril_rwlock_write_lock(&task_hash_table_lock);
[1be7bee]321 ht_link_t *link = hash_table_find(&task_hash_table, &id);
[2f44fafd]322
[1be7bee]323 hashed_task_t *ht = (link != NULL) ?
324 hash_table_get_inst(link, hashed_task_t, link) : NULL;
325
[2f44fafd]326 if ((ht == NULL) || (ht->exit != TASK_EXIT_RUNNING)) {
327 rc = EINVAL;
328 goto finish;
329 }
[1be7bee]330
331 ht->retval = IPC_GET_ARG1(*call);
[2f44fafd]332 ht->retval_type = IPC_GET_ARG2(*call) ? RVAL_SET_EXIT : RVAL_SET;
[1be7bee]333
334 process_pending_wait();
335
[2f44fafd]336finish:
[d4ec49e]337 fibril_rwlock_write_unlock(&task_hash_table_lock);
[2f44fafd]338 return rc;
[1be7bee]339}
340
[62273d1]341void task_terminated(task_id_t id, task_exit_t texit)
[1be7bee]342{
343 /* Mark task as finished. */
[2f44fafd]344 fibril_rwlock_write_lock(&task_hash_table_lock);
[1be7bee]345 ht_link_t *link = hash_table_find(&task_hash_table, &id);
[2f44fafd]346 if (link == NULL) {
347 goto finish;
348 }
[1be7bee]349
350 hashed_task_t *ht = hash_table_get_inst(link, hashed_task_t, link);
351
[d4ec49e]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) {
[2f44fafd]357 ht->exit = TASK_EXIT_UNEXPECTED;
358 } else {
359 ht->exit = texit;
360 }
[1be7bee]361 process_pending_wait();
[62273d1]362
363 hash_table_remove_item(&task_hash_table, &ht->link);
[2f44fafd]364finish:
365 fibril_rwlock_write_unlock(&task_hash_table_lock);
[1be7bee]366}
367
368/**
369 * @}
370 */
Note: See TracBrowser for help on using the repository browser.