source: mainline/uspace/srv/taskman/main.c@ 918ac9b

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

taskman: Notify all waiters for NS

This removes race condition when NS started later than those who required (only
one would be connected then).

  • Property mode set to 100644
File size: 9.7 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 * Locking order:
31 * - task_hash_table_lock (task.c),
32 * - pending_wait_lock (event.c),
33 * - listeners_lock (event.c).
34 *
35 * @addtogroup taskman
36 * @{
37 */
38
39#include <adt/prodcons.h>
40#include <assert.h>
41#include <async.h>
42#include <errno.h>
43#include <fibril_synch.h>
44#include <ipc/services.h>
45#include <ipc/taskman.h>
46#include <loader/loader.h>
47#include <macros.h>
48#include <ns.h>
49#include <stdio.h>
50#include <stdlib.h>
51
52#include "event.h"
53#include "task.h"
54#include "taskman.h"
55
56//#define DPRINTF(...) printf(__VA_ARGS__)
57#define DPRINTF(...) /* empty */
58
59typedef struct {
60 link_t link;
61 async_sess_t *sess;
62} sess_ref_t;
63
64static prodcons_t sess_queue;
65
66/** We keep session to NS on our own in taskman */
67static async_sess_t *session_ns = NULL;
68
69static FIBRIL_MUTEX_INITIALIZE(session_ns_mtx);
70static FIBRIL_CONDVAR_INITIALIZE(session_ns_cv);
71
72/*
73 * Static functions
74 */
75static void connect_to_loader(ipc_callid_t iid, ipc_call_t *icall)
76{
77 DPRINTF("%s:%i from %llu\n", __func__, __LINE__, icall->in_task_id);
78 /* We don't accept the connection request, we forward it instead to
79 * freshly spawned loader. */
80 int rc = loader_spawn("loader");
81
82 if (rc != EOK) {
83 async_answer_0(iid, rc);
84 return;
85 }
86
87 /* Wait until spawned task presents itself to us. */
88 link_t *link = prodcons_consume(&sess_queue);
89 sess_ref_t *sess_ref = list_get_instance(link, sess_ref_t, link);
90
91 /* Forward the connection request (strip interface arg). */
92 async_exch_t *exch = async_exchange_begin(sess_ref->sess);
93 rc = async_forward_fast(iid, exch,
94 IPC_GET_ARG2(*icall),
95 IPC_GET_ARG3(*icall),
96 0, IPC_FF_NONE);
97 async_exchange_end(exch);
98
99 /* After forward we can dispose all session-related resources */
100 async_hangup(sess_ref->sess);
101 free(sess_ref);
102
103 if (rc != EOK) {
104 async_answer_0(iid, rc);
105 return;
106 }
107
108 /* Everything OK. */
109}
110
111static void connect_to_ns(ipc_callid_t iid, ipc_call_t *icall)
112{
113 DPRINTF("%s, %llu\n", __func__, icall->in_task_id);
114
115 /* Wait until we know NS */
116 fibril_mutex_lock(&session_ns_mtx);
117 while (session_ns == NULL) {
118 fibril_condvar_wait(&session_ns_cv, &session_ns_mtx);
119 }
120 fibril_mutex_unlock(&session_ns_mtx);
121
122 /* Do not accept connection, forward it */
123 async_exch_t *exch = async_exchange_begin(session_ns);
124 int rc = async_forward_fast(iid, exch, 0, 0, 0, IPC_FF_NONE);
125 async_exchange_end(exch);
126
127 if (rc != EOK) {
128 async_answer_0(iid, rc);
129 return;
130 }
131}
132
133static void taskman_new_task(ipc_callid_t iid, ipc_call_t *icall)
134{
135 int rc = task_intro(icall->in_task_id);
136 async_answer_0(iid, rc);
137}
138
139static void taskman_i_am_ns(ipc_callid_t iid, ipc_call_t *icall)
140{
141 DPRINTF("%s, %llu\n", __func__, icall->in_task_id);
142 int rc = EOK;
143
144 fibril_mutex_lock(&session_ns_mtx);
145 if (session_ns != NULL) {
146 rc = EEXISTS;
147 goto finish;
148 }
149
150 /* Used only for connection forwarding -- atomic */
151 session_ns = async_callback_receive(EXCHANGE_ATOMIC);
152
153 if (session_ns == NULL) {
154 rc = ENOENT;
155 printf("%s: Cannot connect to NS\n", NAME);
156 }
157
158 fibril_condvar_broadcast(&session_ns_cv);
159finish:
160 fibril_mutex_unlock(&session_ns_mtx);
161 async_answer_0(iid, rc);
162}
163
164static void taskman_ctl_wait(ipc_callid_t iid, ipc_call_t *icall)
165{
166 task_id_t id = (task_id_t)
167 MERGE_LOUP32(IPC_GET_ARG1(*icall), IPC_GET_ARG2(*icall));
168 int flags = IPC_GET_ARG3(*icall);
169 task_id_t waiter_id = icall->in_task_id;
170
171 wait_for_task(id, flags, iid, waiter_id);
172}
173
174static void taskman_ctl_retval(ipc_callid_t iid, ipc_call_t *icall)
175{
176 task_id_t sender = icall->in_task_id;
177 int retval = IPC_GET_ARG1(*icall);
178 bool wait_for_exit = IPC_GET_ARG2(*icall);
179
180 DPRINTF("%s:%i from %llu/%i\n", __func__, __LINE__, sender, retval);
181
182 int rc = task_set_retval(sender, retval, wait_for_exit);
183 async_answer_0(iid, rc);
184}
185
186static void taskman_ctl_ev_callback(ipc_callid_t iid, ipc_call_t *icall)
187{
188 DPRINTF("%s:%i from %llu\n", __func__, __LINE__, icall->in_task_id);
189
190 /* Atomic -- will be used for notifications only */
191 async_sess_t *sess = async_callback_receive(EXCHANGE_ATOMIC);
192 if (sess == NULL) {
193 async_answer_0(iid, ENOMEM);
194 return;
195 }
196
197 int rc = event_register_listener(icall->in_task_id, sess);
198 async_answer_0(iid, rc);
199}
200
201static void task_exit_event(ipc_callid_t iid, ipc_call_t *icall, void *arg)
202{
203 task_id_t id = MERGE_LOUP32(IPC_GET_ARG1(*icall), IPC_GET_ARG2(*icall));
204 exit_reason_t exit_reason = IPC_GET_ARG3(*icall);
205 DPRINTF("%s:%i from %llu/%i\n", __func__, __LINE__, id, exit_reason);
206 task_terminated(id, exit_reason);
207}
208
209static void task_fault_event(ipc_callid_t iid, ipc_call_t *icall, void *arg)
210{
211 task_id_t id = MERGE_LOUP32(IPC_GET_ARG1(*icall), IPC_GET_ARG2(*icall));
212 DPRINTF("%s:%i from %llu\n", __func__, __LINE__, id);
213 task_failed(id);
214}
215
216static void loader_callback(ipc_callid_t iid, ipc_call_t *icall)
217{
218 DPRINTF("%s:%i from %llu\n", __func__, __LINE__, icall->in_task_id);
219 // TODO check that loader is expected, would probably discard prodcons
220 // scheme
221
222 /* Preallocate session container */
223 sess_ref_t *sess_ref = malloc(sizeof(sess_ref_t));
224 if (sess_ref == NULL) {
225 async_answer_0(iid, ENOMEM);
226 }
227
228 /* Create callback connection */
229 sess_ref->sess = async_callback_receive_start(EXCHANGE_ATOMIC, icall);
230 if (sess_ref->sess == NULL) {
231 async_answer_0(iid, EINVAL);
232 return;
233 }
234
235 async_answer_0(iid, EOK);
236
237 /* Notify spawners */
238 link_initialize(&sess_ref->link);
239 prodcons_produce(&sess_queue, &sess_ref->link);
240}
241
242static bool handle_call(ipc_callid_t iid, ipc_call_t *icall)
243{
244 switch (IPC_GET_IMETHOD(*icall)) {
245 case TASKMAN_NEW_TASK:
246 taskman_new_task(iid, icall);
247 break;
248 case TASKMAN_I_AM_NS:
249 taskman_i_am_ns(iid, icall);
250 break;
251 case TASKMAN_WAIT:
252 taskman_ctl_wait(iid, icall);
253 break;
254 case TASKMAN_RETVAL:
255 taskman_ctl_retval(iid, icall);
256 break;
257 case TASKMAN_EVENT_CALLBACK:
258 taskman_ctl_ev_callback(iid, icall);
259 break;
260 default:
261 return false;
262 }
263 return true;
264}
265
266static bool handle_implicit_call(ipc_callid_t iid, ipc_call_t *icall)
267{
268 DPRINTF("%s:%i %i(%i) from %llu\n", __func__, __LINE__,
269 IPC_GET_IMETHOD(*icall),
270 IPC_GET_ARG1(*icall),
271 icall->in_task_id);
272
273 if (IPC_GET_IMETHOD(*icall) < IPC_FIRST_USER_METHOD) {
274 switch (IPC_GET_ARG1(*icall)) {
275 case TASKMAN_CONNECT_TO_NS:
276 connect_to_ns(iid, icall);
277 break;
278 case TASKMAN_CONNECT_TO_LOADER:
279 connect_to_loader(iid, icall);
280 break;
281 case TASKMAN_LOADER_CALLBACK:
282 loader_callback(iid, icall);
283 break;
284 default:
285 return false;
286
287 }
288 } else {
289 return handle_call(iid, icall);
290 }
291
292 return true;
293}
294
295static void implicit_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
296{
297 if (!handle_implicit_call(iid, icall)) {
298 async_answer_0(iid, ENOTSUP);
299 return;
300 }
301
302 while (true) {
303 ipc_call_t call;
304 ipc_callid_t callid = async_get_call(&call);
305
306 if (!IPC_GET_IMETHOD(call)) {
307 /* Client disconnected */
308 break;
309 }
310
311 if (!handle_implicit_call(callid, &call)) {
312 async_answer_0(callid, ENOTSUP);
313 break;
314 }
315 }
316}
317
318static void taskman_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
319{
320 /*
321 * We don't expect (yet) clients to connect, having this function is
322 * just to adapt to async framework that creates new connection for
323 * each IPC_M_CONNECT_ME_TO.
324 * In this case those are to be forwarded, so don't continue
325 * "listening" on such connections.
326 */
327 if (!handle_implicit_call(iid, icall)) {
328 /* If cannot handle connection requst, give up trying */
329 async_answer_0(iid, EHANGUP);
330 return;
331 }
332}
333
334
335
336int main(int argc, char *argv[])
337{
338 printf(NAME ": HelenOS task manager\n");
339
340 /* Initialization */
341 prodcons_initialize(&sess_queue);
342 int rc = task_init();
343 if (rc != EOK) {
344 return rc;
345 }
346 rc = event_init();
347 if (rc != EOK) {
348 return rc;
349 }
350
351 rc = async_event_subscribe(EVENT_EXIT, task_exit_event, NULL);
352 if (rc != EOK) {
353 printf(NAME ": Cannot register for exit events (%i).\n", rc);
354 return rc;
355 }
356
357 rc = async_event_subscribe(EVENT_FAULT, task_fault_event, NULL);
358 if (rc != EOK) {
359 printf(NAME ": Cannot register for fault events (%i).\n", rc);
360 return rc;
361 }
362
363 task_id_t self_id = task_get_id();
364 rc = task_intro(self_id);
365 if (rc != EOK) {
366 printf(NAME ": Cannot register self as task (%i).\n", rc);
367 }
368
369 /* Start sysman server */
370 async_set_implicit_connection(implicit_connection);
371 async_set_client_connection(taskman_connection);
372
373 printf(NAME ": Accepting connections\n");
374 (void)task_set_retval(self_id, EOK, false);
375 async_manager();
376
377 /* not reached */
378 return 0;
379}
380
381/**
382 * @}
383 */
Note: See TracBrowser for help on using the repository browser.