[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH 18/20] libxl: provide progress reporting for long-running operations



This will be used for reporting, during domain creation, that the
console is ready.

Signed-off-by: Ian Jackson <ian.jackson@xxxxxxxxxxxxx>
---
 tools/libxl/libxl.h          |   45 +++++++++++++++
 tools/libxl/libxl_event.c    |  122 ++++++++++++++++++++++++++++++++----------
 tools/libxl/libxl_internal.h |   28 ++++++++++
 3 files changed, 167 insertions(+), 28 deletions(-)

diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h
index 6f59364..1bfe684 100644
--- a/tools/libxl/libxl.h
+++ b/tools/libxl/libxl.h
@@ -435,6 +435,51 @@ typedef struct {
     } u;
 } libxl_asyncop_how;
 
+/*
+ * Some more complex asynchronous operations can report intermediate
+ * progress.  How this is to be reported is controlled, for each
+ * function, by a parameter
+ *    libxl_asyncprogress_how *aop_FOO_how;
+ * for each kind of progress FOO supported by that function.  Each
+ * such kind of progress is associated with an event type.
+ *
+ * The function description will document whether, when, and how
+ * many times, the intermediate progress will be reported, and
+ * what the corresponding event type(s) are.
+ *
+ * If aop_FOO_how==NULL, intermediate progress reports are discarded.
+ *
+ * If aop_FOO_how->callback==NULL, intermediate progress reports
+ * generate libxl events which can be obtained from libxl_event_wait
+ * or libxl_event_check.
+ *
+ * If aop_FOO_how->callback!=NULL, libxl will report intermediate
+ * progress by calling callback(ctx, &event, for_callback).
+ *
+ * The rules for these events are otherwise the same as those for
+ * ordinary events.  The reentrancy and threading rules for the
+ * callback are the same as those for ao completion callbacks.
+ *
+ * Note that the callback, if provided, is responsible for freeing
+ * the event.
+ *
+ * If callbacks are requested, they will be made, and returned, before
+ * the long-running libxl operation is considered finished (so if the
+ * long-running libxl operation was invoked with ao_how==NULL then any
+ * callbacks will occur strictly before the long-running operation
+ * returns).  However, the callbacks may occur on any thread.
+ *
+ * In general, otherwise, no promises are made about the relative
+ * order of callbacks in a multithreaded program.  In particular
+ * different callbacks relating to the same long-running operation may
+ * be delivered out of order.
+ */
+
+typedef struct {
+    void (*callback)(libxl_ctx *ctx, libxl_event*, void *for_callback);
+    libxl_ev_user for_event; /* always used */
+    void *for_callback; /* passed to callback */
+} libxl_asyncprogress_how;
 
 #define LIBXL_VERSION 0
 
diff --git a/tools/libxl/libxl_event.c b/tools/libxl/libxl_event.c
index 1b7495a..f355e3d 100644
--- a/tools/libxl/libxl_event.c
+++ b/tools/libxl/libxl_event.c
@@ -899,12 +899,25 @@ static void egc_run_callbacks(libxl__egc *egc)
 {
     EGC_GC;
     libxl_event *ev, *ev_tmp;
+    libxl__aop_occurred *aop, *aop_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_TAILQ_FOREACH_SAFE(aop, &egc->aops_for_callback, entry, aop_tmp) {
+        LIBXL_TAILQ_REMOVE(&egc->aops_for_callback, aop, entry);
+        aop->how->callback(CTX, aop->ev, aop->how->for_callback);
+
+        CTX_LOCK;
+        aop->ao->progress_reports_outstanding--;
+        libxl__ao_complete_check_progress_reports(egc, aop->ao);
+        CTX_UNLOCK;
+
+        free(aop);
+    }
+
     libxl__ao *ao, *ao_tmp;
     LIBXL_TAILQ_FOREACH_SAFE(ao, &egc->aos_for_callback,
                              entry_for_callback, ao_tmp) {
@@ -1285,6 +1298,7 @@ void libxl__ao_abort(libxl__ao *ao)
     assert(ao->magic == LIBXL__AO_MAGIC);
     assert(ao->in_initiator);
     assert(!ao->complete);
+    assert(!ao->progress_reports_outstanding);
     libxl__ao__destroy(CTX, ao);
 }
 
@@ -1295,6 +1309,23 @@ void libxl__ao_complete(libxl__egc *egc, libxl__ao *ao, 
int rc)
     ao->complete = 1;
     ao->rc = rc;
 
+    libxl__ao_complete_check_progress_reports(egc, ao);
+}
+
+void libxl__ao_complete_check_progress_reports(libxl__egc *egc, libxl__ao *ao)
+{
+    /* We don't consider an ao complete if it has any outstanding
+     * callbacks.  These callbacks might be outstanding on other
+     * threads, queued up in the other threads' egc's.  Those threads
+     * will, after making the callback, take out the lock again,
+     * decrememt progress_reports_outstanding, and call us again.
+     */
+
+    assert(ao->progress_reports_outstanding >= 0);
+
+    if (!ao->complete || ao->progress_reports_outstanding)
+        return;
+
     if (ao->poller) {
         assert(ao->in_initiator);
         if (!ao->constructing)
@@ -1316,34 +1347,6 @@ void libxl__ao_complete(libxl__egc *egc, libxl__ao *ao, 
int rc)
         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->constructing = 1;
-    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;
@@ -1401,6 +1404,69 @@ int libxl__ao_inprogress(libxl__ao *ao)
 }
 
 
+/* progress reporting */
+
+/* The application indicates a desire to ignore events by passing NULL
+ * for how.  But we want to copy *how.  So we have this dummy function
+ * whose address is stored in callback if the app passed how==NULL. */
+static void dummy_asyncprogress_callback_ignore
+  (libxl_ctx *ctx, libxl_event *ev, void *for_callback) { }
+
+void libxl__ao_progress_gethow(libxl_asyncprogress_how *in_state,
+                               const libxl_asyncprogress_how *from_app) {
+    if (from_app)
+        *in_state = *from_app;
+    else
+        in_state->callback = dummy_asyncprogress_callback_ignore;
+}
+
+void libxl__ao_progress_report(libxl__egc *egc, libxl__ao *ao,
+        const libxl_asyncprogress_how *how, libxl_event *ev)
+{
+    ev->for_user = how->for_event;
+    if (how->callback == dummy_asyncprogress_callback_ignore) {
+        /* ignore */
+    } else if (how->callback) {
+        libxl__aop_occurred *aop = libxl__zalloc(0, sizeof(*aop));
+        ao->progress_reports_outstanding++;
+        aop->ao = ao;
+        aop->ev = ev;
+        aop->how = how;
+    } else {
+        libxl__event_occurred(egc, ev);
+    }
+}
+
+
+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->constructing = 1;
+    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;
+}
+
+
 /*
  * Local variables:
  * mode: C
diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h
index 56c3eec..4e97f5e 100644
--- a/tools/libxl/libxl_internal.h
+++ b/tools/libxl/libxl_internal.h
@@ -118,6 +118,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__aop_occurred libxl__aop_occurred;
 
 void libxl__alloc_failed(libxl_ctx *, const char *func,
                          size_t nmemb, size_t size) __attribute__((noreturn));
@@ -372,6 +373,14 @@ struct libxl__egc {
     struct libxl__gc gc;
     struct libxl__event_list occurred_for_callback;
     LIBXL_TAILQ_HEAD(, libxl__ao) aos_for_callback;
+    LIBXL_TAILQ_HEAD(, libxl__aop_occurred) aops_for_callback;
+};
+
+struct libxl__aop_occurred {
+    LIBXL_TAILQ_ENTRY(libxl__aop_occurred) entry;
+    libxl__ao *ao;
+    libxl_event *ev;
+    const libxl_asyncprogress_how *how;
 };
 
 #define LIBXL__AO_MAGIC              0xA0FACE00ul
@@ -380,6 +389,7 @@ struct libxl__egc {
 struct libxl__ao {
     uint32_t magic;
     unsigned constructing:1, in_initiator:1, complete:1, notified:1;
+    int progress_reports_outstanding;
     int rc;
     libxl__gc gc;
     libxl_asyncop_how how;
@@ -1369,6 +1379,7 @@ libxl__device_model_version_running(libxl__gc *gc, 
uint32_t domid);
         LIBXL_INIT_GC((egc).gc,ctx);                    \
         LIBXL_TAILQ_INIT(&(egc).occurred_for_callback); \
         LIBXL_TAILQ_INIT(&(egc).aos_for_callback);      \
+        LIBXL_TAILQ_INIT(&(egc).aops_for_callback);     \
     } while(0)
 
 _hidden void libxl__egc_cleanup(libxl__egc *egc);
@@ -1415,6 +1426,9 @@ _hidden void libxl__egc_cleanup(libxl__egc *egc);
  *        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 the operation provides progress reports, the aop_how(s)
+ *        must be copied into the per-operation structure using
+ *        libxl__ao_progress_gethow.
  *
  * - If initiation is successful, the initiating function needs
  *   to run libxl__ao_inprogress right before unlocking and
@@ -1424,6 +1438,10 @@ _hidden void libxl__egc_cleanup(libxl__egc *egc);
  *   call libxl__ao_abort before unlocking and returning whatever
  *   error code is appropriate (AO_ABORT macro).
  *
+ * - If the operation supports progress reports, it may generate
+ *   suitable events with NEW_EVENT and report them with
+ *   libxl__ao_progress_report (with the ctx locked).
+ *
  * - 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
@@ -1479,8 +1497,18 @@ _hidden int libxl__ao_inprogress(libxl__ao *ao); /* 
temporarily unlocks */
 _hidden void libxl__ao_abort(libxl__ao *ao);
 _hidden void libxl__ao_complete(libxl__egc *egc, libxl__ao *ao, int rc);
 
+/* Can be called at any time.  Use is essential for any aop user. */
+_hidden void libxl__ao_progress_gethow(libxl_asyncprogress_how *in_state,
+                                       const libxl_asyncprogress_how 
*from_app);
+
+/* Must be called with the ctx locked.  Will fill in ev->for_user,
+ * so caller need not do that. */
+_hidden void libxl__ao_progress_report(libxl__egc *egc, libxl__ao *ao,
+   const libxl_asyncprogress_how *how, libxl_event *ev /* consumed */);
+
 /* For use by ao machinery ONLY */
 _hidden void libxl__ao__destroy(libxl_ctx*, libxl__ao *ao);
+_hidden void libxl__ao_complete_check_progress_reports(libxl__egc*, 
libxl__ao*);
 
 
 /*
-- 
1.7.2.5


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.