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

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

taskman: Implement waiting both for retval and exit
Conflicts:

uspace/lib/c/generic/task.c
uspace/lib/c/include/task.h

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