Provide libxl_ao_abort.
There is machinery to allow an ao to register an interest in abort
requests, using a libxl__ao_abortable.
This API is not currently very functional: requesting abort will
never have any effect.
Signed-off-by: Ian Jackson <Ian.Jackson@eu.citrix.com>
Acked-by: Wei Liu <wei.liu2@citrix.com>
---
v5: Clarify API doc comment re aborting synchronous operations.
v4: Rename from cancel to abort.
Actually record aos on aos_inprogress.
(Report from Koushik Chakravarty at Citrix.)
Do not mark libxl_ao_cancel hidden (!)
Abolish ERROR_NOTIMPLEMENTED from libxl_ao_cancel.
All operations are supposed to support cancellation.
v2: Minor comment improvements
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);
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);
*/
#define LIBXL_HAVE_DEVICE_CHANNEL 1
+/*
+ * LIBXL_HAVE_AO_ABORT indicates the availability of libxl_ao_abort
+ */
+#define LIBXL_HAVE_AO_ABORT 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. */
void *for_callback; /* passed to callback */
} libxl_asyncprogress_how;
+/*
+ * It is sometimes possible to abort an asynchronous operation.
+ *
+ * libxl_ao_abort searches for an ongoing asynchronous operation whose
+ * ao_how is identical to *how, and tries to abort it. The return
+ * values from libxl_ao_abort are as follows:
+ *
+ * 0
+ *
+ * The operation was found, and attempts are being made to cut it
+ * short. However, it may still take some time to stop. It is
+ * also possible that the operation will nevertheless complete
+ * successfully.
+ *
+ * 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_abort has
+ * had no effect.
+ *
+ * ERROR_ABORTED
+ *
+ * The operation has already been the subject of at least one
+ * call to libxl_ao_abort.
+ *
+ * If the operation was indeed cut short due to the abort request, it
+ * will complete, at some point in the future, with ERROR_ABORTED. 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 an aborted operation might still fail for other reasons
+ * even after the abort was requested.
+ *
+ * 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 abort the previous operation using that
+ * value; otherwise you risk aborting the wrong operation if the
+ * intended target of the abort request completes in the meantime.
+ *
+ * It is possible to abort 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 libxl_ao_abort will abort only the first one it finds).
+ * (And, if you want to do this, obviously the abort would have to be
+ * requested on a different thread.)
+ */
+int libxl_ao_abort(libxl_ctx *ctx, const libxl_asyncop_how *how)
+ LIBXL_EXTERNAL_CALLERS_ONLY;
+
+
#define LIBXL_VERSION 0
/* context functions */
assert(ao->in_initiator);
assert(!ao->complete);
assert(!ao->progress_reports_outstanding);
+ assert(!ao->aborting);
+ LIBXL_LIST_REMOVE(ao, inprogress_entry);
libxl__ao__destroy(CTX, ao);
}
assert(!ao->nested_progeny);
ao->complete = 1;
ao->rc = rc;
-
+ LIBXL_LIST_REMOVE(ao, inprogress_entry);
libxl__ao_complete_check_progress_reports(egc, ao);
}
"ao %p: create: how=%p callback=%p poller=%p",
ao, how, ao->how.callback, ao->poller);
+ LIBXL_LIST_INSERT_HEAD(&ctx->aos_inprogress, ao, inprogress_entry);
+
return ao;
out:
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. */
+ * _YOU_DIDNT_EXPECT_IT, since we don't have a
+ * synchronous cancellation ability. */
}
CTX_UNLOCK;
}
+/* abort requests */
+
+static int ao__abort(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->aborting) {
+ rc = ERROR_ABORTED;
+ goto out;
+ }
+
+ parent->aborting = 1;
+
+ if (LIBXL_LIST_EMPTY(&parent->abortables)) {
+ LIBXL__LOG(ctx, XTL_DEBUG,
+ "ao %p: abort requested and noted, but no-one interested",
+ parent);
+ rc = 0;
+ goto out;
+ }
+
+ /* We keep calling abort hooks until there are none left */
+ while (!LIBXL_LIST_EMPTY(&parent->abortables)) {
+ libxl__egc egc;
+ LIBXL_INIT_EGC(egc,ctx);
+
+ assert(!parent->complete);
+
+ libxl__ao_abortable *abrt = LIBXL_LIST_FIRST(&parent->abortables);
+ assert(parent == ao_nested_root(abrt->ao));
+
+ LIBXL_LIST_REMOVE(abrt, entry);
+ abrt->registered = 0;
+
+ LIBXL__LOG(ctx, XTL_DEBUG, "ao %p: abrt=%p: aborting",
+ parent, abrt->ao);
+ abrt->callback(&egc, abrt, ERROR_ABORTED);
+
+ libxl__ctx_unlock(ctx);
+ libxl__egc_cleanup(&egc);
+ libxl__ctx_lock(ctx);
+ }
+
+ rc = 0;
+
+ out:
+ ao__manip_leave(ctx, parent);
+ return rc;
+}
+
+int libxl_ao_abort(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__abort(ctx, search);
+ out:
+ libxl__ctx_unlock(ctx);
+ return rc;
+}
+
+int libxl__ao_abortable_register(libxl__ao_abortable *abrt)
+{
+ libxl__ao *ao = abrt->ao;
+ libxl__ao *root = ao_nested_root(ao);
+ AO_GC;
+
+ if (root->aborting) {
+ DBG("ao=%p: preemptively aborting ao_abortable registration %p (root=%p)",
+ ao, abrt, root);
+ return ERROR_ABORTED;
+ }
+
+ DBG("ao=%p, abrt=%p: registering (root=%p)", ao, abrt, root);
+ LIBXL_LIST_INSERT_HEAD(&root->abortables, abrt, entry);
+ abrt->registered = 1;
+
+ return 0;
+}
+
+_hidden void libxl__ao_abortable_deregister(libxl__ao_abortable *abrt)
+{
+ if (!abrt->registered)
+ return;
+
+ libxl__ao *ao = abrt->ao;
+ libxl__ao *root __attribute__((unused)) = ao_nested_root(ao);
+ AO_GC;
+
+ DBG("ao=%p, abrt=%p: deregistering (root=%p)", ao, abrt, root);
+ LIBXL_LIST_REMOVE(abrt, entry);
+ abrt->registered = 0;
+}
+
+
/* progress reporting */
/* The application indicates a desire to ignore events by passing NULL
};
+typedef struct libxl__ao_abortable libxl__ao_abortable;
+typedef void libxl__ao_abortable_callback(libxl__egc *egc,
+ libxl__ao_abortable *ao_abortable, int rc /* ABORTED */);
+
+struct libxl__ao_abortable {
+ /* caller must fill this in and it must remain valid */
+ libxl__ao *ao;
+ libxl__ao_abortable_callback *callback;
+ /* remainder is private for abort machinery */
+ bool registered;
+ LIBXL_LIST_ENTRY(libxl__ao_abortable) entry;
+ /*
+ * For nested aos:
+ * Semantically, abort affects the whole tree of aos,
+ * not just the parent.
+ * libxl__ao_abortable.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 abortable is recorded on the "abortables" list
+ * for the ultimate root ao, so that every possible child
+ * abort occurs as a result of the abort of the parent.
+ * We set ao->aborting only in the root.
+ */
+};
+
+_hidden int libxl__ao_abortable_register(libxl__ao_abortable*);
+_hidden void libxl__ao_abortable_deregister(libxl__ao_abortable*);
+
+static inline void libxl__ao_abortable_init
+ (libxl__ao_abortable *c) { c->registered = 0; }
+static inline bool libxl__ao_abortable_isregistered
+ (const libxl__ao_abortable *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,
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;
* 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,
+ aborting:1;
int manip_refcnt;
libxl__ao *nested_root;
int nested_progeny;
int progress_reports_outstanding;
int rc;
+ LIBXL_LIST_HEAD(, libxl__ao_abortable) abortables;
+ LIBXL_LIST_ENTRY(libxl__ao) inprogress_entry;
libxl__gc gc;
libxl_asyncop_how how;
libxl__poller *poller;