diff options
Diffstat (limited to 'src/core/async.c')
| -rw-r--r-- | src/core/async.c | 326 |
1 files changed, 309 insertions, 17 deletions
diff --git a/src/core/async.c b/src/core/async.c index 8bd5d7c29..95bbc057e 100644 --- a/src/core/async.c +++ b/src/core/async.c @@ -16,7 +16,9 @@ * 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> @@ -27,26 +29,316 @@ */ /** - * Wait for asynchronous operation to complete + * Name signal * - * @v aop Asynchronous operation - * @ret rc Return status code + * @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 * - * Blocks until the specified asynchronous operation has completed. - * The return status is the return status of the asynchronous - * operation itself. + * It is valid to create an asynchronous operation with no parent + * operation; see async_init_orphan(). */ -int async_wait ( struct async_operation *aop ) { - int rc; +aid_t async_init ( struct async *async, struct async_operations *aop, + struct async *parent ) { + static aid_t aid = 1; - /* Wait for operation to complete */ - do { - step(); - rc = async_status ( aop ); - } while ( rc == -EINPROGRESS ); - - /* Prepare for next call to async_wait() */ - async_set_status ( aop, -EINPROGRESS ); + /* 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; +} - return rc; +/** + * 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 ); + } +} + +/** + * 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 ) { + + 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; + + /* Send SIGCHLD to parent. Guard against NULL pointer dereferences */ + if ( async->parent ) + async_signal ( async->parent, SIGCHLD ); +} + +/** + * 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 */ +} + +/** + * 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), exit status %d (%s)\n", async, child, + child_aid, child->rc, strerror ( child->rc ) ); + + /* Reap the child */ + assert ( child->aop != NULL ); + assert ( list_empty ( &child->children ) ); + + /* Unlink from operations hierarchy */ + list_del ( &child->siblings ); + child->parent = NULL; + + /* Release all resources */ + if ( child->aop->reap ) { + child->aop->reap ( child ); + } else { + async_reap_default ( 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, + }, +}; |
