#ifndef _GPXE_ASYNC_H #define _GPXE_ASYNC_H /** @file * * Asynchronous operations * */ #include struct async; /** An asynchronous operation ID * * Only positive identifiers are valid; negative values are used to * indicate errors. */ 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 }; /** * A signal handler * * @v async Asynchronous operation * @v signal Signal received */ 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_uninit ( struct async *async ); 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 /** * Initialise orphan asynchronous operation * * @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 aid_t async_init_orphan ( struct async *async ) { return async_init ( async, &orphan_async_operations, NULL ); } /** * Execute and block on an asynchronous operation * * @v async_temp Temporary asynchronous operation structure to use * @v START Code used to start the asynchronous operation * @ret rc Return status code * * This is a notational shorthand for writing * * async_init_orphan ( &async_temp ); * if ( ( rc = START ) == 0 ) * async_wait ( &async_temp ); * if ( rc != 0 ) { * ...handle failure... * } * * and allows you instead to write * * if ( ( rc = async_block ( &async_temp, START ) ) != 0 ) { * ...handle failure... * } * * The argument START is a code snippet; it should initiate an * asynchronous operation as a child of @c async_temp and return an * error status code if it failed to do so (e.g. due to malloc() * failure). */ #define async_block( async_temp, START ) ( { \ int rc; \ \ async_init_orphan ( async_temp ); \ if ( ( rc = START ) == 0 ) \ async_wait ( async_temp, &rc, 1 ); \ rc; \ } ) /** * Execute and block on an asynchronous operation, with progress indicator * * @v async_temp Temporary asynchronous operation structure to use * @v START Code used to start the asynchronous operation * @ret rc Return status code * * As for async_block(), the argument START is a code snippet; it * should initiate an asynchronous operation as a child of @c * async_temp and return an error status code if it failed to do so * (e.g. due to malloc() failure). */ #define async_block_progress( async_temp, START ) ( { \ int rc; \ \ async_init_orphan ( async_temp ); \ if ( ( rc = START ) == 0 ) \ async_wait_progress ( async_temp, &rc );\ rc; \ } ) #endif /* _GPXE_ASYNC_H */