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

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

taskman: Restore uppercase (vimstorm?)

  • Property mode set to 100644
File size: 10.1 KB
RevLine 
[1be7bee]1/*
[c675ab1]2 * Copyright (c) 2009 Martin Decky
3 * Copyright (c) 2009 Jiri Svoboda
4 * Copyright (c) 2015 Michal Koutny
5 * All rights reserved.
[1be7bee]6 *
[c675ab1]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 *
[c675ab1]11 * - Redistributions of source code must retain the above copyright
[1be7bee]12 * notice, this list of conditions and the following disclaimer.
[c675ab1]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.
[c675ab1]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 *
[c675ab1]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
[c675ab1]62/** Task hash table item. */
[1be7bee]63typedef struct {
64 ht_link_t link;
65
[c675ab1]66 task_id_t id; /**< Task id. */
67 task_exit_t exit; /**< Task's uspace exit status. */
68 bool failed; /**< Task failed. */
69 retval_t retval_type; /**< Task returned a value. */
70 int retval; /**< The return value. */
[1be7bee]71} hashed_task_t;
72
73
74static size_t task_key_hash(void *key)
75{
76 return *(task_id_t*)key;
77}
78
79static size_t task_hash(const ht_link_t *item)
80{
81 hashed_task_t *ht = hash_table_get_inst(item, hashed_task_t, link);
82 return ht->id;
83}
84
85static bool task_key_equal(void *key, const ht_link_t *item)
86{
87 hashed_task_t *ht = hash_table_get_inst(item, hashed_task_t, link);
88 return ht->id == *(task_id_t*)key;
89}
90
[c675ab1]91/** Perform actions after removal of item from the hash table. */
[1be7bee]92static void task_remove(ht_link_t *item)
93{
94 free(hash_table_get_inst(item, hashed_task_t, link));
95}
96
[c675ab1]97/** Operations for task hash table. */
[1be7bee]98static hash_table_ops_t task_hash_table_ops = {
99 .hash = task_hash,
100 .key_hash = task_key_hash,
101 .key_equal = task_key_equal,
102 .equal = NULL,
103 .remove_callback = task_remove
104};
105
106/** Task hash table structure. */
107static hash_table_t task_hash_table;
[2f44fafd]108static FIBRIL_RWLOCK_INITIALIZE(task_hash_table_lock);
[1be7bee]109
110/** Pending task wait structure. */
111typedef struct {
112 link_t link;
[d4ec49e]113 task_id_t id; /**< Task ID who we wait for. */
114 task_id_t waiter_id; /**< Task ID who waits. */
[1be7bee]115 ipc_callid_t callid; /**< Call ID waiting for the connection */
[2f44fafd]116 int flags; /**< Wait flags */
[1be7bee]117} pending_wait_t;
118
119static list_t pending_wait;
[2f44fafd]120static FIBRIL_RWLOCK_INITIALIZE(pending_wait_lock);
[1be7bee]121
122int task_init(void)
123{
124 if (!hash_table_create(&task_hash_table, 0, 0, &task_hash_table_ops)) {
125 printf(NAME ": No memory available for tasks\n");
126 return ENOMEM;
127 }
128
129 list_initialize(&pending_wait);
130 return EOK;
131}
132
[2f44fafd]133/** Process pending wait requests
134 *
135 * Assumes task_hash_table_lock is hold (at least read)
136 */
[1be7bee]137void process_pending_wait(void)
138{
[2f44fafd]139 fibril_rwlock_write_lock(&pending_wait_lock);
[1be7bee]140loop:
141 list_foreach(pending_wait, link, pending_wait_t, pr) {
142 ht_link_t *link = hash_table_find(&task_hash_table, &pr->id);
143 if (!link)
144 continue;
145
146 hashed_task_t *ht = hash_table_get_inst(link, hashed_task_t, link);
[2f44fafd]147 int notify_flags = 0;
148 if (ht->exit != TASK_EXIT_RUNNING) {
149 notify_flags |= TASK_WAIT_EXIT;
150 if (ht->retval_type == RVAL_SET_EXIT) {
151 notify_flags |= TASK_WAIT_RETVAL;
152 }
[1be7bee]153 }
[2f44fafd]154 if (ht->retval_type == RVAL_SET) {
155 notify_flags |= TASK_WAIT_RETVAL;
156 }
157
[d4ec49e]158 /*
159 * In current implementation you can wait for single retval,
160 * thus it can be never present in rest flags.
161 */
162 int rest = (~notify_flags & pr->flags) & ~TASK_WAIT_RETVAL;
163 rest &= ~TASK_WAIT_BOTH;
[2f44fafd]164 int match = notify_flags & pr->flags;
165 bool answer = !(pr->callid & IPC_CALLID_NOTIFICATION);
[d4ec49e]166 printf("%s: %x; %x, %x\n", __func__, pr->flags, rest, match);
[2f44fafd]167
168 if (match == 0) {
169 if (notify_flags & TASK_WAIT_EXIT) {
170 /* Nothing to wait for anymore */
171 if (answer) {
172 async_answer_0(pr->callid, EINVAL);
173 }
174 } else {
175 /* Maybe later */
176 continue;
177 }
178 } else if (answer) {
[d4ec49e]179 if ((pr->flags & TASK_WAIT_BOTH) && match == TASK_WAIT_EXIT) {
180 async_answer_1(pr->callid, EINVAL, ht->exit);
181 } else {
182 /* Send both exit status and retval, caller
183 * should know what is valid */
184 async_answer_3(pr->callid, EOK, ht->exit,
185 ht->retval, rest);
186 }
187
188 /* Pending wait has one more chance */
189 if (rest && (pr->flags & TASK_WAIT_BOTH)) {
190 pr->flags = rest | TASK_WAIT_BOTH;
191 continue;
192 }
[2f44fafd]193 }
194
[1be7bee]195
196 list_remove(&pr->link);
197 free(pr);
198 goto loop;
199 }
[2f44fafd]200 fibril_rwlock_write_unlock(&pending_wait_lock);
[1be7bee]201}
202
203void wait_for_task(task_id_t id, int flags, ipc_callid_t callid, ipc_call_t *call)
204{
[d4ec49e]205 assert(!(flags & TASK_WAIT_BOTH) ||
206 ((flags & TASK_WAIT_RETVAL) && (flags & TASK_WAIT_EXIT)));
207
[2f44fafd]208 fibril_rwlock_read_lock(&task_hash_table_lock);
[1be7bee]209 ht_link_t *link = hash_table_find(&task_hash_table, &id);
[2f44fafd]210 fibril_rwlock_read_unlock(&task_hash_table_lock);
211
[1be7bee]212 hashed_task_t *ht = (link != NULL) ?
213 hash_table_get_inst(link, hashed_task_t, link) : NULL;
214
215 if (ht == NULL) {
216 /* No such task exists. */
217 async_answer_0(callid, ENOENT);
218 return;
219 }
220
[62273d1]221 if (ht->exit != TASK_EXIT_RUNNING) {
[d4ec49e]222 //TODO are flags BOTH processed correctly here?
223 async_answer_3(callid, EOK, ht->exit, ht->retval, 0);
[1be7bee]224 return;
225 }
226
[d4ec49e]227 /*
228 * Add request to pending list or reuse existing item for a second
229 * wait.
230 */
231 task_id_t waiter_id = call->in_task_id;
232 fibril_rwlock_write_lock(&pending_wait_lock);
233 pending_wait_t *pr = NULL;
234 list_foreach(pending_wait, link, pending_wait_t, it) {
235 if (it->id == id && it->waiter_id == waiter_id) {
236 pr = it;
237 break;
238 }
[1be7bee]239 }
[d4ec49e]240
241 int rc = EOK;
242 bool reuse = false;
243 if (pr == NULL) {
244 pr = malloc(sizeof(pending_wait_t));
245 if (!pr) {
246 rc = ENOMEM;
247 goto finish;
248 }
[1be7bee]249
[d4ec49e]250 link_initialize(&pr->link);
251 pr->id = id;
252 pr->waiter_id = waiter_id;
253 pr->flags = flags;
254 pr->callid = callid;
255
256 list_append(&pr->link, &pending_wait);
257 rc = EOK;
258 } else if (!(pr->flags & TASK_WAIT_BOTH)) {
259 /*
260 * One task can wait for another task only once (per task, not
261 * fibril).
262 */
263 rc = EEXISTS;
264 } else {
265 /*
266 * Reuse pending wait for the second time.
267 */
268 pr->flags &= ~TASK_WAIT_BOTH; // TODO maybe new flags should be set?
269 pr->callid = callid;
270 reuse = true;
271 }
272 printf("%s: %llu: %x, %x, %i\n", __func__, pr->id, flags, pr->flags, reuse);
[2f44fafd]273
[d4ec49e]274finish:
[2f44fafd]275 fibril_rwlock_write_unlock(&pending_wait_lock);
[d4ec49e]276 // TODO why IPC_CALLID_NOTIFICATION? explain!
277 if (rc != EOK && !(callid & IPC_CALLID_NOTIFICATION))
278 async_answer_0(callid, rc);
279
[1be7bee]280}
281
[2f44fafd]282int task_intro(ipc_call_t *call, bool check_unique)
[1be7bee]283{
[2f44fafd]284 int rc = EOK;
285
286 fibril_rwlock_write_lock(&task_hash_table_lock);
287
[62273d1]288 ht_link_t *link = hash_table_find(&task_hash_table, &call->in_task_id);
[2f44fafd]289 if (link != NULL) {
290 rc = EEXISTS;
291 goto finish;
292 }
[1be7bee]293
294 hashed_task_t *ht = (hashed_task_t *) malloc(sizeof(hashed_task_t));
[2f44fafd]295 if (ht == NULL) {
296 rc = ENOMEM;
297 goto finish;
298 }
[62273d1]299
[1be7bee]300 /*
301 * Insert into the main table.
302 */
[62273d1]303 ht->id = call->in_task_id;
304 ht->exit = TASK_EXIT_RUNNING;
[5cd2290]305 ht->failed = false;
[2f44fafd]306 ht->retval_type = RVAL_UNSET;
[1be7bee]307 ht->retval = -1;
[2f44fafd]308
[1be7bee]309 hash_table_insert(&task_hash_table, &ht->link);
[d4ec49e]310 printf("%s: %llu\n", __func__, ht->id);
[1be7bee]311
[2f44fafd]312finish:
313 fibril_rwlock_write_unlock(&task_hash_table_lock);
314 return rc;
[1be7bee]315}
316
317int task_set_retval(ipc_call_t *call)
318{
[2f44fafd]319 int rc = EOK;
[1be7bee]320 task_id_t id = call->in_task_id;
321
[d4ec49e]322 fibril_rwlock_write_lock(&task_hash_table_lock);
[1be7bee]323 ht_link_t *link = hash_table_find(&task_hash_table, &id);
[2f44fafd]324
[1be7bee]325 hashed_task_t *ht = (link != NULL) ?
326 hash_table_get_inst(link, hashed_task_t, link) : NULL;
327
[2f44fafd]328 if ((ht == NULL) || (ht->exit != TASK_EXIT_RUNNING)) {
329 rc = EINVAL;
330 goto finish;
331 }
[1be7bee]332
333 ht->retval = IPC_GET_ARG1(*call);
[2f44fafd]334 ht->retval_type = IPC_GET_ARG2(*call) ? RVAL_SET_EXIT : RVAL_SET;
[1be7bee]335
336 process_pending_wait();
337
[2f44fafd]338finish:
[d4ec49e]339 fibril_rwlock_write_unlock(&task_hash_table_lock);
[2f44fafd]340 return rc;
[1be7bee]341}
342
[5cd2290]343void task_terminated(task_id_t id, exit_reason_t exit_reason)
[1be7bee]344{
345 /* Mark task as finished. */
[2f44fafd]346 fibril_rwlock_write_lock(&task_hash_table_lock);
[1be7bee]347 ht_link_t *link = hash_table_find(&task_hash_table, &id);
[2f44fafd]348 if (link == NULL) {
349 goto finish;
350 }
[1be7bee]351
352 hashed_task_t *ht = hash_table_get_inst(link, hashed_task_t, link);
353
[d4ec49e]354 /*
[5cd2290]355 * If daemon returns a value and then fails/is killed, it's an
356 * unexpected termination.
[d4ec49e]357 */
[5cd2290]358 if (ht->retval_type == RVAL_UNSET || exit_reason == EXIT_REASON_KILLED) {
[2f44fafd]359 ht->exit = TASK_EXIT_UNEXPECTED;
[5cd2290]360 } else if (ht->failed) {
361 ht->exit = TASK_EXIT_UNEXPECTED;
362 } else {
363 ht->exit = TASK_EXIT_NORMAL;
[2f44fafd]364 }
[1be7bee]365 process_pending_wait();
[62273d1]366
367 hash_table_remove_item(&task_hash_table, &ht->link);
[2f44fafd]368finish:
369 fibril_rwlock_write_unlock(&task_hash_table_lock);
[1be7bee]370}
371
[5cd2290]372void task_failed(task_id_t id)
373{
374 /* Mark task as failed. */
375 fibril_rwlock_write_lock(&task_hash_table_lock);
376 ht_link_t *link = hash_table_find(&task_hash_table, &id);
377 if (link == NULL) {
378 goto finish;
379 }
380
381 hashed_task_t *ht = hash_table_get_inst(link, hashed_task_t, link);
382
383 ht->failed = true;
384 // TODO design substitution for taskmon (monitoring) = invoke dump utility
385
386finish:
387 fibril_rwlock_write_unlock(&task_hash_table_lock);
388}
389
[1be7bee]390/**
391 * @}
392 */
Note: See TracBrowser for help on using the repository browser.