source: mainline/uspace/srv/taskman/event.c@ 012dd8e

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

taskman: Handle INIT_TASKS as tasks spawned by loader

  • everyone is connected to its spawner, except for INIT_TASKS, they are connected to taskman (first binary)
  • taskman is now aware even of INIT_TASKS and taskman itself
  • refactored taskman handshake — NS session is created lazily
  • refactored async.c with usage of create_session
  • changed EINVAL to EINTR on lost waits
  • removed TODOs from taskman and related libc TODOs

Conflicts:

abi/include/abi/ipc/methods.h
boot/Makefile.common
uspace/lib/c/generic/async.c
uspace/lib/c/generic/libc.c
uspace/lib/c/generic/loader.c
uspace/lib/c/generic/ns.c
uspace/lib/c/generic/private/async.h
uspace/lib/c/generic/private/taskman.h
uspace/lib/c/generic/task.c
uspace/lib/c/include/async.h
uspace/lib/c/include/task.h
uspace/srv/loader/main.c
uspace/srv/ns/ns.c

  • Property mode set to 100644
File size: 8.3 KB
Line 
1/*
2 * Copyright (c) 2015 Michal Koutny
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/**
30 *
31 * @addtogroup taskman
32 * @{
33 */
34
35#include <adt/list.h>
36#include <assert.h>
37#include <errno.h>
38#include <ipc/taskman.h>
39#include <stdlib.h>
40#include <task.h>
41
42#include "event.h"
43#include "task.h"
44
45/** Pending task wait structure. */
46typedef struct {
47 link_t link;
48 task_id_t id; /**< Task ID who we wait for. */
49 task_id_t waiter_id; /**< Task ID who waits. */
50 ipc_callid_t callid; /**< Call ID waiting for the event. */
51 int flags; /**< Wait flags. */
52} pending_wait_t;
53
54static list_t pending_waits;
55static FIBRIL_RWLOCK_INITIALIZE(pending_wait_lock);
56
57static list_t listeners;
58static FIBRIL_RWLOCK_INITIALIZE(listeners_lock);
59
60int event_init(void)
61{
62 list_initialize(&pending_waits);
63 list_initialize(&listeners);
64
65 return EOK;
66}
67
68static int event_flags(task_t *task)
69{
70 int flags = 0;
71 if (task->exit != TASK_EXIT_RUNNING) {
72 flags |= TASK_WAIT_EXIT;
73 if (task->retval_type == RVAL_SET_EXIT) {
74 flags |= TASK_WAIT_RETVAL;
75 }
76 }
77 if (task->retval_type == RVAL_SET) {
78 flags |= TASK_WAIT_RETVAL;
79 }
80
81 return flags;
82}
83
84static void event_notify(task_t *sender)
85{
86 // TODO should rlock task_hash_table?
87 int flags = event_flags(sender);
88 if (flags == 0) {
89 return;
90 }
91
92 fibril_rwlock_read_lock(&listeners_lock);
93 list_foreach(listeners, listeners, task_t, t) {
94 assert(t->sess);
95 async_exch_t *exch = async_exchange_begin(t->sess);
96 /* Just send a notification and don't wait for anything */
97 async_send_5(exch, TASKMAN_EV_TASK,
98 LOWER32(sender->id),
99 UPPER32(sender->id),
100 flags,
101 sender->exit,
102 sender->retval,
103 NULL);
104
105 async_exchange_end(exch);
106 }
107 fibril_rwlock_read_unlock(&listeners_lock);
108}
109
110/** Process pending wait requests
111 *
112 * Assumes task_hash_table_lock is hold (at least read)
113 */
114static void process_pending_wait(void)
115{
116 fibril_rwlock_write_lock(&pending_wait_lock);
117loop:
118 list_foreach(pending_waits, link, pending_wait_t, pr) {
119 task_t *t = task_get_by_id(pr->id);
120 if (t == NULL) {
121 continue; // TODO really when does this happen?
122 }
123 int notify_flags = event_flags(t);
124
125 /*
126 * In current implementation you can wait for single retval,
127 * thus it can be never present in rest flags.
128 */
129 int rest = (~notify_flags & pr->flags) & ~TASK_WAIT_RETVAL;
130 rest &= ~TASK_WAIT_BOTH;
131 int match = notify_flags & pr->flags;
132 // TODO why do I even accept such calls?
133 bool answer = !(pr->callid & IPC_CALLID_NOTIFICATION);
134
135 if (match == 0) {
136 if (notify_flags & TASK_WAIT_EXIT) {
137 /* Nothing to wait for anymore */
138 if (answer) {
139 async_answer_0(pr->callid, EINTR);
140 }
141 } else {
142 /* Maybe later */
143 continue;
144 }
145 } else if (answer) {
146 if ((pr->flags & TASK_WAIT_BOTH) && match == TASK_WAIT_EXIT) {
147 /* No sense to wait for both anymore */
148 async_answer_1(pr->callid, EINTR, t->exit);
149 } else {
150 /* Send both exit status and retval, caller
151 * should know what is valid */
152 async_answer_3(pr->callid, EOK, t->exit,
153 t->retval, rest);
154 }
155
156 /* Pending wait has one more chance */
157 if (rest && (pr->flags & TASK_WAIT_BOTH)) {
158 pr->flags = rest | TASK_WAIT_BOTH;
159 continue;
160 }
161 }
162
163
164 list_remove(&pr->link);
165 free(pr);
166 goto loop;
167 }
168 fibril_rwlock_write_unlock(&pending_wait_lock);
169}
170
171int event_register_listener(task_id_t id, async_sess_t *sess)
172{
173 int rc = EOK;
174 fibril_rwlock_write_lock(&task_hash_table_lock);
175 fibril_rwlock_write_lock(&listeners_lock);
176
177 task_t *t = task_get_by_id(id);
178 if (t == NULL) {
179 rc = ENOENT;
180 goto finish;
181 }
182 assert(t->sess == NULL);
183 list_append(&t->listeners, &listeners);
184 t->sess = sess;
185
186finish:
187 fibril_rwlock_write_unlock(&listeners_lock);
188 fibril_rwlock_write_unlock(&task_hash_table_lock);
189 return rc;
190}
191
192void wait_for_task(task_id_t id, int flags, ipc_callid_t callid,
193 task_id_t waiter_id)
194{
195 assert(!(flags & TASK_WAIT_BOTH) ||
196 ((flags & TASK_WAIT_RETVAL) && (flags & TASK_WAIT_EXIT)));
197
198 fibril_rwlock_read_lock(&task_hash_table_lock);
199 task_t *t = task_get_by_id(id);
200 fibril_rwlock_read_unlock(&task_hash_table_lock);
201
202 if (t == NULL) {
203 /* No such task exists. */
204 async_answer_0(callid, ENOENT);
205 return;
206 }
207
208 if (t->exit != TASK_EXIT_RUNNING) {
209 //TODO are flags BOTH processed correctly here?
210 async_answer_3(callid, EOK, t->exit, t->retval, 0);
211 return;
212 }
213
214 /*
215 * Add request to pending list or reuse existing item for a second
216 * wait.
217 */
218 fibril_rwlock_write_lock(&pending_wait_lock);
219 pending_wait_t *pr = NULL;
220 list_foreach(pending_waits, link, pending_wait_t, it) {
221 if (it->id == id && it->waiter_id == waiter_id) {
222 pr = it;
223 break;
224 }
225 }
226
227 int rc = EOK;
228 if (pr == NULL) {
229 pr = malloc(sizeof(pending_wait_t));
230 if (!pr) {
231 rc = ENOMEM;
232 goto finish;
233 }
234
235 link_initialize(&pr->link);
236 pr->id = id;
237 pr->waiter_id = waiter_id;
238 pr->flags = flags;
239 pr->callid = callid;
240
241 list_append(&pr->link, &pending_waits);
242 rc = EOK;
243 } else if (!(pr->flags & TASK_WAIT_BOTH)) {
244 /*
245 * One task can wait for another task only once (per task, not
246 * fibril).
247 */
248 rc = EEXISTS;
249 } else {
250 /*
251 * Reuse pending wait for the second time.
252 */
253 pr->flags &= ~TASK_WAIT_BOTH; // TODO maybe new flags should be set?
254 pr->callid = callid;
255 }
256
257finish:
258 fibril_rwlock_write_unlock(&pending_wait_lock);
259 // TODO why IPC_CALLID_NOTIFICATION? explain!
260 if (rc != EOK && !(callid & IPC_CALLID_NOTIFICATION)) {
261 async_answer_0(callid, rc);
262 }
263}
264
265
266int task_set_retval(task_id_t sender, int retval, bool wait_for_exit)
267{
268 int rc = EOK;
269
270 fibril_rwlock_write_lock(&task_hash_table_lock);
271 task_t *t = task_get_by_id(sender);
272
273 if ((t == NULL) || (t->exit != TASK_EXIT_RUNNING)) {
274 rc = EINVAL;
275 goto finish;
276 }
277
278 t->retval = retval;
279 t->retval_type = wait_for_exit ? RVAL_SET_EXIT : RVAL_SET;
280
281 event_notify(t);
282 process_pending_wait();
283
284finish:
285 fibril_rwlock_write_unlock(&task_hash_table_lock);
286 return rc;
287}
288
289void task_terminated(task_id_t id, exit_reason_t exit_reason)
290{
291 /* Mark task as finished. */
292 fibril_rwlock_write_lock(&task_hash_table_lock);
293 task_t *t = task_get_by_id(id);
294 if (t == NULL) {
295 goto finish;
296 }
297
298 /*
299 * If daemon returns a value and then fails/is killed, it's an
300 * unexpected termination.
301 */
302 if (t->retval_type == RVAL_UNSET || exit_reason == EXIT_REASON_KILLED) {
303 t->exit = TASK_EXIT_UNEXPECTED;
304 } else if (t->failed) {
305 t->exit = TASK_EXIT_UNEXPECTED;
306 } else {
307 t->exit = TASK_EXIT_NORMAL;
308 }
309
310 event_notify(t);
311 process_pending_wait();
312
313 hash_table_remove_item(&task_hash_table, &t->link);
314 // TODO remove from listeners too!
315
316finish:
317 fibril_rwlock_write_unlock(&task_hash_table_lock);
318}
319
320void task_failed(task_id_t id)
321{
322 /* Mark task as failed. */
323 fibril_rwlock_write_lock(&task_hash_table_lock);
324 task_t *t = task_get_by_id(id);
325 if (t == NULL) {
326 goto finish;
327 }
328
329 t->failed = true;
330 // TODO design substitution for taskmon (monitoring) = invoke dump
331 // utility or pass event to registered tasks
332
333finish:
334 fibril_rwlock_write_unlock(&task_hash_table_lock);
335}
336
337/**
338 * @}
339 */
Note: See TracBrowser for help on using the repository browser.