[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH 08/11] libxl: Asynchronous/long-running operation infrastructure
2012/1/26 Ian Jackson <ian.jackson@xxxxxxxxxxxxx>: > Provide a new set of machinery for writing public libxl functions > which may take a long time. ÂThe application gets to decide whether > they want the function to be synchronous, or whether they'd prefer to > get a callback, or an event, when the operation is complete. > > User(s) of this machinery will be introduced in later patch(es). > > Signed-off-by: Ian Jackson <ian.jackson@xxxxxxxxxxxxx> > --- > Âtools/libxl/libxl.h     Â|  53 ++++++++++++ > Âtools/libxl/libxl_event.c  Â| Â188 > ++++++++++++++++++++++++++++++++++++++++++ > Âtools/libxl/libxl_internal.h | Â107 ++++++++++++++++++++++++ > Âtools/libxl/libxl_types.idl Â|  Â4 + > Â4 files changed, 352 insertions(+), 0 deletions(-) > > diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h > index e32881b..fa79c24 100644 > --- a/tools/libxl/libxl.h > +++ b/tools/libxl/libxl.h > @@ -237,6 +237,59 @@ enum { >   ERROR_BUFFERFULL = -13, > Â}; > > + > +/* > + * Some libxl operations can take a long time. ÂThese functions take a > + * parameter to control their concurrency: > + *   libxl_asyncop_how *ao_how > + * > + * If ao_how==NULL, the function will be synchronous. > + * > + * If ao_how!=NULL, the function will set the operation going, and if > + * this is successful will return 0. ÂIn this case the zero error > + * response does NOT mean that the operation was successful; it just > + * means that it has been successfully started. ÂIt will finish later, > + * perhaps with an error. > + * > + * If ao_how->callback!=NULL, the callback will be called when the > + * operation completes. ÂThe same rules as for libxl_event_hooks > + * apply, including the reentrancy rules and the possibility of > + * "disaster", except that libxl calls ao_how->callback instead of > + * libxl_event_hooks.event_occurs. Â(See libxl_event.h.) > + * > + * If ao_how->callback==NULL, a libxl_event will be generated which > + * can be obtained from libxl_event_wait or libxl_event_check. ÂThe > + * event will have type OPERATION_COMPLETE (which is not used > + * elsewhere). > + * > + * Note that it is possible for an asynchronous operation which is to > + * result in a callback to complete during its initiating function > + * call. ÂIn this case the initiating function will return 0 > + * indicating the at the operation is "in progress", even though by > + * the time it returns the operation is complete and the callback has > + * already happened. > + * > + * The application must set and use ao_how->for_event (which will be > + * copied into libxl_event.for_user) or ao_how->for_callback (passed > + * to the callback) to determine which operation finished, and it must > + * of course check the rc value for errors. > + * > + * *ao_how does not need to remain valid after the initiating function > + * returns. > + * > + * Callbacks may occur on any thread in which the application calls > + * libxl. > + */ > + > +typedef struct { > +  Âvoid (*callback)(libxl_ctx *ctx, int rc, void *for_callback); > +  Âunion { > +    Âlibxl_ev_user for_event; /* used if callback==NULL */ > +    Âvoid *for_callback; /* passed to callback */ > +  Â} u; > +} libxl_asyncop_how; > + > + > Â#define LIBXL_VERSION 0 > > Âtypedef struct { > diff --git a/tools/libxl/libxl_event.c b/tools/libxl/libxl_event.c > index 73dfd9d..9e1ab56 100644 > --- a/tools/libxl/libxl_event.c > +++ b/tools/libxl/libxl_event.c > @@ -771,10 +771,21 @@ static void egc_run_callbacks(libxl__egc *egc) > Â{ >   EGC_GC; >   libxl_event *ev, *ev_tmp; > + >   LIBXL_TAILQ_FOREACH_SAFE(ev, &egc->occurred_for_callback, link, ev_tmp) { >     LIBXL_TAILQ_REMOVE(&egc->occurred_for_callback, ev, link); >     CTX->event_hooks->event_occurs(CTX->event_hooks_user, ev); >   } > + > +  Âlibxl__ao *ao, *ao_tmp; > +  ÂLIBXL_TAILQ_FOREACH_SAFE(ao, &egc->aos_for_callback, > +               entry_for_callback, ao_tmp) { > +    ÂLIBXL_TAILQ_REMOVE(&egc->aos_for_callback, ao, entry_for_callback); > +    Âao->how.callback(CTX, ao->rc, ao->how.u.for_callback); > +    Âao->notified = 1; > +    Âif (!ao->in_initiator) > +      Âlibxl__ao__destroy(CTX, ao); > +  Â} > Â} > > Âvoid libxl__egc_cleanup(libxl__egc *egc) > @@ -1061,6 +1072,183 @@ int libxl_event_wait(libxl_ctx *ctx, libxl_event > **event_r, >   return rc; > Â} > > + > + > +/* > + * The two possible state flow of an ao: > + * > + * Completion before initiator return: > + * > + *   Initiator thread            Possible other threads > + * > + *  * ao_create allocates memory and > + *   initialises the struct > + * > + *  * the initiator function does its > + *   work, setting up various internal > + *   asynchronous operations -----------> * asynchronous operations > + *                      Âstart to take place and > + *                      Âmight cause ao completion > + *                        Â| > + *  * initiator calls ao_inprogress       Â| > + *   - if synchronous, run event loop      | > + *    until the ao completes          | > + *               Â- ao completes on some thread > + *               Â- completing thread releases the lock > + *           <--------------' > + *   - ao_inprogress takes the lock > + *   - destroy the ao > + * > + * > + * Completion after initiator return (asynch. only): > + * > + * > + *   Initiator thread            Possible other threads > + * > + *  * ao_create allocates memory and > + *   initialises the struct > + * > + *  * the initiator function does its > + *   work, setting up various internal > + *   asynchronous operations -----------> * asynchronous operations > + *                      Âstart to take place and > + *                      Âmight cause ao completion > + *                        Â| > + *  * initiator calls ao_inprogress       Â| > + *   - observes event not yet done,       | > + *   - returns to caller            Â| > + *                        Â| > + *               Â- ao completes on some thread > + *               Â- generate the event or call the callback > + *               Â- destroy the ao > + */ > + > +void libxl__ao__destroy(libxl_ctx *ctx, libxl__ao *ao) > +{ > +  Âif (!ao) return; > +  Âif (ao->poller) libxl__poller_put(ctx, ao->poller); > +  Âao->magic = LIBXL__AO_MAGIC_DESTROYED; > +  Âlibxl__free_all(&ao->gc); > +  Âfree(ao); > +} > + > +void libxl__ao_abort(libxl__ao *ao) > +{ > +  ÂAO_GC; > +  Âassert(ao->magic == LIBXL__AO_MAGIC); > +  Âassert(ao->in_initiator); > +  Âassert(!ao->complete); > +  Âlibxl__ao__destroy(CTX, ao); > +} > + > +void libxl__ao_complete(libxl__egc *egc, libxl__ao *ao, int rc) > +{ > +  Âassert(ao->magic == LIBXL__AO_MAGIC); > +  Âassert(!ao->complete); > +  Âao->complete = 1; > +  Âao->rc = rc; > + > +  Âif (ao->poller) { > +    Âassert(ao->in_initiator); > +    Âlibxl__poller_wakeup(egc, ao->poller); > +  Â} else if (ao->how.callback) { > +    ÂLIBXL_TAILQ_INSERT_TAIL(&egc->aos_for_callback, ao, > entry_for_callback); > +  Â} else { > +    Âlibxl_event *ev; > +    Âev = NEW_EVENT(egc, OPERATION_COMPLETE, ao->domid); > +    Âif (ev) { > +      Âev->for_user = ao->how.u.for_event; > +      Âev->u.operation_complete.rc = ao->rc; > +      Âlibxl__event_occurred(egc, ev); > +    Â} > +    Âao->notified = 1; > +  Â} > +  Âif (!ao->in_initiator && ao->notified) > +    Âlibxl__ao__destroy(libxl__gc_owner(&egc->gc), ao); > +} > + > +libxl__ao *libxl__ao_create(libxl_ctx *ctx, uint32_t domid, > +              Âconst libxl_asyncop_how *how) > +{ > +  Âlibxl__ao *ao; > + > +  Âao = calloc(1, sizeof(*ao)); > +  Âif (!ao) goto out; > + > +  Âao->magic = LIBXL__AO_MAGIC; > +  Âao->in_initiator = 1; > +  Âao->poller = 0; > +  Âao->domid = domid; > +  ÂLIBXL_INIT_GC(ao->gc, ctx); > + > +  Âif (how) { > +    Âao->how = *how; > +  Â} else { > +    Âao->poller = libxl__poller_get(ctx); > +    Âif (!ao->poller) goto out; > +  Â} > +  Âreturn ao; > + > + out: > +  Âif (ao) libxl__ao__destroy(ctx, ao); > +  Âreturn NULL; > +} > + > +int libxl__ao_inprogress(libxl__ao *ao) > +{ > +  ÂAO_GC; > +  Âint rc; I've just started playing with this, but I've found that if you call AO_INPROGRESS without adding any event it blocks forever, and I'm not sure if that is a design decision or a bug. Anyway, sometimes like in libxl__initiate_device_remove you might call AO_INPROGRESS without adding any event, so I think it will be safe to add something like: if (!CTX->watch_counter) return 0; To prevent waiting forever, if not we need to be sure we only call AO_INPROGRESS when we have added some events (which is not as practical as this simple fix). Since I've just started playing with the events API, I might be doing an incorrect usage of this, if this sounds ok please drop a line and I will send a formal patch. Thanks, Roger. > + > +  Âassert(ao->magic == LIBXL__AO_MAGIC); > +  Âassert(ao->in_initiator); > + > +  Âif (ao->poller) { > +    Â/* Caller wants it done synchronously. */ > +    Â/* We use a fresh gc, so that we can free things > +     * each time round the loop. */ > +    Âlibxl__egc egc; > +    ÂLIBXL_INIT_EGC(egc,CTX); > + > +    Âfor (;;) { > +      Âassert(ao->magic == LIBXL__AO_MAGIC); > + > +      Âif (ao->complete) { > +        Ârc = ao->rc; > +        Âao->notified = 1; > +        Âbreak; > +      Â} > + > +      Ârc = eventloop_iteration(&egc,ao->poller); > +      Âif (rc) { > +        Â/* Oh dear, this is quite unfortunate. */ > +        ÂLIBXL__LOG(CTX, LIBXL__LOG_ERROR, "Error waiting for" > +              " event during long-running operation (rc=%d)", > rc); > +        Âsleep(1); > +        Â/* It's either this or return ERROR_I_DONT_KNOW_WHETHER > +         * _THE_THING_YOU_ASKED_FOR_WILL_BE_DONE_LATER_WHEN > +         * _YOU_DIDNT_EXPECT_IT, since we don't have any kind of > +         * cancellation ability. */ > +      Â} > + > +      ÂCTX_UNLOCK; > +      Âlibxl__egc_cleanup(&egc); > +      ÂCTX_LOCK; > +    Â} > +  Â} else { > +    Ârc = 0; > +  Â} > + > +  Âao->in_initiator = 0; > + > +  Âif (ao->notified) { > +    Âassert(ao->complete); > +    Âlibxl__ao__destroy(CTX,ao); > +  Â} > + > +  Âreturn rc; > +} > + > + > Â/* > Â* Local variables: > Â* mode: C > diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h > index d1b96c1..044eeb4 100644 > --- a/tools/libxl/libxl_internal.h > +++ b/tools/libxl/libxl_internal.h > @@ -114,6 +114,7 @@ _hidden void libxl__log(libxl_ctx *ctx, xentoollog_level > msglevel, int errnoval, > > Âtypedef struct libxl__gc libxl__gc; > Âtypedef struct libxl__egc libxl__egc; > +typedef struct libxl__ao libxl__ao; > > Âtypedef struct libxl__ev_fd libxl__ev_fd; > Âtypedef void libxl__ev_fd_callback(libxl__egc *egc, libxl__ev_fd *ev, > @@ -218,6 +219,10 @@ struct libxl__poller { >   Â* releasing the ctx lock and going into poll; when it comes out >   Â* of poll it will take the poller off the pollers_event list. >   Â* > +   * A thread which is waiting for completion of a synchronous ao > +   * will allocate a poller and record it in the ao, so that other > +   * threads can wake it up. > +   * >   Â* When a thread is done with a poller it should put it onto >   Â* pollers_idle, where it can be reused later. >   Â* > @@ -324,6 +329,21 @@ struct libxl__egc { >   /* for event-generating functions only */ >   struct libxl__gc gc; >   struct libxl__event_list occurred_for_callback; > +  ÂLIBXL_TAILQ_HEAD(, libxl__ao) aos_for_callback; > +}; > + > +#define LIBXL__AO_MAGIC       Â0xA0FACE00ul > +#define LIBXL__AO_MAGIC_DESTROYED  Â0xA0DEAD00ul > + > +struct libxl__ao { > +  Âuint32_t magic; > +  Âunsigned in_initiator:1, complete:1, notified:1; > +  Âint rc; > +  Âlibxl__gc gc; > +  Âlibxl_asyncop_how how; > +  Âlibxl__poller *poller; > +  Âuint32_t domid; > +  ÂLIBXL_TAILQ_ENTRY(libxl__ao) entry_for_callback; > Â}; > > Â#define LIBXL_INIT_GC(gc,ctx) do{        \ > @@ -1108,6 +1128,7 @@ libxl__device_model_version_running(libxl__gc *gc, > uint32_t domid); > Â#define LIBXL_INIT_EGC(egc,ctx) do{           \ >     LIBXL_INIT_GC((egc).gc,ctx);          Â\ >     LIBXL_TAILQ_INIT(&(egc).occurred_for_callback); \ > +    ÂLIBXL_TAILQ_INIT(&(egc).aos_for_callback);   Â\ >   } while(0) > > Â_hidden void libxl__egc_cleanup(libxl__egc *egc); > @@ -1125,6 +1146,92 @@ _hidden void libxl__egc_cleanup(libxl__egc *egc); > > > Â/* > + * Machinery for asynchronous operations ("ao") > + * > + * All "slow" functions (includes anything that might block on a > + * guest or an external script) need to use the asynchronous > + * operation ("ao") machinery. ÂThe function should take a parameter > + * const libxl_asyncop_how *ao_how and must start with a call to > + * AO_INITIATOR_ENTRY. ÂThese functions MAY NOT be called from > + * outside libxl, because they can cause reentrancy callbacks. > + * > + * Lifecycle of an ao: > + * > + * - Created by libxl__ao_create (or the AO_CREATE convenience macro). > + * > + * - After creation, can be used by code which implements > + *  the operation as follows: > + *   Â- the ao's gc, for allocating memory for the lifetime > + *    Âof the operation (possibly with the help of the AO_GC > + *    Âmacro to introduce the gc into scope) > + *   Â- the ao itself may be passed about to sub-functions > + *    Âso that they can stash it away etc. > + *   Â- in particular, the ao pointer must be stashed in some > + *    Âper-operation structure which is also passed as a user > + *    Âpointer to the internal event generation request routines > + *    Âlibxl__evgen_FOO, so that at some point a CALLBACK will be > + *    Âmade when the operation is complete. > + * > + * - If initiation is successful, the initiating function needs > + *  to run libxl__ao_inprogress right before unlocking and > + *  returning, and return whatever it returns (AO_INPROGRESS macro). > + * > + * - If the initiation is unsuccessful, the initiating function must > + *  call libxl__ao_abort before unlocking and returning whatever > + *  error code is appropriate (AO_ABORT macro). > + * > + * - Later, some callback function, whose callback has been requested > + *  directly or indirectly, should call libxl__ao_complete (with the > + *  ctx locked, as it will generally already be in any event callback > + *  function). ÂThis must happen exactly once for each ao (and not if > + *  the ao has been destroyed, obviously), and it may not happen > + *  until libxl__ao_inprogress has been called on the ao. > + * > + * - Note that during callback functions, two gcs are available: > + *  Â- The one in egc, whose lifetime is only this callback > + *  Â- The one in ao, whose lifetime is the asynchronous operation > + *  Usually callback function should use CONTAINER_OF > + *  to obtain its own structure, containing a pointer to the ao, > + *  and then use the gc from that ao. > + */ > + > +#define AO_CREATE(ctx, domid, ao_how)              \ > +  Âlibxl__ctx_lock(ctx);                    \ > +  Âlibxl__ao *ao = libxl__ao_create(ctx, domid, ao_how);    \ > +  Âif (!ao) { libxl__ctx_unlock(ctx); return ERROR_NOMEM; }  Â\ > +  ÂAO_GC; > + > +#define AO_INPROGRESS ({                    Â\ > +    Âlibxl_ctx *ao__ctx = libxl__gc_owner(&ao->gc);     Â\ > +    Âint ao__rc = libxl__ao_inprogress(ao);         Â\ > +    Âlibxl__ctx_unlock(ao__ctx); /* gc is now invalid */   \ > +    Â(ao__rc);                        \ > +  }) > + > +#define AO_ABORT(rc) ({                     \ > +    Âlibxl_ctx *ao__ctx = libxl__gc_owner(&ao->gc);     Â\ > +    Âassert(rc);                       \ > +    Âlibxl__ao_abort(ao);                  Â\ > +    Âlibxl__ctx_unlock(ao__ctx); /* gc is now invalid */   \ > +    Â(rc);                          \ > +  Â}) > + > +#define AO_GC                  \ > +  Âlibxl__gc *const gc = &ao->gc > + > + > +/* All of these MUST be called with the ctx locked. > + * libxl__ao_inprogress MUST be called with the ctx locked exactly once. */ > +_hidden libxl__ao *libxl__ao_create(libxl_ctx*, uint32_t domid, > +                  Âconst libxl_asyncop_how*); > +_hidden int libxl__ao_inprogress(libxl__ao *ao); > +_hidden void libxl__ao_abort(libxl__ao *ao); > +_hidden void libxl__ao_complete(libxl__egc *egc, libxl__ao *ao, int rc); > + > +/* For use by ao machinery ONLY */ > +_hidden void libxl__ao__destroy(libxl_ctx*, libxl__ao *ao); > + > +/* > Â* Convenience macros. > Â*/ > > diff --git a/tools/libxl/libxl_types.idl b/tools/libxl/libxl_types.idl > index 35b53d6..8871c17 100644 > --- a/tools/libxl/libxl_types.idl > +++ b/tools/libxl/libxl_types.idl > @@ -395,6 +395,7 @@ libxl_event_type = Enumeration("event_type", [ >   (1, "DOMAIN_SHUTDOWN"), >   (2, "DOMAIN_DESTROY"), >   (3, "DISK_EJECT"), > +  Â(4, "OPERATION_COMPLETE"), >   ]) > > Âlibxl_ev_user = UInt(64) > @@ -418,4 +419,7 @@ libxl_event = Struct("event",[ >                     ("vdev", string), >                     ("disk", libxl_device_disk), >                 Â])), > +      ("operation_complete", Struct(None, [ > +                    Â("rc", integer), > +                 ])), >      Â]))]) > -- > 1.7.2.5 > > > _______________________________________________ > Xen-devel mailing list > Xen-devel@xxxxxxxxxxxxxxxxxxx > http://lists.xensource.com/xen-devel _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |