source: mainline/uspace/lib/c/generic/task.c@ b8341bc

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

taskman: IPC builerplate for task event API

  • Actual implementation tomorrow…

Conflicts:

uspace/lib/c/include/task.h

  • Property mode set to 100644
File size: 13.9 KB
RevLine 
[f30e6a0b]1/*
[df4ed85]2 * Copyright (c) 2006 Jakub Jermar
[c98e6ee]3 * Copyright (c) 2008 Jiri Svoboda
[1c635d6]4 * Copyright (c) 2014 Martin Sucha
[b22b0a94]5 * Copyright (c) 2015 Michal Koutny
[f30e6a0b]6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * - Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * - The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[b2951e2]30 */
31
[fadd381]32/** @addtogroup libc
[b2951e2]33 * @{
34 */
35/** @file
[ee369f3]36 */
[f30e6a0b]37
[79ae36dd]38#include <assert.h>
[ee369f3]39#include <async.h>
[79ae36dd]40#include <errno.h>
[7b616e2]41#include <ns.h>
[38d150e]42#include <stdlib.h>
[1be7bee]43#include <ipc/taskman.h>
[79ae36dd]44#include <libc.h>
[1be7bee]45#include <loader/loader.h>
46#include <macros.h>
47#include <malloc.h>
48#include <stdarg.h>
49#include <str.h>
50#include <task.h>
[df02460]51#include <vfs/vfs.h>
[1be7bee]52#include "private/ns.h"
53#include "private/task.h"
54
55static async_sess_t *session_taskman = NULL;
[b22b0a94]56static task_event_handler_t task_event_handler = NULL;
[f30e6a0b]57
[d3b8c1f]58task_id_t task_get_id(void)
[f30e6a0b]59{
[dd8d5a7]60#ifdef __32_BITS__
[f30e6a0b]61 task_id_t task_id;
[d3b8c1f]62 (void) __SYSCALL1(SYS_TASK_GET_ID, (sysarg_t) &task_id);
[a35b458]63
[f30e6a0b]64 return task_id;
[dd8d5a7]65#endif /* __32_BITS__ */
[a35b458]66
[dd8d5a7]67#ifdef __64_BITS__
68 return (task_id_t) __SYSCALL0(SYS_TASK_GET_ID);
69#endif /* __64_BITS__ */
[f30e6a0b]70}
[b2951e2]71
[1be7bee]72static async_exch_t *taskman_exchange_begin(void)
73{
74 /* Lazy connection */
75 if (session_taskman == NULL) {
76 // TODO unify exchange mgmt with taskman_handshake/__init
77 session_taskman = service_connect_blocking(EXCHANGE_SERIALIZE,
78 SERVICE_TASKMAN,
79 TASKMAN_CONTROL,
80 0);
81 }
82
83 if (session_taskman == NULL) {
84 return NULL;
85 }
86
87 async_exch_t *exch = async_exchange_begin(session_taskman);
88 return exch;
89}
90
91static void taskman_exchange_end(async_exch_t *exch)
92{
93 async_exchange_end(exch);
94}
95
[bc18d63]96/** Set the task name.
97 *
[ee369f3]98 * @param name The new name, typically the command used to execute the
99 * program.
100 *
[cde999a]101 * @return Zero on success or an error code.
[bc18d63]102 */
[b7fd2a0]103errno_t task_set_name(const char *name)
[bc18d63]104{
[79ae36dd]105 assert(name);
[a35b458]106
[b7fd2a0]107 return (errno_t) __SYSCALL2(SYS_TASK_SET_NAME, (sysarg_t) name, str_size(name));
[bc18d63]108}
109
[1e9f8ab]110/** Kill a task.
111 *
112 * @param task_id ID of task to kill.
113 *
[cde999a]114 * @return Zero on success or an error code.
[1e9f8ab]115 */
116
[b7fd2a0]117errno_t task_kill(task_id_t task_id)
[1e9f8ab]118{
[b7fd2a0]119 return (errno_t) __SYSCALL1(SYS_TASK_KILL, (sysarg_t) &task_id);
[1e9f8ab]120}
121
[1be7bee]122/** Setup waiting for a task.
123 *
124 * If the task finishes after this call succeeds, it is guaranteed that
125 * task_wait(wait, &texit, &retval) will return correct return value for
126 * the task.
127 *
128 * @param id ID of the task to setup waiting for.
129 * @param wait Information necessary for the later task_wait call is stored here.
130 *
131 * @return EOK on success, else error code.
[d4ec49e]132 * @return TODO check this doesn't return EINVAL -- clash with task_wait
[1be7bee]133 */
134static errno_t task_setup_wait(task_id_t id, task_wait_t *wait)
135{
[2f44fafd]136 assert(wait->flags);
[d4ec49e]137 if (wait->flags & TASK_WAIT_BOTH) {
138 wait->flags |= (TASK_WAIT_RETVAL | TASK_WAIT_EXIT);
139 }
140 wait->tid = id;
[1be7bee]141 async_exch_t *exch = taskman_exchange_begin();
142 if (exch == NULL)
143 return EIO;
144
145 wait->aid = async_send_3(exch, TASKMAN_WAIT, LOWER32(id), UPPER32(id),
146 wait->flags, &wait->result);
147 taskman_exchange_end(exch);
148
149 return EOK;
150}
151
[45454e9b]152/** Create a new task by running an executable from the filesystem.
153 *
154 * This is really just a convenience wrapper over the more complicated
[0485135]155 * loader API. Arguments are passed as a null-terminated array of strings.
[c98e6ee]156 *
[79ae36dd]157 * @param id If not NULL, the ID of the task is stored here on success.
[1c635d6]158 * @param wait If not NULL, setup waiting for task's return value and store
159 * the information necessary for waiting here on success.
[79ae36dd]160 * @param path Pathname of the binary to execute.
161 * @param argv Command-line arguments.
162 *
[cde999a]163 * @return Zero on success or an error code.
[ee369f3]164 *
[c98e6ee]165 */
[b7fd2a0]166errno_t task_spawnv(task_id_t *id, task_wait_t *wait, const char *path,
[1c635d6]167 const char *const args[])
[ae45201]168{
169 /* Send default files */
[a35b458]170
[bb9ec2d]171 int fd_stdin = -1;
172 int fd_stdout = -1;
173 int fd_stderr = -1;
[a35b458]174
[bb9ec2d]175 if (stdin != NULL) {
176 (void) vfs_fhandle(stdin, &fd_stdin);
177 }
[a35b458]178
[bb9ec2d]179 if (stdout != NULL) {
180 (void) vfs_fhandle(stdout, &fd_stdout);
181 }
182
183 if (stderr != NULL) {
184 (void) vfs_fhandle(stderr, &fd_stderr);
185 }
[a35b458]186
[bb9ec2d]187 return task_spawnvf(id, wait, path, args, fd_stdin, fd_stdout,
188 fd_stderr);
[ae45201]189}
190
191/** Create a new task by running an executable from the filesystem.
192 *
193 * This is really just a convenience wrapper over the more complicated
194 * loader API. Arguments are passed as a null-terminated array of strings.
195 * Files are passed as null-terminated array of pointers to fdi_node_t.
196 *
[bb9ec2d]197 * @param id If not NULL, the ID of the task is stored here on success.
198 * @param wait If not NULL, setup waiting for task's return value and store
199 * @param path Pathname of the binary to execute.
200 * @param argv Command-line arguments.
201 * @param std_in File to use as stdin.
202 * @param std_out File to use as stdout.
203 * @param std_err File to use as stderr.
[ae45201]204 *
[cde999a]205 * @return Zero on success or an error code.
[ae45201]206 *
207 */
[b7fd2a0]208errno_t task_spawnvf(task_id_t *id, task_wait_t *wait, const char *path,
[bb9ec2d]209 const char *const args[], int fd_stdin, int fd_stdout, int fd_stderr)
[c98e6ee]210{
[bfd1546]211 /* Connect to a program loader. */
[79ae36dd]212 loader_t *ldr = loader_connect();
[0485135]213 if (ldr == NULL)
214 return EREFUSED;
[a35b458]215
[1c635d6]216 bool wait_initialized = false;
[a35b458]217
[47e0a05b]218 /* Get task ID. */
[79ae36dd]219 task_id_t task_id;
[b7fd2a0]220 errno_t rc = loader_get_task_id(ldr, &task_id);
[45454e9b]221 if (rc != EOK)
[47e0a05b]222 goto error;
[a35b458]223
[622cdbe]224 /* Send spawner's current working directory. */
225 rc = loader_set_cwd(ldr);
226 if (rc != EOK)
227 goto error;
[a35b458]228
[bb9ec2d]229 /* Send program binary. */
230 rc = loader_set_program_path(ldr, path);
[45454e9b]231 if (rc != EOK)
[17b2aac]232 goto error;
[a35b458]233
[4470e26]234 /* Send arguments. */
[ee369f3]235 rc = loader_set_args(ldr, args);
[17b2aac]236 if (rc != EOK)
237 goto error;
[a35b458]238
[ae45201]239 /* Send files */
[5126f80]240 int root = vfs_root();
241 if (root >= 0) {
242 rc = loader_add_inbox(ldr, "root", root);
[9c4cf0d]243 vfs_put(root);
[5126f80]244 if (rc != EOK)
245 goto error;
246 }
[a35b458]247
[bb9ec2d]248 if (fd_stdin >= 0) {
249 rc = loader_add_inbox(ldr, "stdin", fd_stdin);
250 if (rc != EOK)
251 goto error;
252 }
[a35b458]253
[bb9ec2d]254 if (fd_stdout >= 0) {
255 rc = loader_add_inbox(ldr, "stdout", fd_stdout);
256 if (rc != EOK)
257 goto error;
258 }
[a35b458]259
[bb9ec2d]260 if (fd_stderr >= 0) {
261 rc = loader_add_inbox(ldr, "stderr", fd_stderr);
262 if (rc != EOK)
263 goto error;
[1b20da0]264 }
[a35b458]265
[4470e26]266 /* Load the program. */
267 rc = loader_load_program(ldr);
268 if (rc != EOK)
269 goto error;
[a35b458]270
[1c635d6]271 /* Setup waiting for return value if needed */
272 if (wait) {
273 rc = task_setup_wait(task_id, wait);
274 if (rc != EOK)
275 goto error;
276 wait_initialized = true;
277 }
[a35b458]278
[4470e26]279 /* Run it. */
280 rc = loader_run(ldr);
[17b2aac]281 if (rc != EOK)
282 goto error;
[a35b458]283
[c98e6ee]284 /* Success */
[0485135]285 if (id != NULL)
286 *id = task_id;
[a35b458]287
[0485135]288 return EOK;
[a35b458]289
[c98e6ee]290error:
[1c635d6]291 if (wait_initialized)
292 task_cancel_wait(wait);
[a35b458]293
[ee369f3]294 /* Error exit */
[45454e9b]295 loader_abort(ldr);
[0485135]296 return rc;
297}
298
[9f5cf68]299/** Create a new task by running an executable from the filesystem.
300 *
301 * This is really just a convenience wrapper over the more complicated
302 * loader API. Arguments are passed in a va_list.
303 *
304 * @param id If not NULL, the ID of the task is stored here on success.
[1c635d6]305 * @param wait If not NULL, setup waiting for task's return value and store
306 * the information necessary for waiting here on success.
[9f5cf68]307 * @param path Pathname of the binary to execute.
308 * @param cnt Number of arguments.
309 * @param ap Command-line arguments.
310 *
[cde999a]311 * @return Zero on success or an error code.
[9f5cf68]312 *
313 */
[b7fd2a0]314errno_t task_spawn(task_id_t *task_id, task_wait_t *wait, const char *path,
[1c635d6]315 int cnt, va_list ap)
[9f5cf68]316{
317 /* Allocate argument list. */
318 const char **arglist = malloc(cnt * sizeof(const char *));
319 if (arglist == NULL)
320 return ENOMEM;
[a35b458]321
[9f5cf68]322 /* Fill in arguments. */
323 const char *arg;
324 cnt = 0;
325 do {
326 arg = va_arg(ap, const char *);
327 arglist[cnt++] = arg;
328 } while (arg != NULL);
[a35b458]329
[9f5cf68]330 /* Spawn task. */
[b7fd2a0]331 errno_t rc = task_spawnv(task_id, wait, path, arglist);
[a35b458]332
[9f5cf68]333 /* Free argument list. */
334 free(arglist);
335 return rc;
336}
337
[0485135]338/** Create a new task by running an executable from the filesystem.
339 *
340 * This is really just a convenience wrapper over the more complicated
341 * loader API. Arguments are passed as a null-terminated list of arguments.
342 *
[79ae36dd]343 * @param id If not NULL, the ID of the task is stored here on success.
[1c635d6]344 * @param wait If not NULL, setup waiting for task's return value and store
345 * the information necessary for waiting here on success.
[79ae36dd]346 * @param path Pathname of the binary to execute.
347 * @param ... Command-line arguments.
348 *
[cde999a]349 * @return Zero on success or an error code.
[0485135]350 *
351 */
[b7fd2a0]352errno_t task_spawnl(task_id_t *task_id, task_wait_t *wait, const char *path, ...)
[0485135]353{
[79ae36dd]354 /* Count the number of arguments. */
[a35b458]355
[0485135]356 va_list ap;
357 const char *arg;
[79ae36dd]358 int cnt = 0;
[a35b458]359
[0485135]360 va_start(ap, path);
361 do {
362 arg = va_arg(ap, const char *);
363 cnt++;
364 } while (arg != NULL);
365 va_end(ap);
[a35b458]366
[0485135]367 va_start(ap, path);
[b7fd2a0]368 errno_t rc = task_spawn(task_id, wait, path, cnt, ap);
[0485135]369 va_end(ap);
[a35b458]370
[0485135]371 return rc;
[adb4d6c2]372}
373
[1c635d6]374/** Cancel waiting for a task.
375 *
376 * This can be called *instead of* task_wait if the caller is not interested
377 * in waiting for the task anymore.
378 *
379 * This function cannot be called if the task_wait was already called.
380 *
381 * @param wait task_wait_t previously initialized by task_setup_wait.
382 */
[1433ecda]383void task_cancel_wait(task_wait_t *wait)
384{
[1c635d6]385 async_forget(wait->aid);
386}
387
388/** Wait for a task to finish.
389 *
390 * This function returns correct values even if the task finished in
391 * between task_setup_wait and this task_wait call.
392 *
393 * This function cannot be called more than once with the same task_wait_t
394 * (it can be reused, but must be reinitialized with task_setup_wait first)
395 *
396 * @param wait task_wait_t previously initialized by task_setup_wait.
397 * @param texit Store type of task exit here.
398 * @param retval Store return value of the task here.
[1b20da0]399 *
[d4ec49e]400 * @return EOK on success
401 * @return EINVAL on lost wait TODO other error codes
[1c635d6]402 */
[b7fd2a0]403errno_t task_wait(task_wait_t *wait, task_exit_t *texit, int *retval)
[1c635d6]404{
[b7fd2a0]405 errno_t rc;
[1c635d6]406 async_wait_for(wait->aid, &rc);
[d4ec49e]407
408 if (rc == EOK || rc == EINVAL) {
[5044114]409 if (wait->flags & TASK_WAIT_EXIT && texit)
410 *texit = ipc_get_arg1(wait->result);
411 if (wait->flags & TASK_WAIT_RETVAL && retval)
412 *retval = ipc_get_arg2(wait->result);
[d4ec49e]413
414 }
415
416 if (rc == EOK) {
417 /* Is there another wait to be done? Wait for it! */
418 int old_flags = wait->flags;
419 wait->flags = ipc_get_arg3(wait->result);
420 if (wait->flags != 0 && (old_flags & TASK_WAIT_BOTH)) {
421 rc = task_setup_wait(wait->tid, wait);
422 }
423 } else {
424 wait->flags = 0;
[1c635d6]425 }
426
[7114d83]427 return rc;
428}
429
[1c635d6]430/** Wait for a task to finish by its id.
431 *
432 * Note that this will fail with ENOENT if the task id is not registered in ns
433 * (e.g. if the task finished). If you are spawning a task and need to wait
434 * for its completion, use wait parameter of the task_spawn* functions instead
435 * to prevent a race where the task exits before you may have a chance to wait
436 * wait for it.
437 *
438 * @param id ID of the task to wait for.
[1be7bee]439 * @param flags Specify for which task output we wait
[1c635d6]440 * @param texit Store type of task exit here.
441 * @param retval Store return value of the task here.
[1b20da0]442 *
[1c635d6]443 * @return EOK on success, else error code.
444 */
[1be7bee]445errno_t task_wait_task_id(task_id_t id, int flags, task_exit_t *texit, int *retval)
[1c635d6]446{
447 task_wait_t wait;
[1be7bee]448 wait.flags = flags;
[b7fd2a0]449 errno_t rc = task_setup_wait(id, &wait);
[1be7bee]450
[1c635d6]451 if (rc != EOK)
452 return rc;
[a35b458]453
[1c635d6]454 return task_wait(&wait, texit, retval);
455}
456
[2f44fafd]457errno_t task_retval_internal(int val, bool wait_for_exit)
[7114d83]458{
[1be7bee]459 async_exch_t *exch = taskman_exchange_begin();
460 if (exch == NULL)
[7b616e2]461 return EIO;
462
[2f44fafd]463 errno_t rc = (int) async_req_2_0(exch, TASKMAN_RETVAL, val, wait_for_exit);
[1be7bee]464 taskman_exchange_end(exch);
465
[79ae36dd]466 return rc;
[ee369f3]467}
468
[2f44fafd]469errno_t task_retval(int val)
470{
471 return task_retval_internal(val, false);
472}
473
[b8341bc]474// TODO extract to separate module
475static void taskman_task_event(ipc_callid_t iid, ipc_call_t *icall)
[b22b0a94]476{
[b8341bc]477 task_id_t tid = (task_id_t)
478 MERGE_LOUP32(IPC_GET_ARG1(*icall), IPC_GET_ARG2(*icall));
479 int flags = IPC_GET_ARG3(*icall);
480 task_exit_t texit = IPC_GET_ARG4(*icall);
481 int retval = IPC_GET_ARG5(*icall);
482
483 task_event_handler(tid, flags, texit, retval);
484
485 async_answer_0(iid, EOK);
486}
487
488static void taskman_event_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
489{
490 /* Accept connection */
491 async_answer_0(iid, EOK);
492
493 while (true) {
494 ipc_call_t call;
495 ipc_callid_t callid = async_get_call(&call);
496
497 if (!IPC_GET_IMETHOD(call)) {
498 /* Hangup, TODO explain or handle differntly */
499 break;
500 }
501
502 switch (IPC_GET_IMETHOD(call)) {
503 case TASKMAN_EV_TASK:
504 taskman_task_event(callid, &call);
505 break;
506 default:
507 async_answer_0(callid, ENOTSUP);
508 break;
509 }
510 }
511}
512
513/**
514 * Blocks, calls handler in another fibril
515 */
516int task_register_event_handler(task_event_handler_t handler)
517{
518 /*
519 * so far support assign once, modification cannot be naïve due to
520 * races
521 */
522 assert(task_event_handler == NULL);
523 assert(handler != NULL); /* no support for "unregistration" */
524
[b22b0a94]525 task_event_handler = handler;
[b8341bc]526
527 async_exch_t *exch = taskman_exchange_begin();
528 aid_t req = async_send_0(exch, TASKMAN_EVENT_CALLBACK, NULL);
529
530 int rc = async_connect_to_me(exch, 0, 0, 0, taskman_event_conn, NULL);
531 taskman_exchange_end(exch);
532
533 if (rc != EOK) {
534 return rc;
535 }
536
537 sysarg_t retval;
538 async_wait_for(req, &retval);
539 return retval;
[b22b0a94]540}
[1be7bee]541
542void __task_init(async_sess_t *sess)
543{
544 assert(session_taskman == NULL);
545 session_taskman = sess;
546}
547
[fadd381]548/** @}
[b2951e2]549 */
Note: See TracBrowser for help on using the repository browser.