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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1c635d6 was 1c635d6, checked in by Martin Sucha <sucha14@…>, 11 years ago

Do not hold a task's return value after it has disconnected.

Holding the task's return value meant that if nobody waited
for task's result, it polluted NS's memory. This was apparently
done because of a race between spawning a task and waiting for it.

We solve this problem in another way: ns discards the return value
as soon as the task disconnects from it. This typically happens
when the task finishes its execution. In order to avoid the race,
we send the wait request to ns while spawning the task (i.e. when
we talk to the loader), but before we allow the loaded program
to run.

Fixes #132

  • Property mode set to 100644
File size: 10.4 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 <task.h>
38#include <loader/loader.h>
39#include <stdarg.h>
40#include <str.h>
41#include <ipc/ns.h>
42#include <macros.h>
43#include <assert.h>
44#include <async.h>
45#include <errno.h>
46#include <malloc.h>
47#include <libc.h>
48#include "private/ns.h"
49#include <vfs/vfs.h>
50
51task_id_t task_get_id(void)
52{
53#ifdef __32_BITS__
54 task_id_t task_id;
55 (void) __SYSCALL1(SYS_TASK_GET_ID, (sysarg_t) &task_id);
56
57 return task_id;
58#endif /* __32_BITS__ */
59
60#ifdef __64_BITS__
61 return (task_id_t) __SYSCALL0(SYS_TASK_GET_ID);
62#endif /* __64_BITS__ */
63}
64
65/** Set the task name.
66 *
67 * @param name The new name, typically the command used to execute the
68 * program.
69 *
70 * @return Zero on success or negative error code.
71 */
72int task_set_name(const char *name)
73{
74 assert(name);
75
76 return __SYSCALL2(SYS_TASK_SET_NAME, (sysarg_t) name, str_size(name));
77}
78
79/** Kill a task.
80 *
81 * @param task_id ID of task to kill.
82 *
83 * @return Zero on success or negative error code.
84 */
85
86int task_kill(task_id_t task_id)
87{
88 return (int) __SYSCALL1(SYS_TASK_KILL, (sysarg_t) &task_id);
89}
90
91/** Create a new task by running an executable from the filesystem.
92 *
93 * This is really just a convenience wrapper over the more complicated
94 * loader API. Arguments are passed as a null-terminated array of strings.
95 *
96 * @param id If not NULL, the ID of the task is stored here on success.
97 * @param wait If not NULL, setup waiting for task's return value and store
98 * the information necessary for waiting here on success.
99 * @param path Pathname of the binary to execute.
100 * @param argv Command-line arguments.
101 *
102 * @return Zero on success or negative error code.
103 *
104 */
105int task_spawnv(task_id_t *id, task_wait_t *wait, const char *path,
106 const char *const args[])
107{
108 /* Send default files */
109 int *files[4];
110 int fd_stdin;
111 int fd_stdout;
112 int fd_stderr;
113
114 if ((stdin != NULL) && (fhandle(stdin, &fd_stdin) == EOK))
115 files[0] = &fd_stdin;
116 else
117 files[0] = NULL;
118
119 if ((stdout != NULL) && (fhandle(stdout, &fd_stdout) == EOK))
120 files[1] = &fd_stdout;
121 else
122 files[1] = NULL;
123
124 if ((stderr != NULL) && (fhandle(stderr, &fd_stderr) == EOK))
125 files[2] = &fd_stderr;
126 else
127 files[2] = NULL;
128
129 files[3] = NULL;
130
131 return task_spawnvf(id, wait, path, args, files);
132}
133
134/** Create a new task by running an executable from the filesystem.
135 *
136 * This is really just a convenience wrapper over the more complicated
137 * loader API. Arguments are passed as a null-terminated array of strings.
138 * Files are passed as null-terminated array of pointers to fdi_node_t.
139 *
140 * @param id If not NULL, the ID of the task is stored here on success.
141 * @param wait If not NULL, setup waiting for task's return value and store
142 * the information necessary for waiting here on success.
143 * @param path Pathname of the binary to execute.
144 * @param argv Command-line arguments.
145 * @param files Standard files to use.
146 *
147 * @return Zero on success or negative error code.
148 *
149 */
150int task_spawnvf(task_id_t *id, task_wait_t *wait, const char *path,
151 const char *const args[], int *const files[])
152{
153 /* Connect to a program loader. */
154 loader_t *ldr = loader_connect();
155 if (ldr == NULL)
156 return EREFUSED;
157
158 bool wait_initialized = false;
159
160 /* Get task ID. */
161 task_id_t task_id;
162 int rc = loader_get_task_id(ldr, &task_id);
163 if (rc != EOK)
164 goto error;
165
166 /* Send spawner's current working directory. */
167 rc = loader_set_cwd(ldr);
168 if (rc != EOK)
169 goto error;
170
171 /* Send program pathname. */
172 rc = loader_set_pathname(ldr, path);
173 if (rc != EOK)
174 goto error;
175
176 /* Send arguments. */
177 rc = loader_set_args(ldr, args);
178 if (rc != EOK)
179 goto error;
180
181 /* Send files */
182 rc = loader_set_files(ldr, files);
183 if (rc != EOK)
184 goto error;
185
186 /* Load the program. */
187 rc = loader_load_program(ldr);
188 if (rc != EOK)
189 goto error;
190
191 /* Setup waiting for return value if needed */
192 if (wait) {
193 rc = task_setup_wait(task_id, wait);
194 if (rc != EOK)
195 goto error;
196 wait_initialized = true;
197 }
198
199 /* Run it. */
200 rc = loader_run(ldr);
201 if (rc != EOK)
202 goto error;
203
204 /* Success */
205 if (id != NULL)
206 *id = task_id;
207
208 return EOK;
209
210error:
211 if (wait_initialized)
212 task_cancel_wait(wait);
213
214 /* Error exit */
215 loader_abort(ldr);
216 return rc;
217}
218
219/** Create a new task by running an executable from the filesystem.
220 *
221 * This is really just a convenience wrapper over the more complicated
222 * loader API. Arguments are passed in a va_list.
223 *
224 * @param id If not NULL, the ID of the task is stored here on success.
225 * @param wait If not NULL, setup waiting for task's return value and store
226 * the information necessary for waiting here on success.
227 * @param path Pathname of the binary to execute.
228 * @param cnt Number of arguments.
229 * @param ap Command-line arguments.
230 *
231 * @return Zero on success or negative error code.
232 *
233 */
234int task_spawn(task_id_t *task_id, task_wait_t *wait, const char *path,
235 int cnt, va_list ap)
236{
237 /* Allocate argument list. */
238 const char **arglist = malloc(cnt * sizeof(const char *));
239 if (arglist == NULL)
240 return ENOMEM;
241
242 /* Fill in arguments. */
243 const char *arg;
244 cnt = 0;
245 do {
246 arg = va_arg(ap, const char *);
247 arglist[cnt++] = arg;
248 } while (arg != NULL);
249
250 /* Spawn task. */
251 int rc = task_spawnv(task_id, wait, path, arglist);
252
253 /* Free argument list. */
254 free(arglist);
255 return rc;
256}
257
258/** Create a new task by running an executable from the filesystem.
259 *
260 * This is really just a convenience wrapper over the more complicated
261 * loader API. Arguments are passed as a null-terminated list of arguments.
262 *
263 * @param id If not NULL, the ID of the task is stored here on success.
264 * @param wait If not NULL, setup waiting for task's return value and store
265 * the information necessary for waiting here on success.
266 * @param path Pathname of the binary to execute.
267 * @param ... Command-line arguments.
268 *
269 * @return Zero on success or negative error code.
270 *
271 */
272int task_spawnl(task_id_t *task_id, task_wait_t *wait, const char *path, ...)
273{
274 /* Count the number of arguments. */
275
276 va_list ap;
277 const char *arg;
278 int cnt = 0;
279
280 va_start(ap, path);
281 do {
282 arg = va_arg(ap, const char *);
283 cnt++;
284 } while (arg != NULL);
285 va_end(ap);
286
287 va_start(ap, path);
288 int rc = task_spawn(task_id, wait, path, cnt, ap);
289 va_end(ap);
290
291 return rc;
292}
293
294/** Setup waiting for a task.
295 *
296 * If the task finishes after this call succeeds, it is guaranteed that
297 * task_wait(wait, &texit, &retval) will return correct return value for
298 * the task.
299 *
300 * @param id ID of the task to setup waiting for.
301 * @param wait Information necessary for the later task_wait call is stored here.
302 *
303 * @return EOK on success, else error code.
304 */
305int task_setup_wait(task_id_t id, task_wait_t *wait)
306{
307 async_exch_t *exch = async_exchange_begin(session_ns);
308 wait->aid = async_send_2(exch, NS_TASK_WAIT, LOWER32(id), UPPER32(id),
309 &wait->result);
310 async_exchange_end(exch);
311
312 return EOK;
313}
314
315/** Cancel waiting for a task.
316 *
317 * This can be called *instead of* task_wait if the caller is not interested
318 * in waiting for the task anymore.
319 *
320 * This function cannot be called if the task_wait was already called.
321 *
322 * @param wait task_wait_t previously initialized by task_setup_wait.
323 */
324void task_cancel_wait(task_wait_t *wait) {
325 async_forget(wait->aid);
326}
327
328/** Wait for a task to finish.
329 *
330 * This function returns correct values even if the task finished in
331 * between task_setup_wait and this task_wait call.
332 *
333 * This function cannot be called more than once with the same task_wait_t
334 * (it can be reused, but must be reinitialized with task_setup_wait first)
335 *
336 * @param wait task_wait_t previously initialized by task_setup_wait.
337 * @param texit Store type of task exit here.
338 * @param retval Store return value of the task here.
339 *
340 * @return EOK on success, else error code.
341 */
342int task_wait(task_wait_t *wait, task_exit_t *texit, int *retval)
343{
344 assert(texit);
345 assert(retval);
346
347 sysarg_t rc;
348 async_wait_for(wait->aid, &rc);
349
350 if (rc == EOK) {
351 *texit = IPC_GET_ARG1(wait->result);
352 *retval = IPC_GET_ARG2(wait->result);
353 }
354
355 return rc;
356}
357
358/** Wait for a task to finish by its id.
359 *
360 * Note that this will fail with ENOENT if the task id is not registered in ns
361 * (e.g. if the task finished). If you are spawning a task and need to wait
362 * for its completion, use wait parameter of the task_spawn* functions instead
363 * to prevent a race where the task exits before you may have a chance to wait
364 * wait for it.
365 *
366 * @param id ID of the task to wait for.
367 * @param texit Store type of task exit here.
368 * @param retval Store return value of the task here.
369 *
370 * @return EOK on success, else error code.
371 */
372int task_wait_task_id(task_id_t id, task_exit_t *texit, int *retval)
373{
374 task_wait_t wait;
375 int rc = task_setup_wait(id, &wait);
376 if (rc != EOK)
377 return rc;
378
379 return task_wait(&wait, texit, retval);
380}
381
382int task_retval(int val)
383{
384 async_exch_t *exch = async_exchange_begin(session_ns);
385 int rc = (int) async_req_1_0(exch, NS_RETVAL, val);
386 async_exchange_end(exch);
387
388 return rc;
389}
390
391/** @}
392 */
Note: See TracBrowser for help on using the repository browser.