/* * Copyright (C) 2006 Michael Brown . * * 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 #include #include #include #include /** @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"; } } /** * 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, }, };