123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- /*
- * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
- #include <string.h>
- #include <errno.h>
- #include <assert.h>
- #include <gpxe/process.h>
- #include <gpxe/async.h>
-
- /** @file
- *
- * Asynchronous operations
- *
- */
-
- /**
- * Name signal
- *
- * @v signal Signal number
- * @ret name Name of signal
- */
- static inline __attribute__ (( always_inline )) const char *
- signal_name ( enum signal signal ) {
- switch ( signal ) {
- case SIGCHLD: return "SIGCHLD";
- case SIGKILL: return "SIGKILL";
- case SIGUPDATE: return "SIGUPDATE";
- default: return "SIG<UNKNOWN>";
- }
- }
-
- /**
- * Initialise an asynchronous operation
- *
- * @v async Asynchronous operation
- * @v aop Asynchronous operation operations to use
- * @v parent Parent asynchronous operation, or NULL
- * @ret aid Asynchronous operation ID
- *
- * It is valid to create an asynchronous operation with no parent
- * operation; see async_init_orphan().
- */
- aid_t async_init ( struct async *async, struct async_operations *aop,
- struct async *parent ) {
- static aid_t aid = 1;
-
- /* Assign identifier. Negative IDs are used to indicate
- * errors, so avoid assigning them.
- */
- ++aid;
- aid &= ( ( ~( ( aid_t ) 0 ) ) >> 1 );
-
- DBGC ( async, "ASYNC %p (type %p) initialising as", async, aop );
- if ( parent ) {
- DBGC ( async, " child of ASYNC %p", parent );
- } else {
- DBGC ( async, " orphan" );
- }
- DBGC ( async, " with ID %ld\n", aid );
-
- assert ( async != NULL );
- assert ( aop != NULL );
-
- /* Add to hierarchy */
- if ( parent ) {
- async->parent = parent;
- list_add ( &async->siblings, &parent->children );
- }
- INIT_LIST_HEAD ( &async->children );
-
- /* Initialise fields */
- async->rc = -EINPROGRESS;
- async->completed = 0;
- async->total = 0;
- async->aop = aop;
- async->aid = aid;
-
- return async->aid;
- }
-
- /**
- * Uninitialise an asynchronous operation
- *
- * @v async Asynchronous operation
- *
- * Abandon an asynchronous operation without signalling the parent.
- * You may do this only during the period between calling async_init()
- * and returning to the parent for the first time. It is designed to
- * simplify the error paths of asynchronous operations that themselves
- * spawn further asynchronous operations.
- *
- * An example may help:
- *
- * int start_something ( ..., struct async *parent ) {
- * struct my_data_structure *myself;
- *
- * ... allocate memory for myself ...
- *
- * async_init ( &myself->async, &my_async_operations, parent );
- * if ( ( rc = start_child_operation ( ..., &myself->async ) ) != 0 ) {
- * async_uninit ( &myself->async );
- * return rc;
- * }
- *
- * return 0;
- * }
- *
- * It is valid to call async_uninit() on an asynchronous operation
- * that has not yet been initialised (i.e. a zeroed-out @c struct @c
- * async).
- */
- void async_uninit ( struct async *async ) {
-
- assert ( async != NULL );
-
- if ( async->parent ) {
- assert ( list_empty ( &async->children ) );
-
- DBGC ( async, "ASYNC %p uninitialising\n", async );
- list_del ( &async->siblings );
- }
- }
-
- /**
- * SIGCHLD 'ignore' handler
- *
- * @v async Asynchronous operation
- * @v signal Signal received
- */
- static void async_ignore_sigchld ( struct async *async, enum signal signal ) {
- aid_t waited_aid;
-
- assert ( async != NULL );
- assert ( signal == SIGCHLD );
-
- /* Reap the child */
- waited_aid = async_wait ( async, NULL, 0 );
- assert ( waited_aid >= 0 );
- }
-
- /**
- * 'Ignore' signal handler
- *
- * @v async Asynchronous operation
- * @v signal Signal received
- */
- void async_ignore_signal ( struct async *async, enum signal signal ) {
-
- DBGC ( async, "ASYNC %p using ignore handler for %s\n",
- async, signal_name ( signal ) );
-
- assert ( async != NULL );
-
- switch ( signal ) {
- case SIGCHLD:
- async_ignore_sigchld ( async, signal );
- break;
- case SIGKILL:
- case SIGUPDATE:
- default:
- /* Nothing to do */
- break;
- }
- }
-
- /**
- * Default signal handler
- *
- * @v async Asynchronous operation
- * @v signal Signal received
- */
- static void async_default_signal ( struct async *async, enum signal signal ) {
-
- DBGC ( async, "ASYNC %p using default handler for %s\n",
- async, signal_name ( signal ) );
-
- assert ( async != NULL );
-
- switch ( signal ) {
- case SIGCHLD:
- case SIGKILL:
- case SIGUPDATE:
- default:
- /* Nothing to do */
- break;
- }
- }
-
- /**
- * Send signal to asynchronous operation
- *
- * @v async Asynchronous operation
- * @v signal Signal to send
- */
- void async_signal ( struct async *async, enum signal signal ) {
- signal_handler_t handler;
-
- DBGC ( async, "ASYNC %p receiving %s\n",
- async, signal_name ( signal ) );
-
- assert ( async != NULL );
- assert ( async->aop != NULL );
- assert ( signal < SIGMAX );
-
- handler = async->aop->signal[signal];
- if ( handler ) {
- /* Use the asynchronous operation's signal handler */
- handler ( async, signal );
- } else {
- /* Use the default handler */
- async_default_signal ( async, signal );
- }
- }
-
- /**
- * Send signal to all child asynchronous operations
- *
- * @v async Asynchronous operation
- * @v signal Signal to send
- */
- void async_signal_children ( struct async *async, enum signal signal ) {
- struct async *child;
- struct async *tmp;
-
- assert ( async != NULL );
-
- list_for_each_entry_safe ( child, tmp, &async->children, siblings ) {
- async_signal ( child, signal );
- }
- }
-
- /**
- * Reap default handler
- *
- * @v async Asynchronous operation
- */
- static void async_reap_default ( struct async *async ) {
-
- DBGC ( async, "ASYNC %p ignoring REAP\n", async );
-
- assert ( async != NULL );
-
- /* Nothing to do */
- }
-
- /**
- * Reap asynchronous operation
- *
- * @v async Asynchronous operation
- *
- * Note that the asynchronous operation should have been freed by
- * calling this function; you may not dereference @c async after this
- * call.
- */
- static void async_reap ( struct async *async ) {
-
- DBGC ( async, "ASYNC %p being reaped, exit status %d (%s)\n",
- async, async->rc, strerror ( async->rc ) );
-
- assert ( async != NULL );
- assert ( async->aop != NULL );
- assert ( list_empty ( &async->children ) );
-
- /* Unlink from hierarchy */
- if ( async->parent )
- list_del ( &async->siblings );
- async->parent = NULL;
-
- /* Release all resources */
- if ( async->aop->reap ) {
- async->aop->reap ( async );
- } else {
- async_reap_default ( async );
- }
- }
-
- /**
- * Mark asynchronous operation as complete
- *
- * @v async Asynchronous operation
- * @v rc Return status code
- *
- * An asynchronous operation should call this once it has completed.
- * After calling async_done(), it must be prepared to be reaped by
- * having its reap() method called.
- */
- void async_done ( struct async *async, int rc ) {
- struct async *child;
- struct async *tmp;
-
- DBGC ( async, "ASYNC %p completing with status %d (%s)\n",
- async, rc, strerror ( rc ) );
-
- assert ( async != NULL );
- assert ( async->parent != NULL );
- assert ( rc != -EINPROGRESS );
-
- /* Store return status code */
- async->rc = rc;
-
- /* Disown all of our children */
- list_for_each_entry_safe ( child, tmp, &async->children, siblings ) {
- DBGC ( async, "ASYNC %p disowning child ASYNC %p\n",
- async, child );
- list_del ( &child->siblings );
- child->parent = NULL;
- }
-
- /* Send SIGCHLD to parent. If we don't have a parent then we
- * have to take care of our own funeral arrangements.
- */
- if ( async->parent ) {
- async_signal ( async->parent, SIGCHLD );
- } else {
- async_reap ( async );
- }
- }
-
- /**
- * Wait for any child asynchronous operation to complete
- *
- * @v child Child asynchronous operation
- * @v rc Child exit status to fill in, or NULL
- * @v block Block waiting for child operation to complete
- * @ret aid Asynchronous operation ID, or -1 on error
- */
- aid_t async_wait ( struct async *async, int *rc, int block ) {
- struct async *child;
- aid_t child_aid;
- int dummy_rc;
-
- DBGC ( async, "ASYNC %p performing %sblocking wait%s\n", async,
- ( block ? "" : "non-" ), ( rc ? "" : " (ignoring status)" ) );
-
- assert ( async != NULL );
-
- /* Avoid multiple tests for "if ( rc )" */
- if ( ! rc )
- rc = &dummy_rc;
-
- while ( 1 ) {
-
- /* Return immediately if we have no children */
- if ( list_empty ( &async->children ) ) {
- DBGC ( async, "ASYNC %p has no more children\n",
- async );
- *rc = -ECHILD;
- return -1;
- }
-
- /* Look for a completed child */
- list_for_each_entry ( child, &async->children, siblings ) {
- if ( child->rc == -EINPROGRESS )
- continue;
-
- /* Found a completed child */
- *rc = child->rc;
- child_aid = child->aid;
-
- DBGC ( async, "ASYNC %p reaping child ASYNC %p "
- "(ID %ld)\n", async, child, child_aid );
-
- /* Reap the child and return */
- async_reap ( child );
- return child_aid;
- }
-
- /* Return immediately if non-blocking */
- if ( ! block ) {
- *rc = -EINPROGRESS;
- return -1;
- }
-
- /* Allow processes to run */
- step();
- }
- }
-
- /**
- * Default asynchronous operations
- *
- * The default is to ignore SIGCHLD (i.e. to automatically reap
- * children) and to use the default handler (i.e. do nothing) for all
- * other signals.
- */
- struct async_operations default_async_operations = {
- .signal = {
- [SIGCHLD] = SIG_IGN,
- },
- };
-
- /**
- * Default asynchronous operations for orphan asynchronous operations
- *
- * The default for orphan asynchronous operations is to do nothing for
- * SIGCHLD (i.e. to not automatically reap children), on the
- * assumption that you're probably creating the orphan solely in order
- * to async_wait() on it.
- */
- struct async_operations orphan_async_operations = {
- .signal = {
- [SIGCHLD] = SIG_DFL,
- },
- };
|