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
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;
112 task_id_t id; /**< Task ID. */
113 ipc_callid_t callid; /**< Call ID waiting for the connection */
[2f44fafd]114 int flags; /**< Wait flags */
[1be7bee]115} pending_wait_t;
116
117static list_t pending_wait;
[2f44fafd]118static FIBRIL_RWLOCK_INITIALIZE(pending_wait_lock);
[1be7bee]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
[2f44fafd]131/** Process pending wait requests
132 *
133 * Assumes task_hash_table_lock is hold (at least read)
134 */
[1be7bee]135void process_pending_wait(void)
136{
[2f44fafd]137 fibril_rwlock_write_lock(&pending_wait_lock);
[1be7bee]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);
[2f44fafd]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 }
[1be7bee]151 }
[2f44fafd]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
[1be7bee]175
176 list_remove(&pr->link);
177 free(pr);
178 goto loop;
179 }
[2f44fafd]180 fibril_rwlock_write_unlock(&pending_wait_lock);
[1be7bee]181}
182
183void wait_for_task(task_id_t id, int flags, ipc_callid_t callid, ipc_call_t *call)
184{
[2f44fafd]185 fibril_rwlock_read_lock(&task_hash_table_lock);
[1be7bee]186 ht_link_t *link = hash_table_find(&task_hash_table, &id);
[2f44fafd]187 fibril_rwlock_read_unlock(&task_hash_table_lock);
188
[1be7bee]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
[62273d1]198 if (ht->exit != TASK_EXIT_RUNNING) {
199 task_exit_t texit = ht->exit;
[1be7bee]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;
[2f44fafd]218
219 fibril_rwlock_write_lock(&pending_wait_lock);
[1be7bee]220 list_append(&pr->link, &pending_wait);
[2f44fafd]221 fibril_rwlock_write_unlock(&pending_wait_lock);
[1be7bee]222}
223
[2f44fafd]224int task_intro(ipc_call_t *call, bool check_unique)
[1be7bee]225{
[2f44fafd]226 int rc = EOK;
227
228 fibril_rwlock_write_lock(&task_hash_table_lock);
229
[62273d1]230 ht_link_t *link = hash_table_find(&task_hash_table, &call->in_task_id);
[2f44fafd]231 if (link != NULL) {
232 rc = EEXISTS;
233 goto finish;
234 }
[1be7bee]235
236 hashed_task_t *ht = (hashed_task_t *) malloc(sizeof(hashed_task_t));
[2f44fafd]237 if (ht == NULL) {
238 rc = ENOMEM;
239 goto finish;
240 }
[62273d1]241
[1be7bee]242 /*
243 * Insert into the main table.
244 */
[62273d1]245 ht->id = call->in_task_id;
246 ht->exit = TASK_EXIT_RUNNING;
[2f44fafd]247 ht->retval_type = RVAL_UNSET;
[1be7bee]248 ht->retval = -1;
[2f44fafd]249
[1be7bee]250 hash_table_insert(&task_hash_table, &ht->link);
251
[2f44fafd]252finish:
253 fibril_rwlock_write_unlock(&task_hash_table_lock);
254 return rc;
[1be7bee]255}
256
257int task_set_retval(ipc_call_t *call)
258{
[2f44fafd]259 int rc = EOK;
[1be7bee]260 task_id_t id = call->in_task_id;
261
[2f44fafd]262 fibril_rwlock_read_lock(&task_hash_table_lock);
[1be7bee]263 ht_link_t *link = hash_table_find(&task_hash_table, &id);
[2f44fafd]264
[1be7bee]265 hashed_task_t *ht = (link != NULL) ?
266 hash_table_get_inst(link, hashed_task_t, link) : NULL;
267
[2f44fafd]268 if ((ht == NULL) || (ht->exit != TASK_EXIT_RUNNING)) {
269 rc = EINVAL;
270 goto finish;
271 }
[1be7bee]272
273 ht->retval = IPC_GET_ARG1(*call);
[2f44fafd]274 ht->retval_type = IPC_GET_ARG2(*call) ? RVAL_SET_EXIT : RVAL_SET;
[1be7bee]275
276 process_pending_wait();
277
[2f44fafd]278finish:
279 fibril_rwlock_read_unlock(&task_hash_table_lock);
280 return rc;
[1be7bee]281}
282
[62273d1]283void task_terminated(task_id_t id, task_exit_t texit)
[1be7bee]284{
285 /* Mark task as finished. */
[2f44fafd]286 fibril_rwlock_write_lock(&task_hash_table_lock);
[1be7bee]287 ht_link_t *link = hash_table_find(&task_hash_table, &id);
[2f44fafd]288 if (link == NULL) {
289 goto finish;
290 }
[1be7bee]291
292 hashed_task_t *ht = hash_table_get_inst(link, hashed_task_t, link);
293
[2f44fafd]294 if (ht->retval_type == RVAL_UNSET) {
295 ht->exit = TASK_EXIT_UNEXPECTED;
296 } else {
297 ht->exit = texit;
298 }
[1be7bee]299 process_pending_wait();
[62273d1]300
301 hash_table_remove_item(&task_hash_table, &ht->link);
[2f44fafd]302finish:
303 fibril_rwlock_write_unlock(&task_hash_table_lock);
[1be7bee]304}
305
306/**
307 * @}
308 */
Note: See TracBrowser for help on using the repository browser.