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
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's uspace exit status. */
68 bool failed; /**< Task failed. */
69 retval_t retval_type; /**< Task returned a value. */
70 int retval; /**< The return value. */
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
91/** Perform actions after removal of item from the hash table. */
92static void task_remove(ht_link_t *item)
93{
94 free(hash_table_get_inst(item, hashed_task_t, link));
95}
96
97/** Operations for task hash table. */
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;
108static FIBRIL_RWLOCK_INITIALIZE(task_hash_table_lock);
109
110/** Pending task wait structure. */
111typedef struct {
112 link_t link;
113 task_id_t id; /**< Task ID who we wait for. */
114 task_id_t waiter_id; /**< Task ID who waits. */
115 ipc_callid_t callid; /**< Call ID waiting for the connection */
116 int flags; /**< Wait flags */
117} pending_wait_t;
118
119static list_t pending_wait;
120static FIBRIL_RWLOCK_INITIALIZE(pending_wait_lock);
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
133/** Process pending wait requests
134 *
135 * Assumes task_hash_table_lock is hold (at least read)
136 */
137void process_pending_wait(void)
138{
139 fibril_rwlock_write_lock(&pending_wait_lock);
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);
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 }
153 }
154 if (ht->retval_type == RVAL_SET) {
155 notify_flags |= TASK_WAIT_RETVAL;
156 }
157
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;
164 int match = notify_flags & pr->flags;
165 bool answer = !(pr->callid & IPC_CALLID_NOTIFICATION);
166 printf("%s: %x; %x, %x\n", __func__, pr->flags, rest, match);
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) {
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 }
193 }
194
195
196 list_remove(&pr->link);
197 free(pr);
198 goto loop;
199 }
200 fibril_rwlock_write_unlock(&pending_wait_lock);
201}
202
203void wait_for_task(task_id_t id, int flags, ipc_callid_t callid, ipc_call_t *call)
204{
205 assert(!(flags & TASK_WAIT_BOTH) ||
206 ((flags & TASK_WAIT_RETVAL) && (flags & TASK_WAIT_EXIT)));
207
208 fibril_rwlock_read_lock(&task_hash_table_lock);
209 ht_link_t *link = hash_table_find(&task_hash_table, &id);
210 fibril_rwlock_read_unlock(&task_hash_table_lock);
211
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
221 if (ht->exit != TASK_EXIT_RUNNING) {
222 //TODO are flags BOTH processed correctly here?
223 async_answer_3(callid, EOK, ht->exit, ht->retval, 0);
224 return;
225 }
226
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 }
239 }
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 }
249
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);
273
274finish:
275 fibril_rwlock_write_unlock(&pending_wait_lock);
276 // TODO why IPC_CALLID_NOTIFICATION? explain!
277 if (rc != EOK && !(callid & IPC_CALLID_NOTIFICATION))
278 async_answer_0(callid, rc);
279
280}
281
282int task_intro(ipc_call_t *call, bool check_unique)
283{
284 int rc = EOK;
285
286 fibril_rwlock_write_lock(&task_hash_table_lock);
287
288 ht_link_t *link = hash_table_find(&task_hash_table, &call->in_task_id);
289 if (link != NULL) {
290 rc = EEXISTS;
291 goto finish;
292 }
293
294 hashed_task_t *ht = (hashed_task_t *) malloc(sizeof(hashed_task_t));
295 if (ht == NULL) {
296 rc = ENOMEM;
297 goto finish;
298 }
299
300 /*
301 * Insert into the main table.
302 */
303 ht->id = call->in_task_id;
304 ht->exit = TASK_EXIT_RUNNING;
305 ht->failed = false;
306 ht->retval_type = RVAL_UNSET;
307 ht->retval = -1;
308
309 hash_table_insert(&task_hash_table, &ht->link);
310 printf("%s: %llu\n", __func__, ht->id);
311
312finish:
313 fibril_rwlock_write_unlock(&task_hash_table_lock);
314 return rc;
315}
316
317int task_set_retval(ipc_call_t *call)
318{
319 int rc = EOK;
320 task_id_t id = call->in_task_id;
321
322 fibril_rwlock_write_lock(&task_hash_table_lock);
323 ht_link_t *link = hash_table_find(&task_hash_table, &id);
324
325 hashed_task_t *ht = (link != NULL) ?
326 hash_table_get_inst(link, hashed_task_t, link) : NULL;
327
328 if ((ht == NULL) || (ht->exit != TASK_EXIT_RUNNING)) {
329 rc = EINVAL;
330 goto finish;
331 }
332
333 ht->retval = IPC_GET_ARG1(*call);
334 ht->retval_type = IPC_GET_ARG2(*call) ? RVAL_SET_EXIT : RVAL_SET;
335
336 process_pending_wait();
337
338finish:
339 fibril_rwlock_write_unlock(&task_hash_table_lock);
340 return rc;
341}
342
343void task_terminated(task_id_t id, exit_reason_t exit_reason)
344{
345 /* Mark task as finished. */
346 fibril_rwlock_write_lock(&task_hash_table_lock);
347 ht_link_t *link = hash_table_find(&task_hash_table, &id);
348 if (link == NULL) {
349 goto finish;
350 }
351
352 hashed_task_t *ht = hash_table_get_inst(link, hashed_task_t, link);
353
354 /*
355 * If daemon returns a value and then fails/is killed, it's an
356 * unexpected termination.
357 */
358 if (ht->retval_type == RVAL_UNSET || exit_reason == EXIT_REASON_KILLED) {
359 ht->exit = TASK_EXIT_UNEXPECTED;
360 } else if (ht->failed) {
361 ht->exit = TASK_EXIT_UNEXPECTED;
362 } else {
363 ht->exit = TASK_EXIT_NORMAL;
364 }
365 process_pending_wait();
366
367 hash_table_remove_item(&task_hash_table, &ht->link);
368finish:
369 fibril_rwlock_write_unlock(&task_hash_table_lock);
370}
371
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
390/**
391 * @}
392 */
Note: See TracBrowser for help on using the repository browser.