source: mainline/uspace/srv/taskman/task.c@ 55fe220

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

taskman: Implement task_wait API to pass all tests

  • different behavior for different wait flags
  • add locking to fibril code in taskman

Conflicts:

uspace/lib/c/generic/libc.c
uspace/lib/c/generic/task.c

  • Property mode set to 100644
File size: 7.8 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. */
113 ipc_callid_t callid; /**< Call ID waiting for the connection */
114 int flags; /**< Wait flags */
115} pending_wait_t;
116
117static list_t pending_wait;
118static FIBRIL_RWLOCK_INITIALIZE(pending_wait_lock);
119
120int task_init(void)
121{
122 if (!hash_table_create(&task_hash_table, 0, 0, &task_hash_table_ops)) {
123 printf(NAME ": No memory available for tasks\n");
124 return ENOMEM;
125 }
126
127 list_initialize(&pending_wait);
128 return EOK;
129}
130
131/** Process pending wait requests
132 *
133 * Assumes task_hash_table_lock is hold (at least read)
134 */
135void process_pending_wait(void)
136{
137 fibril_rwlock_write_lock(&pending_wait_lock);
138loop:
139 list_foreach(pending_wait, link, pending_wait_t, pr) {
140 ht_link_t *link = hash_table_find(&task_hash_table, &pr->id);
141 if (!link)
142 continue;
143
144 hashed_task_t *ht = hash_table_get_inst(link, hashed_task_t, link);
145 int notify_flags = 0;
146 if (ht->exit != TASK_EXIT_RUNNING) {
147 notify_flags |= TASK_WAIT_EXIT;
148 if (ht->retval_type == RVAL_SET_EXIT) {
149 notify_flags |= TASK_WAIT_RETVAL;
150 }
151 }
152 if (ht->retval_type == RVAL_SET) {
153 notify_flags |= TASK_WAIT_RETVAL;
154 }
155
156 int match = notify_flags & pr->flags;
157 bool answer = !(pr->callid & IPC_CALLID_NOTIFICATION);
158
159 if (match == 0) {
160 if (notify_flags & TASK_WAIT_EXIT) {
161 /* Nothing to wait for anymore */
162 if (answer) {
163 async_answer_0(pr->callid, EINVAL);
164 }
165 } else {
166 /* Maybe later */
167 continue;
168 }
169 } else if (answer) {
170 /* Send both exit status and retval, caller should know
171 * what is valid */
172 async_answer_2(pr->callid, EOK, ht->exit, ht->retval);
173 }
174
175
176 list_remove(&pr->link);
177 free(pr);
178 goto loop;
179 }
180 fibril_rwlock_write_unlock(&pending_wait_lock);
181}
182
183void wait_for_task(task_id_t id, int flags, ipc_callid_t callid, ipc_call_t *call)
184{
185 fibril_rwlock_read_lock(&task_hash_table_lock);
186 ht_link_t *link = hash_table_find(&task_hash_table, &id);
187 fibril_rwlock_read_unlock(&task_hash_table_lock);
188
189 hashed_task_t *ht = (link != NULL) ?
190 hash_table_get_inst(link, hashed_task_t, link) : NULL;
191
192 if (ht == NULL) {
193 /* No such task exists. */
194 async_answer_0(callid, ENOENT);
195 return;
196 }
197
198 if (ht->exit != TASK_EXIT_RUNNING) {
199 task_exit_t texit = ht->exit;
200 async_answer_2(callid, EOK, texit, ht->retval);
201 return;
202 }
203
204 /* Add to pending list */
205 pending_wait_t *pr =
206 (pending_wait_t *) malloc(sizeof(pending_wait_t));
207 if (!pr) {
208 // TODO why IPC_CALLID_NOTIFICATION? explain!
209 if (!(callid & IPC_CALLID_NOTIFICATION))
210 async_answer_0(callid, ENOMEM);
211 return;
212 }
213
214 link_initialize(&pr->link);
215 pr->id = id;
216 pr->flags = flags;
217 pr->callid = callid;
218
219 fibril_rwlock_write_lock(&pending_wait_lock);
220 list_append(&pr->link, &pending_wait);
221 fibril_rwlock_write_unlock(&pending_wait_lock);
222}
223
224int task_intro(ipc_call_t *call, bool check_unique)
225{
226 int rc = EOK;
227
228 fibril_rwlock_write_lock(&task_hash_table_lock);
229
230 ht_link_t *link = hash_table_find(&task_hash_table, &call->in_task_id);
231 if (link != NULL) {
232 rc = EEXISTS;
233 goto finish;
234 }
235
236 hashed_task_t *ht = (hashed_task_t *) malloc(sizeof(hashed_task_t));
237 if (ht == NULL) {
238 rc = ENOMEM;
239 goto finish;
240 }
241
242 /*
243 * Insert into the main table.
244 */
245 ht->id = call->in_task_id;
246 ht->exit = TASK_EXIT_RUNNING;
247 ht->retval_type = RVAL_UNSET;
248 ht->retval = -1;
249
250 hash_table_insert(&task_hash_table, &ht->link);
251
252finish:
253 fibril_rwlock_write_unlock(&task_hash_table_lock);
254 return rc;
255}
256
257int task_set_retval(ipc_call_t *call)
258{
259 int rc = EOK;
260 task_id_t id = call->in_task_id;
261
262 fibril_rwlock_read_lock(&task_hash_table_lock);
263 ht_link_t *link = hash_table_find(&task_hash_table, &id);
264
265 hashed_task_t *ht = (link != NULL) ?
266 hash_table_get_inst(link, hashed_task_t, link) : NULL;
267
268 if ((ht == NULL) || (ht->exit != TASK_EXIT_RUNNING)) {
269 rc = EINVAL;
270 goto finish;
271 }
272
273 ht->retval = IPC_GET_ARG1(*call);
274 ht->retval_type = IPC_GET_ARG2(*call) ? RVAL_SET_EXIT : RVAL_SET;
275
276 process_pending_wait();
277
278finish:
279 fibril_rwlock_read_unlock(&task_hash_table_lock);
280 return rc;
281}
282
283void task_terminated(task_id_t id, task_exit_t texit)
284{
285 /* Mark task as finished. */
286 fibril_rwlock_write_lock(&task_hash_table_lock);
287 ht_link_t *link = hash_table_find(&task_hash_table, &id);
288 if (link == NULL) {
289 goto finish;
290 }
291
292 hashed_task_t *ht = hash_table_get_inst(link, hashed_task_t, link);
293
294 if (ht->retval_type == RVAL_UNSET) {
295 ht->exit = TASK_EXIT_UNEXPECTED;
296 } else {
297 ht->exit = texit;
298 }
299 process_pending_wait();
300
301 hash_table_remove_item(&task_hash_table, &ht->link);
302finish:
303 fibril_rwlock_write_unlock(&task_hash_table_lock);
304}
305
306/**
307 * @}
308 */
Note: See TracBrowser for help on using the repository browser.