diff options
Diffstat (limited to 'src/include/gpxe/async.h')
| -rw-r--r-- | src/include/gpxe/async.h | 184 |
1 files changed, 146 insertions, 38 deletions
diff --git a/src/include/gpxe/async.h b/src/include/gpxe/async.h index 8a6819786..d3b075b92 100644 --- a/src/include/gpxe/async.h +++ b/src/include/gpxe/async.h @@ -7,56 +7,164 @@ * */ -#include <errno.h> -#include <assert.h> +#include <gpxe/list.h> -/** An asynchronous operation */ -struct async_operation { - /** Operation status - * - * This is an error code as defined in errno.h, plus an offset - * of EINPROGRESS. This means that a status value of 0 - * corresponds to a return status code of -EINPROGRESS, - * i.e. that the default state of an asynchronous operation is - * "not yet completed". - */ - int status; -}; +struct async; -/** - * Set asynchronous operation status +/** An asynchronous operation ID * - * @v aop Asynchronous operation - * @v rc Return status code + * Only positive identifiers are valid; negative values are used to + * indicate errors. */ -static inline __attribute__ (( always_inline )) void -async_set_status ( struct async_operation *aop, int rc ) { - aop->status = ( rc + EINPROGRESS ); -} +typedef long aid_t; + +/** Signals that can be delivered to asynchronous operations */ +enum signal { + /** A child asynchronous operation has completed + * + * The parent should call async_wait() to reap the completed + * child. async_wait() will return the exit status and + * operation identifier of the child. + * + * The handler for this signal can be set to @c NULL; if it + * is, then the children will accumulate as zombies until + * async_wait() is called. + * + * The handler for this signal can also be set to @c SIG_IGN; + * if it is, then the children will automatically be reaped. + * Note that if you use @c SIG_IGN then you will not be able + * to retrieve the return status of the children; the call to + * async_wait() will simply return -ECHILD. + */ + SIGCHLD = 0, + /** Cancel asynchronous operation + * + * This signal should trigger the asynchronous operation to + * cancel itself (including killing all its own children, if + * any), and then call async_done(). The asynchronous + * operation is allowed to not complete immediately. + * + * The handler for this signal can be set to @c NULL; if it + * is, then attempts to cancel the asynchronous operation will + * fail and the operation will complete normally. Anything + * waiting for the operation to cancel will block. + */ + SIGKILL, + /** Update progress of asynchronous operation + * + * This signal should cause the asynchronous operation to + * immediately update the @c completed and @c total fields. + * + * The handler for this signal can be set to @c NULL; if it + * is, then the asynchronous operation is expected to keep its + * @c completed and @c total fields up to date at all times. + */ + SIGUPDATE, + SIGMAX +}; /** - * Get asynchronous operation status + * A signal handler * - * @v aop Asynchronous operation - * @ret rc Return status code + * @v async Asynchronous operation + * @v signal Signal received */ -static inline __attribute__ (( always_inline )) int -async_status ( struct async_operation *aop ) { - return ( aop->status - EINPROGRESS ); -} +typedef void ( * signal_handler_t ) ( struct async *async, + enum signal signal ); + +/** Asynchronous operation operations */ +struct async_operations { + /** Reap asynchronous operation + * + * @v async Asynchronous operation + * + * Release all resources associated with the asynchronous + * operation. This will be called only after the asynchronous + * operation itself calls async_done(), so the only remaining + * resources will probably be the memory used by the struct + * async itself. + * + * This method can be set to @c NULL; if it is, then no + * resources will be freed. This may be suitable for + * asynchronous operations that consume no dynamically + * allocated memory. + */ + void ( * reap ) ( struct async *async ); + /** Handle signals */ + signal_handler_t signal[SIGMAX]; +}; + +/** An asynchronous operation */ +struct async { + /** Other asynchronous operations with the same parent */ + struct list_head siblings; + /** Child asynchronous operations */ + struct list_head children; + /** Parent asynchronous operation + * + * This field is optional; if left to NULL then the owner must + * never call async_done(). + */ + struct async *parent; + /** Asynchronous operation ID */ + aid_t aid; + /** Final return status code */ + int rc; + + /** Amount of operation completed so far + * + * The units for this quantity are arbitrary. @c completed + * divded by @total should give something which approximately + * represents the progress through the operation. For a + * download operation, using byte counts would make sense. + * + * This progress indicator should also incorporate the status + * of any child asynchronous operations. + */ + unsigned long completed; + /** Total operation size + * + * See @c completed. A zero value means "total size unknown" + * and is explcitly permitted; users should take this into + * account before calculating @c completed/total. + */ + unsigned long total; + + struct async_operations *aop; +}; + +extern struct async_operations default_async_operations; +extern struct async_operations orphan_async_operations; + +extern aid_t async_init ( struct async *async, struct async_operations *aop, + struct async *parent ); +extern void async_ignore_signal ( struct async *async, enum signal signal ); +extern void async_signal ( struct async *async, enum signal signal ); +extern void async_signal_children ( struct async *async, enum signal signal ); +extern void async_done ( struct async *async, int rc ); +extern aid_t async_wait ( struct async *async, int *rc, int block ); + +/** Default signal handler */ +#define SIG_DFL NULL + +/** Ignore signal */ +#define SIG_IGN async_ignore_signal /** - * Flag asynchronous operation as complete + * Initialise orphan asynchronous operation * - * @v aop Asynchronous operation - * @v rc Return status code + * @v async Asynchronous operation + * @ret aid Asynchronous operation ID + * + * An orphan asynchronous operation can act as a context for child + * operations. However, you must not call async_done() on such an + * operation, since this would attempt to send a signal to its + * (non-existent) parent. Instead, simply free the structure (after + * calling async_wait() to ensure that any child operations have + * completed). */ -static inline __attribute__ (( always_inline )) void -async_done ( struct async_operation *aop, int rc ) { - assert ( rc != -EINPROGRESS ); - async_set_status ( aop, rc ); +static inline aid_t async_init_orphan ( struct async *async ) { + return async_init ( async, &orphan_async_operations, NULL ); } -extern int async_wait ( struct async_operation *aop ); - #endif /* _GPXE_ASYNC_H */ |
