Changeset 1c635d6 in mainline for uspace/lib/c/generic/task.c


Ignore:
Timestamp:
2014-08-26T15:12:12Z (11 years ago)
Author:
Martin Sucha <sucha14@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
613d644
Parents:
df7f5cea
Message:

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/c/generic/task.c

    rdf7f5cea r1c635d6  
    22 * Copyright (c) 2006 Jakub Jermar
    33 * Copyright (c) 2008 Jiri Svoboda
     4 * Copyright (c) 2014 Martin Sucha
    45 * All rights reserved.
    56 *
     
    9495 *
    9596 * @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.
    9699 * @param path Pathname of the binary to execute.
    97100 * @param argv Command-line arguments.
     
    100103 *
    101104 */
    102 int task_spawnv(task_id_t *id, const char *path, const char *const args[])
     105int task_spawnv(task_id_t *id, task_wait_t *wait, const char *path,
     106    const char *const args[])
    103107{
    104108        /* Send default files */
     
    125129        files[3] = NULL;
    126130       
    127         return task_spawnvf(id, path, args, files);
     131        return task_spawnvf(id, wait, path, args, files);
    128132}
    129133
     
    135139 *
    136140 * @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.
    137143 * @param path  Pathname of the binary to execute.
    138144 * @param argv  Command-line arguments.
     
    142148 *
    143149 */
    144 int task_spawnvf(task_id_t *id, const char *path, const char *const args[],
    145     int *const files[])
     150int task_spawnvf(task_id_t *id, task_wait_t *wait, const char *path,
     151    const char *const args[], int *const files[])
    146152{
    147153        /* Connect to a program loader. */
     
    150156                return EREFUSED;
    151157       
     158        bool wait_initialized = false;
     159       
    152160        /* Get task ID. */
    153161        task_id_t task_id;
     
    181189                goto error;
    182190       
     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       
    183199        /* Run it. */
    184200        rc = loader_run(ldr);
     
    193209       
    194210error:
     211        if (wait_initialized)
     212                task_cancel_wait(wait);
     213       
    195214        /* Error exit */
    196215        loader_abort(ldr);
     
    204223 *
    205224 * @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.
    206227 * @param path Pathname of the binary to execute.
    207228 * @param cnt  Number of arguments.
     
    211232 *
    212233 */
    213 int task_spawn(task_id_t *task_id, const char *path, int cnt, va_list ap)
     234int task_spawn(task_id_t *task_id, task_wait_t *wait, const char *path,
     235    int cnt, va_list ap)
    214236{
    215237        /* Allocate argument list. */
     
    227249       
    228250        /* Spawn task. */
    229         int rc = task_spawnv(task_id, path, arglist);
     251        int rc = task_spawnv(task_id, wait, path, arglist);
    230252       
    231253        /* Free argument list. */
     
    240262 *
    241263 * @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.
    242266 * @param path Pathname of the binary to execute.
    243267 * @param ...  Command-line arguments.
     
    246270 *
    247271 */
    248 int task_spawnl(task_id_t *task_id, const char *path, ...)
     272int task_spawnl(task_id_t *task_id, task_wait_t *wait, const char *path, ...)
    249273{
    250274        /* Count the number of arguments. */
     
    262286       
    263287        va_start(ap, path);
    264         int rc = task_spawn(task_id, path, cnt, ap);
     288        int rc = task_spawn(task_id, wait, path, cnt, ap);
    265289        va_end(ap);
    266290       
     
    268292}
    269293
    270 int task_wait(task_id_t id, task_exit_t *texit, int *retval)
     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)
    271343{
    272344        assert(texit);
    273345        assert(retval);
    274        
    275         async_exch_t *exch = async_exchange_begin(session_ns);
    276         sysarg_t te, rv;
    277         int rc = (int) async_req_2_2(exch, NS_TASK_WAIT, LOWER32(id),
    278             UPPER32(id), &te, &rv);
    279         async_exchange_end(exch);
    280        
    281         *texit = te;
    282         *retval = rv;
    283        
    284         return rc;
     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);
    285380}
    286381
Note: See TracChangeset for help on using the changeset viewer.