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

[Xen-devel] [PATCH 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..5d0b567 100644
--- a/tools/xenstore/xs.c
+++ b/tools/xenstore/xs.c
@@ -132,7 +132,23 @@ 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 esave = errno;
+       int flags = fcntl(fd, F_GETFL);
+       if (flags == -1)
+               goto out;
+
+       if (nonblock)
+               flags |= O_NONBLOCK;
+       else
+               flags &= ~O_NONBLOCK;
+
+       fcntl(fd, F_SETFL, flags);
+out:
+       errno = esave;
+}
 
 int xs_fileno(struct xs_handle *h)
 {
@@ -327,8 +343,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 +360,28 @@ 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)
+               setnonblock(fd, 0);
+       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


 


Rackspace

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