WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-devel

[Xen-devel] [PATCH RFC v2 02/13] libxenstore: Provide xs_check_watch

Event-driven programs want to wait until the xs_fileno triggers for
reading, and then repeatedly call xs_check_watch.

Also xs_read_watch exposes a useless "num" out parameter, which should
always (if things aren't going hideously wrong) be at least 2 and
which the caller shouldn't be interested in.  So xs_check_watch
doesn't have one of those.

Signed-off-by: Ian Jackson <ian.jackson@xxxxxxxxxxxxx>
---
 tools/xenstore/xs.c |   89 ++++++++++++++++++++++++++++++++++++++++++++-------
 tools/xenstore/xs.h |   16 +++++++++
 2 files changed, 93 insertions(+), 12 deletions(-)

diff --git a/tools/xenstore/xs.c b/tools/xenstore/xs.c
index c72ea83..96e4b7a 100644
--- a/tools/xenstore/xs.c
+++ b/tools/xenstore/xs.c
@@ -132,7 +132,20 @@ struct xs_handle {
 
 #endif
 
-static int read_message(struct xs_handle *h);
+static int read_message(struct xs_handle *h, int nonblocking);
+
+static void setnonblock(int fd, int nonblock) {
+       int flags = fcntl(fd, F_GETFL);
+       if (flags == -1)
+               return;
+
+       if (nonblock)
+               flags |= O_NONBLOCK;
+       else
+               flags &= ~O_NONBLOCK;
+
+       fcntl(fd, F_SETFL, flags);
+}
 
 int xs_fileno(struct xs_handle *h)
 {
@@ -327,8 +340,16 @@ void xs_close(struct xs_handle* xsh)
                xs_daemon_close(xsh);
 }
 
-static bool read_all(int fd, void *data, unsigned int len)
+static bool read_all(int fd, void *data, unsigned int len, int nonblocking)
+       /* With nonblocking, either reads either everything requested,
+        * or nothing. */
 {
+       if (!len)
+               return true;
+
+       if (nonblocking)
+               setnonblock(fd, 1);
+
        while (len) {
                int done;
 
@@ -336,18 +357,31 @@ static bool read_all(int fd, void *data, unsigned int len)
                if (done < 0) {
                        if (errno == EINTR)
                                continue;
-                       return false;
+                       goto out_false;
                }
                if (done == 0) {
                        /* It closed fd on us?  EBADF is appropriate. */
                        errno = EBADF;
-                       return false;
+                       goto out_false;
                }
                data += done;
                len -= done;
+
+               if (nonblocking) {
+                       setnonblock(fd, 0);
+                       nonblocking = 0;
+               }
        }
 
        return true;
+
+out_false:
+       if (nonblocking) {
+               int e = errno;
+               setnonblock(fd, 0);
+               errno = e;
+       }
+       return false;
 }
 
 #ifdef XSTEST
@@ -376,7 +410,7 @@ static void *read_reply(
        read_from_thread = read_thread_exists(h);
 
        /* Read from comms channel ourselves if there is no reader thread. */
-       if (!read_from_thread && (read_message(h) == -1))
+       if (!read_from_thread && (read_message(h, 0) == -1))
                return NULL;
 
        mutex_lock(&h->reply_mutex);
@@ -695,7 +729,8 @@ bool xs_watch(struct xs_handle *h, const char *path, const 
char *token)
  * Returns array of two pointers: path and token, or NULL.
  * Call free() after use.
  */
-char **xs_read_watch(struct xs_handle *h, unsigned int *num)
+static char **read_watch_internal(struct xs_handle *h, unsigned int *num,
+                                 int nonblocking)
 {
        struct xs_stored_msg *msg;
        char **ret, *strings, c = 0;
@@ -709,14 +744,20 @@ char **xs_read_watch(struct xs_handle *h, unsigned int 
*num)
         * we haven't called xs_watch.  Presumably the application
         * will do so later; in the meantime we just block.
         */
-       while (list_empty(&h->watch_list) && h->fd != -1)
+       while (list_empty(&h->watch_list) && h->fd != -1) {
+               if (nonblocking) {
+                       mutex_unlock(&h->watch_mutex);
+                       errno = EAGAIN;
+                       return 0;
+               }
                condvar_wait(&h->watch_condvar, &h->watch_mutex);
+       }
 #else /* !defined(USE_PTHREAD) */
        /* Read from comms channel ourselves if there are no threads
         * and therefore no reader thread. */
 
        assert(!read_thread_exists(h)); /* not threadsafe but worth a check */
-       if ((read_message(h) == -1))
+       if ((read_message(h, nonblocking) == -1))
                return NULL;
 
 #endif /* !defined(USE_PTHREAD) */
@@ -762,6 +803,24 @@ char **xs_read_watch(struct xs_handle *h, unsigned int 
*num)
        return ret;
 }
 
+char **xs_check_watch(struct xs_handle *h)
+{
+       unsigned int num;
+       char **ret;
+       ret = read_watch_internal(h, &num, 1);
+       if (ret) assert(num >= 2);
+       return ret;
+}
+
+/* Find out what node change was on (will block if nothing pending).
+ * Returns array of two pointers: path and token, or NULL.
+ * Call free() after use.
+ */
+char **xs_read_watch(struct xs_handle *h, unsigned int *num)
+{
+       return read_watch_internal(h, num, 0);
+}
+
 /* Remove a watch on a node.
  * Returns false on failure (no watch on that node).
  */
@@ -942,11 +1001,17 @@ char *xs_debug_command(struct xs_handle *h, const char 
*cmd,
                        ARRAY_SIZE(iov), NULL);
 }
 
-static int read_message(struct xs_handle *h)
+static int read_message(struct xs_handle *h, int nonblocking)
 {
        /* IMPORTANT: It is forbidden to call this function without
         * acquiring the request lock and checking that h->read_thr_exists
         * is false.  See "Lock discipline" in struct xs_handle, above. */
+
+       /* If nonblocking==1, this function will always read either
+        * nothing, returning -1 and setting errno==EAGAIN, or we read
+        * whole amount requested.  Ie as soon as we have the start of
+        * the message we block until we get all of it.
+        */
          
        struct xs_stored_msg *msg = NULL;
        char *body = NULL;
@@ -958,7 +1023,7 @@ static int read_message(struct xs_handle *h)
        if (msg == NULL)
                goto error;
        cleanup_push(free, msg);
-       if (!read_all(h->fd, &msg->hdr, sizeof(msg->hdr))) { /* Cancellation 
point */
+       if (!read_all(h->fd, &msg->hdr, sizeof(msg->hdr), nonblocking)) { /* 
Cancellation point */
                saved_errno = errno;
                goto error_freemsg;
        }
@@ -968,7 +1033,7 @@ static int read_message(struct xs_handle *h)
        if (body == NULL)
                goto error_freemsg;
        cleanup_push(free, body);
-       if (!read_all(h->fd, body, msg->hdr.len)) { /* Cancellation point */
+       if (!read_all(h->fd, body, msg->hdr.len, 0)) { /* Cancellation point */
                saved_errno = errno;
                goto error_freebody;
        }
@@ -1023,7 +1088,7 @@ static void *read_thread(void *arg)
        struct xs_handle *h = arg;
        int fd;
 
-       while (read_message(h) != -1)
+       while (read_message(h, 0) != -1)
                continue;
 
        /* An error return from read_message leaves the socket in an undefined
diff --git a/tools/xenstore/xs.h b/tools/xenstore/xs.h
index 1cbe255..63f535d 100644
--- a/tools/xenstore/xs.h
+++ b/tools/xenstore/xs.h
@@ -135,6 +135,22 @@ bool xs_watch(struct xs_handle *h, const char *path, const 
char *token);
 /* Return the FD to poll on to see if a watch has fired. */
 int xs_fileno(struct xs_handle *h);
 
+/* Check for node changes.  On success, returns a non-NULL pointer ret
+ * such that ret[0] and ret[1] are valid C strings, namely the
+ * triggering path (see docs/misc/xenstore.txt) and the token (from
+ * xs_watch).  On error return value is NULL setting errno.
+ * 
+ * Callers should, after xs_fileno has become readable, repeatedly
+ * call xs_check_watch until it returns NULL and sets errno to EAGAIN.
+ * (If the fd became readable, xs_check_watch is allowed to make it no
+ * longer show up as readable even if future calls to xs_check_watch
+ * will return more watch events.)
+ *
+ * After the caller is finished with the returned information it
+ * should be freed all in one go with free(ret).
+ */
+char **xs_check_watch(struct xs_handle *h);
+
 /* Find out what node change was on (will block if nothing pending).
  * Returns array containing the path and token. Use XS_WATCH_* to access these
  * elements. Call free() after use.
-- 
1.7.2.5


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel