source: mainline/uspace/lib/c/generic/task.c@ 1be7bee

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

sysman: Move task retval and waiting logic to taskman (partially)

  • two important sessions: NS and taskman
  • depending on boot task vs spawned task those sessions are initiated differently

Conflicts:

uspace/lib/c/generic/async.c
uspace/lib/c/generic/libc.c
uspace/lib/c/generic/task.c
uspace/lib/c/include/ipc/ns.h
uspace/lib/c/include/task.h
uspace/lib/posix/source/sys/wait.c
uspace/srv/loader/main.c
uspace/srv/ns/ns.c

  • Property mode set to 100644
File size: 11.6 KB
Line 
1/*
2 * Copyright (c) 2006 Jakub Jermar
3 * Copyright (c) 2008 Jiri Svoboda
4 * Copyright (c) 2014 Martin Sucha
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.
29 */
30
31/** @addtogroup libc
32 * @{
33 */
34/** @file
35 */
36
37#include <assert.h>
38#include <async.h>
39#include <errno.h>
40#include <ns.h>
41#include <stdlib.h>
42#include <ipc/taskman.h>
43#include <libc.h>
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>
50#include <vfs/vfs.h>
51#include "private/ns.h"
52#include "private/task.h"
53
54static async_sess_t *session_taskman = NULL;
55
56task_id_t task_get_id(void)
57{
58#ifdef __32_BITS__
59 task_id_t task_id;
60 (void) __SYSCALL1(SYS_TASK_GET_ID, (sysarg_t) &task_id);
61
62 return task_id;
63#endif /* __32_BITS__ */
64
65#ifdef __64_BITS__
66 return (task_id_t) __SYSCALL0(SYS_TASK_GET_ID);
67#endif /* __64_BITS__ */
68}
69
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
94/** Set the task name.
95 *
96 * @param name The new name, typically the command used to execute the
97 * program.
98 *
99 * @return Zero on success or an error code.
100 */
101errno_t task_set_name(const char *name)
102{
103 assert(name);
104
105 return (errno_t) __SYSCALL2(SYS_TASK_SET_NAME, (sysarg_t) name, str_size(name));
106}
107
108/** Kill a task.
109 *
110 * @param task_id ID of task to kill.
111 *
112 * @return Zero on success or an error code.
113 */
114
115errno_t task_kill(task_id_t task_id)
116{
117 return (errno_t) __SYSCALL1(SYS_TASK_KILL, (sysarg_t) &task_id);
118}
119
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.
130 */
131static errno_t task_setup_wait(task_id_t id, task_wait_t *wait)
132{
133 async_exch_t *exch = taskman_exchange_begin();
134 if (exch == NULL)
135 return EIO;
136
137 wait->aid = async_send_3(exch, TASKMAN_WAIT, LOWER32(id), UPPER32(id),
138 wait->flags, &wait->result);
139 taskman_exchange_end(exch);
140
141 return EOK;
142}
143
144/** Create a new task by running an executable from the filesystem.
145 *
146 * This is really just a convenience wrapper over the more complicated
147 * loader API. Arguments are passed as a null-terminated array of strings.
148 *
149 * @param id If not NULL, the ID of the task is stored here on success.
150 * @param wait If not NULL, setup waiting for task's return value and store
151 * the information necessary for waiting here on success.
152 * @param path Pathname of the binary to execute.
153 * @param argv Command-line arguments.
154 *
155 * @return Zero on success or an error code.
156 *
157 */
158errno_t task_spawnv(task_id_t *id, task_wait_t *wait, const char *path,
159 const char *const args[])
160{
161 /* Send default files */
162
163 int fd_stdin = -1;
164 int fd_stdout = -1;
165 int fd_stderr = -1;
166
167 if (stdin != NULL) {
168 (void) vfs_fhandle(stdin, &fd_stdin);
169 }
170
171 if (stdout != NULL) {
172 (void) vfs_fhandle(stdout, &fd_stdout);
173 }
174
175 if (stderr != NULL) {
176 (void) vfs_fhandle(stderr, &fd_stderr);
177 }
178
179 return task_spawnvf(id, wait, path, args, fd_stdin, fd_stdout,
180 fd_stderr);
181}
182
183/** Create a new task by running an executable from the filesystem.
184 *
185 * This is really just a convenience wrapper over the more complicated
186 * loader API. Arguments are passed as a null-terminated array of strings.
187 * Files are passed as null-terminated array of pointers to fdi_node_t.
188 *
189 * @param id If not NULL, the ID of the task is stored here on success.
190 * @param wait If not NULL, setup waiting for task's return value and store
191 * @param path Pathname of the binary to execute.
192 * @param argv Command-line arguments.
193 * @param std_in File to use as stdin.
194 * @param std_out File to use as stdout.
195 * @param std_err File to use as stderr.
196 *
197 * @return Zero on success or an error code.
198 *
199 */
200errno_t task_spawnvf(task_id_t *id, task_wait_t *wait, const char *path,
201 const char *const args[], int fd_stdin, int fd_stdout, int fd_stderr)
202{
203 /* Connect to a program loader. */
204 loader_t *ldr = loader_connect();
205 if (ldr == NULL)
206 return EREFUSED;
207
208 bool wait_initialized = false;
209
210 /* Get task ID. */
211 task_id_t task_id;
212 errno_t rc = loader_get_task_id(ldr, &task_id);
213 if (rc != EOK)
214 goto error;
215
216 /* Send spawner's current working directory. */
217 rc = loader_set_cwd(ldr);
218 if (rc != EOK)
219 goto error;
220
221 /* Send program binary. */
222 rc = loader_set_program_path(ldr, path);
223 if (rc != EOK)
224 goto error;
225
226 /* Send arguments. */
227 rc = loader_set_args(ldr, args);
228 if (rc != EOK)
229 goto error;
230
231 /* Send files */
232 int root = vfs_root();
233 if (root >= 0) {
234 rc = loader_add_inbox(ldr, "root", root);
235 vfs_put(root);
236 if (rc != EOK)
237 goto error;
238 }
239
240 if (fd_stdin >= 0) {
241 rc = loader_add_inbox(ldr, "stdin", fd_stdin);
242 if (rc != EOK)
243 goto error;
244 }
245
246 if (fd_stdout >= 0) {
247 rc = loader_add_inbox(ldr, "stdout", fd_stdout);
248 if (rc != EOK)
249 goto error;
250 }
251
252 if (fd_stderr >= 0) {
253 rc = loader_add_inbox(ldr, "stderr", fd_stderr);
254 if (rc != EOK)
255 goto error;
256 }
257
258 /* Load the program. */
259 rc = loader_load_program(ldr);
260 if (rc != EOK)
261 goto error;
262
263 /* Setup waiting for return value if needed */
264 if (wait) {
265 rc = task_setup_wait(task_id, wait);
266 if (rc != EOK)
267 goto error;
268 wait_initialized = true;
269 }
270
271 /* Run it. */
272 rc = loader_run(ldr);
273 if (rc != EOK)
274 goto error;
275
276 /* Success */
277 if (id != NULL)
278 *id = task_id;
279
280 return EOK;
281
282error:
283 if (wait_initialized)
284 task_cancel_wait(wait);
285
286 /* Error exit */
287 loader_abort(ldr);
288 return rc;
289}
290
291/** Create a new task by running an executable from the filesystem.
292 *
293 * This is really just a convenience wrapper over the more complicated
294 * loader API. Arguments are passed in a va_list.
295 *
296 * @param id If not NULL, the ID of the task is stored here on success.
297 * @param wait If not NULL, setup waiting for task's return value and store
298 * the information necessary for waiting here on success.
299 * @param path Pathname of the binary to execute.
300 * @param cnt Number of arguments.
301 * @param ap Command-line arguments.
302 *
303 * @return Zero on success or an error code.
304 *
305 */
306errno_t task_spawn(task_id_t *task_id, task_wait_t *wait, const char *path,
307 int cnt, va_list ap)
308{
309 /* Allocate argument list. */
310 const char **arglist = malloc(cnt * sizeof(const char *));
311 if (arglist == NULL)
312 return ENOMEM;
313
314 /* Fill in arguments. */
315 const char *arg;
316 cnt = 0;
317 do {
318 arg = va_arg(ap, const char *);
319 arglist[cnt++] = arg;
320 } while (arg != NULL);
321
322 /* Spawn task. */
323 errno_t rc = task_spawnv(task_id, wait, path, arglist);
324
325 /* Free argument list. */
326 free(arglist);
327 return rc;
328}
329
330/** Create a new task by running an executable from the filesystem.
331 *
332 * This is really just a convenience wrapper over the more complicated
333 * loader API. Arguments are passed as a null-terminated list of arguments.
334 *
335 * @param id If not NULL, the ID of the task is stored here on success.
336 * @param wait If not NULL, setup waiting for task's return value and store
337 * the information necessary for waiting here on success.
338 * @param path Pathname of the binary to execute.
339 * @param ... Command-line arguments.
340 *
341 * @return Zero on success or an error code.
342 *
343 */
344errno_t task_spawnl(task_id_t *task_id, task_wait_t *wait, const char *path, ...)
345{
346 /* Count the number of arguments. */
347
348 va_list ap;
349 const char *arg;
350 int cnt = 0;
351
352 va_start(ap, path);
353 do {
354 arg = va_arg(ap, const char *);
355 cnt++;
356 } while (arg != NULL);
357 va_end(ap);
358
359 va_start(ap, path);
360 errno_t rc = task_spawn(task_id, wait, path, cnt, ap);
361 va_end(ap);
362
363 return rc;
364}
365
366/** Cancel waiting for a task.
367 *
368 * This can be called *instead of* task_wait if the caller is not interested
369 * in waiting for the task anymore.
370 *
371 * This function cannot be called if the task_wait was already called.
372 *
373 * @param wait task_wait_t previously initialized by task_setup_wait.
374 */
375void task_cancel_wait(task_wait_t *wait)
376{
377 async_forget(wait->aid);
378}
379
380/** Wait for a task to finish.
381 *
382 * This function returns correct values even if the task finished in
383 * between task_setup_wait and this task_wait call.
384 *
385 * This function cannot be called more than once with the same task_wait_t
386 * (it can be reused, but must be reinitialized with task_setup_wait first)
387 *
388 * @param wait task_wait_t previously initialized by task_setup_wait.
389 * @param texit Store type of task exit here.
390 * @param retval Store return value of the task here.
391 *
392 * @return EOK on success, else error code.
393 */
394errno_t task_wait(task_wait_t *wait, task_exit_t *texit, int *retval)
395{
396 assert(texit);
397 assert(retval);
398
399 errno_t rc;
400 async_wait_for(wait->aid, &rc);
401
402 if (rc == EOK) {
403 *texit = ipc_get_arg1(&wait->result);
404 *retval = ipc_get_arg2(&wait->result);
405 }
406
407 return rc;
408}
409
410/** Wait for a task to finish by its id.
411 *
412 * Note that this will fail with ENOENT if the task id is not registered in ns
413 * (e.g. if the task finished). If you are spawning a task and need to wait
414 * for its completion, use wait parameter of the task_spawn* functions instead
415 * to prevent a race where the task exits before you may have a chance to wait
416 * wait for it.
417 *
418 * @param id ID of the task to wait for.
419 * @param flags Specify for which task output we wait
420 * @param texit Store type of task exit here.
421 * @param retval Store return value of the task here.
422 *
423 * @return EOK on success, else error code.
424 */
425errno_t task_wait_task_id(task_id_t id, int flags, task_exit_t *texit, int *retval)
426{
427 task_wait_t wait;
428 wait.flags = flags;
429 errno_t rc = task_setup_wait(id, &wait);
430
431 if (rc != EOK)
432 return rc;
433
434 return task_wait(&wait, texit, retval);
435}
436
437errno_t task_retval(int val)
438{
439 async_exch_t *exch = taskman_exchange_begin();
440 if (exch == NULL)
441 return EIO;
442
443 int rc = (int) async_req_1_0(exch, TASKMAN_RETVAL, val);
444 taskman_exchange_end(exch);
445
446 return rc;
447}
448
449
450void __task_init(async_sess_t *sess)
451{
452 assert(session_taskman == NULL);
453 session_taskman = sess;
454}
455
456/** @}
457 */
Note: See TracBrowser for help on using the repository browser.