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

Last change on this file since f92b315 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: 11.8 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>
[012dd8e]51#include <taskman.h>
[df02460]52#include <vfs/vfs.h>
[1be7bee]53
[012dd8e]54#include "private/task.h"
55#include "private/taskman.h"
[f30e6a0b]56
[d3b8c1f]57task_id_t task_get_id(void)
[f30e6a0b]58{
[dd8d5a7]59#ifdef __32_BITS__
[f30e6a0b]60 task_id_t task_id;
[d3b8c1f]61 (void) __SYSCALL1(SYS_TASK_GET_ID, (sysarg_t) &task_id);
[a35b458]62
[f30e6a0b]63 return task_id;
[dd8d5a7]64#endif /* __32_BITS__ */
[a35b458]65
[dd8d5a7]66#ifdef __64_BITS__
67 return (task_id_t) __SYSCALL0(SYS_TASK_GET_ID);
68#endif /* __64_BITS__ */
[f30e6a0b]69}
[b2951e2]70
[bc18d63]71/** Set the task name.
72 *
[ee369f3]73 * @param name The new name, typically the command used to execute the
74 * program.
75 *
[cde999a]76 * @return Zero on success or an error code.
[bc18d63]77 */
[b7fd2a0]78errno_t task_set_name(const char *name)
[bc18d63]79{
[79ae36dd]80 assert(name);
[a35b458]81
[b7fd2a0]82 return (errno_t) __SYSCALL2(SYS_TASK_SET_NAME, (sysarg_t) name, str_size(name));
[bc18d63]83}
84
[1e9f8ab]85/** Kill a task.
86 *
87 * @param task_id ID of task to kill.
88 *
[cde999a]89 * @return Zero on success or an error code.
[1e9f8ab]90 */
91
[b7fd2a0]92errno_t task_kill(task_id_t task_id)
[1e9f8ab]93{
[b7fd2a0]94 return (errno_t) __SYSCALL1(SYS_TASK_KILL, (sysarg_t) &task_id);
[1e9f8ab]95}
96
[1be7bee]97/** Setup waiting for a task.
98 *
99 * If the task finishes after this call succeeds, it is guaranteed that
100 * task_wait(wait, &texit, &retval) will return correct return value for
101 * the task.
102 *
103 * @param id ID of the task to setup waiting for.
104 * @param wait Information necessary for the later task_wait call is stored here.
105 *
106 * @return EOK on success, else error code.
107 */
108static errno_t task_setup_wait(task_id_t id, task_wait_t *wait)
109{
[2f44fafd]110 assert(wait->flags);
[d4ec49e]111 if (wait->flags & TASK_WAIT_BOTH) {
112 wait->flags |= (TASK_WAIT_RETVAL | TASK_WAIT_EXIT);
113 }
114 wait->tid = id;
[1be7bee]115 async_exch_t *exch = taskman_exchange_begin();
116 if (exch == NULL)
117 return EIO;
118
119 wait->aid = async_send_3(exch, TASKMAN_WAIT, LOWER32(id), UPPER32(id),
120 wait->flags, &wait->result);
121 taskman_exchange_end(exch);
122
123 return EOK;
124}
125
[45454e9b]126/** Create a new task by running an executable from the filesystem.
127 *
128 * This is really just a convenience wrapper over the more complicated
[0485135]129 * loader API. Arguments are passed as a null-terminated array of strings.
[c98e6ee]130 *
[79ae36dd]131 * @param id If not NULL, the ID of the task is stored here on success.
[1c635d6]132 * @param wait If not NULL, setup waiting for task's return value and store
133 * the information necessary for waiting here on success.
[79ae36dd]134 * @param path Pathname of the binary to execute.
135 * @param argv Command-line arguments.
136 *
[cde999a]137 * @return Zero on success or an error code.
[ee369f3]138 *
[c98e6ee]139 */
[b7fd2a0]140errno_t task_spawnv(task_id_t *id, task_wait_t *wait, const char *path,
[1c635d6]141 const char *const args[])
[ae45201]142{
143 /* Send default files */
[a35b458]144
[bb9ec2d]145 int fd_stdin = -1;
146 int fd_stdout = -1;
147 int fd_stderr = -1;
[a35b458]148
[bb9ec2d]149 if (stdin != NULL) {
150 (void) vfs_fhandle(stdin, &fd_stdin);
151 }
[a35b458]152
[bb9ec2d]153 if (stdout != NULL) {
154 (void) vfs_fhandle(stdout, &fd_stdout);
155 }
156
157 if (stderr != NULL) {
158 (void) vfs_fhandle(stderr, &fd_stderr);
159 }
[a35b458]160
[bb9ec2d]161 return task_spawnvf(id, wait, path, args, fd_stdin, fd_stdout,
162 fd_stderr);
[ae45201]163}
164
165/** Create a new task by running an executable from the filesystem.
166 *
167 * This is really just a convenience wrapper over the more complicated
168 * loader API. Arguments are passed as a null-terminated array of strings.
169 * Files are passed as null-terminated array of pointers to fdi_node_t.
170 *
[bb9ec2d]171 * @param id If not NULL, the ID of the task is stored here on success.
172 * @param wait If not NULL, setup waiting for task's return value and store
173 * @param path Pathname of the binary to execute.
174 * @param argv Command-line arguments.
175 * @param std_in File to use as stdin.
176 * @param std_out File to use as stdout.
177 * @param std_err File to use as stderr.
[ae45201]178 *
[cde999a]179 * @return Zero on success or an error code.
[ae45201]180 *
181 */
[b7fd2a0]182errno_t task_spawnvf(task_id_t *id, task_wait_t *wait, const char *path,
[bb9ec2d]183 const char *const args[], int fd_stdin, int fd_stdout, int fd_stderr)
[c98e6ee]184{
[bfd1546]185 /* Connect to a program loader. */
[79ae36dd]186 loader_t *ldr = loader_connect();
[0485135]187 if (ldr == NULL)
188 return EREFUSED;
[a35b458]189
[1c635d6]190 bool wait_initialized = false;
[a35b458]191
[47e0a05b]192 /* Get task ID. */
[79ae36dd]193 task_id_t task_id;
[b7fd2a0]194 errno_t rc = loader_get_task_id(ldr, &task_id);
[45454e9b]195 if (rc != EOK)
[47e0a05b]196 goto error;
[a35b458]197
[622cdbe]198 /* Send spawner's current working directory. */
199 rc = loader_set_cwd(ldr);
200 if (rc != EOK)
201 goto error;
[a35b458]202
[bb9ec2d]203 /* Send program binary. */
204 rc = loader_set_program_path(ldr, path);
[45454e9b]205 if (rc != EOK)
[17b2aac]206 goto error;
[a35b458]207
[4470e26]208 /* Send arguments. */
[ee369f3]209 rc = loader_set_args(ldr, args);
[17b2aac]210 if (rc != EOK)
211 goto error;
[a35b458]212
[ae45201]213 /* Send files */
[5126f80]214 int root = vfs_root();
215 if (root >= 0) {
216 rc = loader_add_inbox(ldr, "root", root);
[9c4cf0d]217 vfs_put(root);
[5126f80]218 if (rc != EOK)
219 goto error;
220 }
[a35b458]221
[bb9ec2d]222 if (fd_stdin >= 0) {
223 rc = loader_add_inbox(ldr, "stdin", fd_stdin);
224 if (rc != EOK)
225 goto error;
226 }
[a35b458]227
[bb9ec2d]228 if (fd_stdout >= 0) {
229 rc = loader_add_inbox(ldr, "stdout", fd_stdout);
230 if (rc != EOK)
231 goto error;
232 }
[a35b458]233
[bb9ec2d]234 if (fd_stderr >= 0) {
235 rc = loader_add_inbox(ldr, "stderr", fd_stderr);
236 if (rc != EOK)
237 goto error;
[1b20da0]238 }
[a35b458]239
[4470e26]240 /* Load the program. */
241 rc = loader_load_program(ldr);
242 if (rc != EOK)
243 goto error;
[a35b458]244
[1c635d6]245 /* Setup waiting for return value if needed */
246 if (wait) {
247 rc = task_setup_wait(task_id, wait);
248 if (rc != EOK)
249 goto error;
250 wait_initialized = true;
251 }
[a35b458]252
[4470e26]253 /* Run it. */
254 rc = loader_run(ldr);
[17b2aac]255 if (rc != EOK)
256 goto error;
[a35b458]257
[c98e6ee]258 /* Success */
[0485135]259 if (id != NULL)
260 *id = task_id;
[a35b458]261
[0485135]262 return EOK;
[a35b458]263
[c98e6ee]264error:
[1c635d6]265 if (wait_initialized)
266 task_cancel_wait(wait);
[a35b458]267
[ee369f3]268 /* Error exit */
[45454e9b]269 loader_abort(ldr);
[0485135]270 return rc;
271}
272
[9f5cf68]273/** Create a new task by running an executable from the filesystem.
274 *
275 * This is really just a convenience wrapper over the more complicated
276 * loader API. Arguments are passed in a va_list.
277 *
278 * @param id If not NULL, the ID of the task is stored here on success.
[1c635d6]279 * @param wait If not NULL, setup waiting for task's return value and store
280 * the information necessary for waiting here on success.
[9f5cf68]281 * @param path Pathname of the binary to execute.
282 * @param cnt Number of arguments.
283 * @param ap Command-line arguments.
284 *
[cde999a]285 * @return Zero on success or an error code.
[9f5cf68]286 *
287 */
[b7fd2a0]288errno_t task_spawn(task_id_t *task_id, task_wait_t *wait, const char *path,
[1c635d6]289 int cnt, va_list ap)
[9f5cf68]290{
291 /* Allocate argument list. */
292 const char **arglist = malloc(cnt * sizeof(const char *));
293 if (arglist == NULL)
294 return ENOMEM;
[a35b458]295
[9f5cf68]296 /* Fill in arguments. */
297 const char *arg;
298 cnt = 0;
299 do {
300 arg = va_arg(ap, const char *);
301 arglist[cnt++] = arg;
302 } while (arg != NULL);
[a35b458]303
[9f5cf68]304 /* Spawn task. */
[b7fd2a0]305 errno_t rc = task_spawnv(task_id, wait, path, arglist);
[a35b458]306
[9f5cf68]307 /* Free argument list. */
308 free(arglist);
309 return rc;
310}
311
[0485135]312/** Create a new task by running an executable from the filesystem.
313 *
314 * This is really just a convenience wrapper over the more complicated
315 * loader API. Arguments are passed as a null-terminated list of arguments.
316 *
[79ae36dd]317 * @param id If not NULL, the ID of the task is stored here on success.
[1c635d6]318 * @param wait If not NULL, setup waiting for task's return value and store
319 * the information necessary for waiting here on success.
[79ae36dd]320 * @param path Pathname of the binary to execute.
321 * @param ... Command-line arguments.
322 *
[cde999a]323 * @return Zero on success or an error code.
[0485135]324 *
325 */
[b7fd2a0]326errno_t task_spawnl(task_id_t *task_id, task_wait_t *wait, const char *path, ...)
[0485135]327{
[79ae36dd]328 /* Count the number of arguments. */
[a35b458]329
[0485135]330 va_list ap;
331 const char *arg;
[79ae36dd]332 int cnt = 0;
[a35b458]333
[0485135]334 va_start(ap, path);
335 do {
336 arg = va_arg(ap, const char *);
337 cnt++;
338 } while (arg != NULL);
339 va_end(ap);
[a35b458]340
[0485135]341 va_start(ap, path);
[b7fd2a0]342 errno_t rc = task_spawn(task_id, wait, path, cnt, ap);
[0485135]343 va_end(ap);
[a35b458]344
[0485135]345 return rc;
[adb4d6c2]346}
347
[1c635d6]348/** Cancel waiting for a task.
349 *
350 * This can be called *instead of* task_wait if the caller is not interested
351 * in waiting for the task anymore.
352 *
353 * This function cannot be called if the task_wait was already called.
354 *
355 * @param wait task_wait_t previously initialized by task_setup_wait.
356 */
[1433ecda]357void task_cancel_wait(task_wait_t *wait)
358{
[1c635d6]359 async_forget(wait->aid);
360}
361
362/** Wait for a task to finish.
363 *
364 * This function returns correct values even if the task finished in
365 * between task_setup_wait and this task_wait call.
366 *
367 * This function cannot be called more than once with the same task_wait_t
368 * (it can be reused, but must be reinitialized with task_setup_wait first)
369 *
[012dd8e]370 * @param[in/out] wait task_wait_t previously initialized by task_setup_wait
371 * or returned by task_wait with non-zero flags. the
372 * flags are updated so that they represent what can be
373 * still waited for.
374 * @param[out] texit Store type of task exit here.
375 * @param[out] retval Store return value of the task here.
[1b20da0]376 *
[d4ec49e]377 * @return EOK on success
[012dd8e]378 * @return EINTR on lost wait
[1c635d6]379 */
[b7fd2a0]380errno_t task_wait(task_wait_t *wait, task_exit_t *texit, int *retval)
[1c635d6]381{
[b7fd2a0]382 errno_t rc;
[1c635d6]383 async_wait_for(wait->aid, &rc);
[d4ec49e]384
[012dd8e]385 if (rc == EOK || rc == EINTR) {
[5044114]386 if (wait->flags & TASK_WAIT_EXIT && texit)
387 *texit = ipc_get_arg1(wait->result);
388 if (wait->flags & TASK_WAIT_RETVAL && retval)
389 *retval = ipc_get_arg2(wait->result);
[d4ec49e]390
391 }
392
393 if (rc == EOK) {
394 /* Is there another wait to be done? Wait for it! */
395 int old_flags = wait->flags;
396 wait->flags = ipc_get_arg3(wait->result);
397 if (wait->flags != 0 && (old_flags & TASK_WAIT_BOTH)) {
398 rc = task_setup_wait(wait->tid, wait);
399 }
400 } else {
401 wait->flags = 0;
[1c635d6]402 }
403
[7114d83]404 return rc;
405}
406
[1c635d6]407/** Wait for a task to finish by its id.
408 *
409 * Note that this will fail with ENOENT if the task id is not registered in ns
410 * (e.g. if the task finished). If you are spawning a task and need to wait
411 * for its completion, use wait parameter of the task_spawn* functions instead
412 * to prevent a race where the task exits before you may have a chance to wait
413 * wait for it.
414 *
415 * @param id ID of the task to wait for.
[1be7bee]416 * @param flags Specify for which task output we wait
[1c635d6]417 * @param texit Store type of task exit here.
418 * @param retval Store return value of the task here.
[1b20da0]419 *
[1c635d6]420 * @return EOK on success, else error code.
421 */
[1be7bee]422errno_t task_wait_task_id(task_id_t id, int flags, task_exit_t *texit, int *retval)
[1c635d6]423{
424 task_wait_t wait;
[1be7bee]425 wait.flags = flags;
[b7fd2a0]426 errno_t rc = task_setup_wait(id, &wait);
[1be7bee]427
[1c635d6]428 if (rc != EOK)
429 return rc;
[a35b458]430
[1c635d6]431 return task_wait(&wait, texit, retval);
432}
433
[2f44fafd]434errno_t task_retval_internal(int val, bool wait_for_exit)
[7114d83]435{
[1be7bee]436 async_exch_t *exch = taskman_exchange_begin();
437 if (exch == NULL)
[7b616e2]438 return EIO;
439
[2f44fafd]440 errno_t rc = (int) async_req_2_0(exch, TASKMAN_RETVAL, val, wait_for_exit);
[1be7bee]441 taskman_exchange_end(exch);
442
[79ae36dd]443 return rc;
[ee369f3]444}
445
[2f44fafd]446errno_t task_retval(int val)
447{
448 return task_retval_internal(val, false);
449}
450
[fadd381]451/** @}
[b2951e2]452 */
Note: See TracBrowser for help on using the repository browser.