From mboxrd@z Thu Jan 1 00:00:00 1970 From: Ian Jackson Subject: [PATCH 17/29] libxl: cancellation: Provide public ao cancellation API Date: Tue, 10 Feb 2015 20:10:04 +0000 Message-ID: <1423599016-32639-18-git-send-email-ian.jackson@eu.citrix.com> References: <1423599016-32639-1-git-send-email-ian.jackson@eu.citrix.com> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Return-path: In-Reply-To: <1423599016-32639-1-git-send-email-ian.jackson@eu.citrix.com> List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Sender: xen-devel-bounces@lists.xen.org Errors-To: xen-devel-bounces@lists.xen.org To: xen-devel@lists.xensource.com Cc: Ian Jackson , Euan Harris List-Id: xen-devel@lists.xenproject.org Provide libxl_ao_cancel. There is machinery to allow an ao to register an interest in its cancellation, using a libxl__ao_cancellable. This API is not currently very functional: attempting cancellation it will always return NOTIMPLEMENTED and have no effect. Signed-off-by: Ian Jackson --- v2: Minor comment improvements --- tools/libxl/libxl.c | 3 ++ tools/libxl/libxl.h | 64 ++++++++++++++++++++++ tools/libxl/libxl_event.c | 123 ++++++++++++++++++++++++++++++++++++++++++ tools/libxl/libxl_internal.h | 42 ++++++++++++++- 4 files changed, 231 insertions(+), 1 deletion(-) diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c index ae0c7e1..193493b 100644 --- a/tools/libxl/libxl.c +++ b/tools/libxl/libxl.c @@ -73,6 +73,8 @@ int libxl_ctx_alloc(libxl_ctx **pctx, int version, LIBXL_LIST_INIT(&ctx->evtchns_waiting); libxl__ev_fd_init(&ctx->evtchn_efd); + LIBXL_LIST_INIT(&ctx->aos_inprogress); + LIBXL_TAILQ_INIT(&ctx->death_list); libxl__ev_xswatch_init(&ctx->death_watch); @@ -174,6 +176,7 @@ int libxl_ctx_free(libxl_ctx *ctx) assert(LIBXL_LIST_EMPTY(&ctx->efds)); assert(LIBXL_TAILQ_EMPTY(&ctx->etimes)); assert(LIBXL_LIST_EMPTY(&ctx->evtchns_waiting)); + assert(LIBXL_LIST_EMPTY(&ctx->aos_inprogress)); if (ctx->xch) xc_interface_close(ctx->xch); libxl_version_info_dispose(&ctx->version_info); diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h index 9385e82..e8a2a91 100644 --- a/tools/libxl/libxl.h +++ b/tools/libxl/libxl.h @@ -642,6 +642,11 @@ typedef struct libxl__ctx libxl_ctx; */ #define LIBXL_HAVE_DEVICE_CHANNEL 1 +/* + * LIBXL_HAVE_AO_CANCEL indicates the availability of libxl_ao_cancel + */ +#define LIBXL_HAVE_AO_CANCEL 1 + /* Functions annotated with LIBXL_EXTERNAL_CALLERS_ONLY may not be * called from within libxl itself. Callers outside libxl, who * do not #include libxl_internal.h, are fine. */ @@ -904,6 +909,65 @@ typedef struct { void *for_callback; /* passed to callback */ } libxl_asyncprogress_how; +/* + * It is sometimes possible to cancel an asynchronous operation. + * + * libxl_ao_cancel searches for an ongoing asynchronous operation whose + * ao_how is identical to *how, and tries to cancel it. The return + * values from libxl_ao_cancel are as follows: + * + * 0 + * + * The operation in question has (at least some) support for + * cancellation. It will be cut short. However, it may still + * take some time to cancel. + * + * ERROR_NOTFOUND + * + * No matching ongoing operation was found. This might happen + * for an actual operation if the operation has already completed + * (perhaps on another thread). The call to libxl_ao_cancel has + * had no effect. + * + * ERROR_NOTIMPLEMENTED + * + * As far as could be determined, the operation in question does + * not support cancellation. The operation may subsequently + * complete normally, as if it had never been cancelled; however, + * the cancellation attempt will still have been noted and it is + * possible that the operation will be successfully cancelled. + * + * ERROR_CANCELLED + * + * The operation has already been the subject of at least one + * call to libxl_ao_cancel. + * + * If the operation was indeed cut short due to the cancellation, it + * will complete, at some point in the future, with ERROR_CANCELLED. + * In that case, depending on the operation it have performed some of + * the work in question and left the operation half-done. Consult the + * documentation for individual operations. + * + * Note that a cancelled operation might still fail for other reasons + * even after it has been cancelled. + * + * If your application is multithreaded you must not reuse an + * ao_how->for_event or ao_how->for_callback value (with a particular + * ao_how->callback) unless you are sure that none of your other + * threads are going to cancel the previous operation using that + * value; otherwise you risk cancelling the wrong operation if the + * intended target of the cancellation completes in the meantime. + * + * It is possible to cancel even an operation which is being performed + * synchronously, but since in that case how==NULL you had better only + * have one such operation, because it is not possible to tell them + * apart. (And, if you want to do this, obviously the cancellation + * would have to be requested on a different thread.) + */ +int libxl_ao_cancel(libxl_ctx *ctx, const libxl_asyncop_how *how) + LIBXL_EXTERNAL_CALLERS_ONLY; + + #define LIBXL_VERSION 0 /* context functions */ diff --git a/tools/libxl/libxl_event.c b/tools/libxl/libxl_event.c index 80677e0..55013ef 100644 --- a/tools/libxl/libxl_event.c +++ b/tools/libxl/libxl_event.c @@ -1773,6 +1773,7 @@ void libxl__ao_abort(libxl__ao *ao) assert(ao->in_initiator); assert(!ao->complete); assert(!ao->progress_reports_outstanding); + assert(!ao->cancelling); libxl__ao__destroy(CTX, ao); } @@ -1938,6 +1939,128 @@ int libxl__ao_inprogress(libxl__ao *ao, } +/* cancellation */ + +static int ao__cancel(libxl_ctx *ctx, libxl__ao *parent) +/* Temporarily unlocks ctx, which must be locked exactly once on entry. */ +{ + int rc; + ao__manip_enter(parent); + + if (parent->cancelling) { + rc = ERROR_CANCELLED; + goto out; + } + + parent->cancelling = 1; + + if (LIBXL_LIST_EMPTY(&parent->cancellables)) { + LIBXL__LOG(ctx, XTL_DEBUG, + "ao %p: cancellation requested, but not not implemented", + parent); + rc = ERROR_NOTIMPLEMENTED; + goto out; + } + + /* We keep calling cancellation hooks until there are none left */ + while (!LIBXL_LIST_EMPTY(&parent->cancellables)) { + libxl__egc egc; + LIBXL_INIT_EGC(egc,ctx); + + assert(!parent->complete); + + libxl__ao_cancellable *canc = LIBXL_LIST_FIRST(&parent->cancellables); + assert(parent == ao_nested_root(canc->ao)); + + LIBXL_LIST_REMOVE(canc, entry); + canc->registered = 0; + + LIBXL__LOG(ctx, XTL_DEBUG, "ao %p: canc=%p: cancelling", + parent, canc->ao); + canc->callback(&egc, canc, ERROR_CANCELLED); + + libxl__ctx_unlock(ctx); + libxl__egc_cleanup(&egc); + libxl__ctx_lock(ctx); + } + + rc = 0; + + out: + ao__manip_leave(ctx, parent); + return rc; +} + +_hidden int libxl_ao_cancel(libxl_ctx *ctx, const libxl_asyncop_how *how) +{ + libxl__ao *search; + libxl__ctx_lock(ctx); + int rc; + + LIBXL_LIST_FOREACH(search, &ctx->aos_inprogress, inprogress_entry) { + if (how) { + /* looking for ao to be reported by callback or event */ + if (search->poller) + /* sync */ + continue; + if (how->callback != search->how.callback) + continue; + if (how->callback + ? (how->u.for_callback != search->how.u.for_callback) + : (how->u.for_event != search->how.u.for_event)) + continue; + } else { + /* looking for synchronous call */ + if (!search->poller) + /* async */ + continue; + } + goto found; + } + rc = ERROR_NOTFOUND; + goto out; + + found: + rc = ao__cancel(ctx, search); + out: + libxl__ctx_unlock(ctx); + return rc; +} + +int libxl__ao_cancellable_register(libxl__ao_cancellable *canc) +{ + libxl__ao *ao = canc->ao; + libxl__ao *root = ao_nested_root(ao); + AO_GC; + + if (root->cancelling) { + DBG("ao=%p: preemptively cancelling cancellable registration %p (root=%p)", + ao, canc, root); + return ERROR_CANCELLED; + } + + DBG("ao=%p, canc=%p: registering (root=%p)", ao, canc, root); + LIBXL_LIST_INSERT_HEAD(&root->cancellables, canc, entry); + canc->registered = 1; + + return 0; +} + +_hidden void libxl__ao_cancellable_deregister(libxl__ao_cancellable *canc) +{ + if (!canc->registered) + return; + + libxl__ao *ao = canc->ao; + libxl__ao *root __attribute__((unused)) = ao_nested_root(ao); + AO_GC; + + DBG("ao=%p, canc=%p: deregistering (root=%p)", ao, canc, root); + LIBXL_LIST_REMOVE(canc, entry); + canc->registered = 0; +} + + /* progress reporting */ /* The application indicates a desire to ignore events by passing NULL diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h index d2c2637..46383c4 100644 --- a/tools/libxl/libxl_internal.h +++ b/tools/libxl/libxl_internal.h @@ -173,6 +173,41 @@ struct libxl__ev_fd { }; +typedef struct libxl__ao_cancellable libxl__ao_cancellable; +typedef void libxl__ao_cancellable_callback(libxl__egc *egc, + libxl__ao_cancellable *cancellable, int rc /* CANCELLED */); + +struct libxl__ao_cancellable { + /* caller must fill this in and it must remain valid */ + libxl__ao *ao; + libxl__ao_cancellable_callback *callback; + /* remainder is private for cancellation machinery */ + bool registered; + LIBXL_LIST_ENTRY(libxl__ao_cancellable) entry; + /* + * For nested aos: + * Semantically, cancellation affects the whole tree of aos, + * not just the parent. + * libxl__ao_cancellable.ao refers to the child, so + * that the child callback sees the right ao. (After all, + * it was code dealing with the child that set .ao.) + * But, the cancellable is recorded on the "cancellables" list + * for the ultimate root ao, so that every possible child + * cancellation occurs as a result of the cancellation of the + * parent. + * We set ao->cancelling only in the root. + */ +}; + +_hidden int libxl__ao_cancellable_register(libxl__ao_cancellable*); +_hidden void libxl__ao_cancellable_deregister(libxl__ao_cancellable*); + +static inline void libxl__ao_cancellable_init + (libxl__ao_cancellable *c) { c->registered = 0; } +static inline bool libxl__ao_cancellable_isregistered + (const libxl__ao_cancellable *c) { return c->registered; } + + typedef struct libxl__ev_time libxl__ev_time; typedef void libxl__ev_time_callback(libxl__egc *egc, libxl__ev_time *ev, const struct timeval *requested_abs, @@ -362,6 +397,8 @@ struct libxl__ctx { LIBXL_LIST_HEAD(, libxl__ev_evtchn) evtchns_waiting; libxl__ev_fd evtchn_efd; + LIBXL_LIST_HEAD(, libxl__ao) aos_inprogress; + LIBXL_TAILQ_HEAD(libxl__evgen_domain_death_list, libxl_evgen_domain_death) death_list /* sorted by domid */, death_reported; @@ -448,12 +485,15 @@ struct libxl__ao { * only in libxl__ao_complete.) */ uint32_t magic; - unsigned constructing:1, in_initiator:1, complete:1, notified:1; + unsigned constructing:1, in_initiator:1, complete:1, notified:1, + cancelling:1; int manip_refcnt; libxl__ao *nested_root; int nested_progeny; int progress_reports_outstanding; int rc; + LIBXL_LIST_HEAD(, libxl__ao_cancellable) cancellables; + LIBXL_LIST_ENTRY(libxl__ao) inprogress_entry; libxl__gc gc; libxl_asyncop_how how; libxl__poller *poller; -- 1.7.10.4