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-tools

Re: [Xen-tools] b0893b876c8c4c4eb507d48fc1c4af4268ddecde and watch behav

To: Christian Limpach <Christian.Limpach@xxxxxxxxxxxx>
Subject: Re: [Xen-tools] b0893b876c8c4c4eb507d48fc1c4af4268ddecde and watch behaviour
From: Rusty Russell <rusty@xxxxxxxxxxxxxxx>
Date: Thu, 22 Sep 2005 17:34:58 +1000
Cc: Xen Tools <xen-tools@xxxxxxxxxxxxxxxxxxx>
Delivery-date: Thu, 22 Sep 2005 07:32:34 +0000
Envelope-to: www-data@xxxxxxxxxxxxxxxxxxx
In-reply-to: <1127370955.7567.57.camel@xxxxxxxxxxxxxxxxxxxxx>
List-help: <mailto:xen-tools-request@lists.xensource.com?subject=help>
List-id: Xen control tools developers <xen-tools.lists.xensource.com>
List-post: <mailto:xen-tools@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-tools>, <mailto:xen-tools-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-tools>, <mailto:xen-tools-request@lists.xensource.com?subject=unsubscribe>
References: <1127370955.7567.57.camel@xxxxxxxxxxxxxxxxxxxxx>
Sender: xen-tools-bounces@xxxxxxxxxxxxxxxxxxx
On Thu, 2005-09-22 at 16:35 +1000, Rusty Russell wrote:
> Hi Christian!
> 
>       I was looking through b0893b876c8c4c4eb507d48fc1c4af4268ddecde, and it
> took me a while to figure out what the code was doing.  I think there
> are two issues here:
> 
> 1) Directories implicitly created by mkdir/write don't fire watches (eg.
> mkdir /a/sub/dir only fires a watch event for /a/sub/dir, even if it
> also created /a/sub), and
> 2) Rm only fires a single event for a watch, as an optimization.
> 
> The first one is a bug, I think, and I'm testing a patch as I type this.

Indeed, here is the patch.  Applies on top of those 3 tdb-conversion
patches...
Rusty.

# HG changeset patch
# User Rusty Russell <rusty@xxxxxxxxxxxxxxx>
# Node ID 5ea1c70f0adb9625a5640b61c9068923a95cb8d7
# Parent  6ec88332e05dd2433ca568de619b3e809f66ca41
Fire watches on implicitly-created directories.
This patch separates watch event creation and firing, which improves robustness 
a little should we run out of memory, as well as allowing us to build up a 
series of different events (such as for each directory created in a write/mkdir 
which creates more than one), and fire them on successful completion.

Signed-off-by: Rusty Russell <rusty@xxxxxxxxxxxxxxx>

diff -r 6ec88332e05d -r 5ea1c70f0adb tools/xenstore/testsuite/07watch.test
--- a/tools/xenstore/testsuite/07watch.test     Wed Sep 21 05:44:48 2005
+++ b/tools/xenstore/testsuite/07watch.test     Thu Sep 22 06:42:54 2005
@@ -184,6 +184,7 @@
 expect 1:/test/subnode:token
 1 waitwatch
 1 ackwatch token
+1 close
 
 # Watch should not double-send after we ack, even if we did something in 
between.
 1 watch /test2 token
@@ -195,3 +196,32 @@
 1 ackwatch token
 expect 1: waitwatch failed: Connection timed out
 1 waitwatch
+1 close
+
+# Watch fires on implicitly-created directories (mkdir)
+1 watch /test2 token
+2 mkdir /test2/sub/dir/dir2
+expect 1:/test2/sub:token
+1 waitwatch
+1 ackwatch token
+expect 1:/test2/sub/dir:token
+1 waitwatch
+1 ackwatch token
+expect 1:/test2/sub/dir/dir2:token
+1 waitwatch
+1 ackwatch token
+1 close
+
+# Watch fires on implicitly-created directories (write)
+1 watch /test2 token
+2 write /test2/sub2/dir/entry contents
+expect 1:/test2/sub2:token
+1 waitwatch
+1 ackwatch token
+expect 1:/test2/sub2/dir:token
+1 waitwatch
+1 ackwatch token
+expect 1:/test2/sub2/dir/entry:token
+1 waitwatch
+1 ackwatch token
+1 close
diff -r 6ec88332e05d -r 5ea1c70f0adb tools/xenstore/xenstored_core.c
--- a/tools/xenstore/xenstored_core.c   Wed Sep 21 05:44:48 2005
+++ b/tools/xenstore/xenstored_core.c   Thu Sep 22 06:42:54 2005
@@ -747,7 +747,8 @@
        return strrchr(name, '/') + 1;
 }
 
-static struct node *construct_node(struct connection *conn, const char *name)
+static struct node *construct_node(struct connection *conn, const char *name,
+                                  struct events **ev)
 {
        const char *base;
        unsigned int baselen;
@@ -757,7 +758,7 @@
        /* If parent doesn't exist, create it. */
        parent = read_node(conn, parentname);
        if (!parent)
-               parent = construct_node(conn, parentname);
+               parent = construct_node(conn, parentname, ev);
        if (!parent)
                return NULL;
        
@@ -774,6 +775,7 @@
        node = talloc(name, struct node);
        node->tdb = tdb_context(conn);
        node->name = talloc_strdup(node, name);
+       *ev = add_events(conn, name, false, *ev);
 
        /* Inherit permissions, except domains own what they create */
        node->num_perms = parent->num_perms;
@@ -808,11 +810,12 @@
  * This helps fsck if we die during this. */
 static struct node *create_node(struct connection *conn, 
                                const char *name,
-                               void *data, unsigned int datalen)
+                               void *data, unsigned int datalen,
+                               struct events **ev)
 {
        struct node *node, *i;
 
-       node = construct_node(conn, name);
+       node = construct_node(conn, name, ev);
        if (!node)
                return NULL;
 
@@ -838,6 +841,7 @@
 {
        unsigned int offset, datalen;
        struct node *node;
+       struct events *ev = NULL;
        char *vec[1] = { NULL }; /* gcc4 + -W + -Werror fucks code. */
        char *name;
 
@@ -858,7 +862,8 @@
                        send_error(conn, errno);
                        return;
                }
-               node = create_node(conn, name, in->buffer + offset, datalen);
+               node = create_node(conn, name, in->buffer + offset, datalen,
+                                  &ev);
                if (!node) {
                        send_error(conn, errno);
                        return;
@@ -866,6 +871,7 @@
        } else {
                node->data = in->buffer + offset;
                node->datalen = datalen;
+               ev = add_events(conn, name, false, NULL);
                if (!write_node(conn, node)){
                        send_error(conn, errno);
                        return;
@@ -873,13 +879,14 @@
        }
 
        add_change_node(conn->transaction, name, false);
-       fire_watches(conn, name, false);
+       fire_events(ev);
        send_ack(conn, XS_WRITE);
 }
 
 static void do_mkdir(struct connection *conn, const char *name)
 {
        struct node *node;
+       struct events *ev = NULL;
 
        name = canonicalize(conn, name);
        node = get_node(conn, name, XS_PERM_WRITE);
@@ -891,13 +898,13 @@
                        send_error(conn, errno);
                        return;
                }
-               node = create_node(conn, name, NULL, 0);
+               node = create_node(conn, name, NULL, 0, &ev);
                if (!node) {
                        send_error(conn, errno);
                        return;
                }
                add_change_node(conn->transaction, name, false);
-               fire_watches(conn, name, false);
+               fire_events(ev);
        }
        send_ack(conn, XS_MKDIR);
 }
@@ -948,6 +955,7 @@
 static void do_rm(struct connection *conn, const char *name)
 {
        struct node *node, *parent;
+       struct events *ev = NULL;
 
        name = canonicalize(conn, name);
        node = get_node(conn, name, XS_PERM_WRITE);
@@ -978,6 +986,7 @@
                return;
        }
 
+       ev = add_events(conn, name, true, NULL);
        if (!delete_child(conn, parent, basename(name))) {
                send_error(conn, EINVAL);
                return;
@@ -985,7 +994,7 @@
 
        delete_node(conn, node);
        add_change_node(conn->transaction, name, true);
-       fire_watches(conn, name, true);
+       fire_events(ev);
        send_ack(conn, XS_RM);
 }
 
@@ -1014,6 +1023,7 @@
        unsigned int num;
        char *name, *permstr;
        struct node *node;
+       struct events *ev;
 
        num = xs_count_strings(in->buffer, in->used);
        if (num < 2) {
@@ -1033,6 +1043,7 @@
                return;
        }
 
+       ev = add_events(conn, name, false, NULL);
        node->perms = talloc_array(node, struct xs_permissions, num);
        node->num_perms = num;
        if (!xs_strings_to_perms(node->perms, num, permstr)) {
@@ -1045,7 +1056,7 @@
        }
 
        add_change_node(conn->transaction, name, false);
-       fire_watches(conn, name, false);
+       fire_events(ev);
        send_ack(conn, XS_SET_PERMS);
 }
 
diff -r 6ec88332e05d -r 5ea1c70f0adb tools/xenstore/xenstored_domain.c
--- a/tools/xenstore/xenstored_domain.c Wed Sep 21 05:44:48 2005
+++ b/tools/xenstore/xenstored_domain.c Thu Sep 22 06:42:54 2005
@@ -235,7 +235,7 @@
        }
 
        if (released)
-               fire_watches(NULL, "@releaseDomain", false);
+               special_event("@releaseDomain");
 }
 
 /* We scan all domains rather than use the information given here. */
@@ -330,8 +330,7 @@
        /* Now domain belongs to its connection. */
        talloc_steal(domain->conn, domain);
 
-       fire_watches(conn, "@introduceDomain", false);
-
+       special_event("@introduceDomain");
        send_ack(conn, XS_INTRODUCE);
 }
 
@@ -378,10 +377,9 @@
                send_error(conn, EINVAL);
                return;
        }
-
        talloc_free(domain->conn);
 
-       fire_watches(conn, "@releaseDomain", false);
+       special_event("@releaseDomain");
 
        send_ack(conn, XS_RELEASE);
 }
diff -r 6ec88332e05d -r 5ea1c70f0adb tools/xenstore/xenstored_transaction.c
--- a/tools/xenstore/xenstored_transaction.c    Wed Sep 21 05:44:48 2005
+++ b/tools/xenstore/xenstored_transaction.c    Thu Sep 22 06:42:54 2005
@@ -161,11 +161,18 @@
        talloc_steal(arg, trans);
 
        if (streq(arg, "T")) {
+               struct events *ev = NULL;
+
                /* FIXME: Merge, rather failing on any change. */
                if (trans->generation != generation) {
                        send_error(conn, EAGAIN);
                        return;
                }
+
+               /* Allocate watch events to send *before* commit. */
+               list_for_each_entry(i, &trans->changes, list)
+                       ev = add_events(conn, i->node, i->recurse, ev);
+
                if (!replace_tdb(trans->tdb_name, trans->tdb)) {
                        send_error(conn, errno);
                        return;
@@ -173,9 +180,7 @@
                /* Don't close this: we won! */
                trans->tdb = NULL;
 
-               /* Fire off the watches for everything that changed. */
-               list_for_each_entry(i, &trans->changes, list)
-                       fire_watches(conn, i->node, i->recurse);
+               fire_events(ev);
                generation++;
        }
        send_ack(conn, XS_TRANSACTION_END);
diff -r 6ec88332e05d -r 5ea1c70f0adb tools/xenstore/xenstored_watch.c
--- a/tools/xenstore/xenstored_watch.c  Wed Sep 21 05:44:48 2005
+++ b/tools/xenstore/xenstored_watch.c  Thu Sep 22 06:42:54 2005
@@ -38,6 +38,8 @@
        /* The events on this watch. */
        struct list_head list;
 
+       struct watch *watch;
+
        /* Data to send (node\0token\0). */
        unsigned int len;
        char *data;
@@ -54,8 +56,17 @@
        /* Is this relative to connnection's implicit path? */
        const char *relative_path;
 
+       /* Connection which placed this watch. */
+       struct connection *conn;
+
        char *token;
        char *node;
+};
+
+struct events
+{
+       unsigned int num;
+       struct watch_event **events;
 };
 
 /* Look through our watches: if any of them have an event, queue it. */
@@ -91,11 +102,13 @@
 {
        struct watch_event *event = _event;
 
+       list_del(&event->list);
        trace_destroy(event, "watch_event");
        return 0;
 }
 
-static void add_event(struct connection *conn,
+static void add_event(struct events *ev,
+                     struct connection *conn,
                      struct watch *watch,
                      const char *name)
 {
@@ -116,7 +129,10 @@
                        name++;
        }
 
-       event = talloc(watch, struct watch_event);
+       ev->events = talloc_realloc(ev, ev->events, struct watch_event *,
+                                   ev->num + 1);
+       ev->events[ev->num++] = event = talloc(ev, struct watch_event);
+       event->watch = watch;
        event->len = strlen(name) + 1 + strlen(watch->token) + 1;
        event->data = talloc_array(event, char, event->len);
        strcpy(event->data, name);
@@ -126,30 +142,38 @@
        trace_create(event, "watch_event");
 }
 
-/* FIXME: we fail to fire on out of memory.  Should drop connections. */
-void fire_watches(struct connection *conn, const char *name, bool recurse)
+static struct events *new_events(const void *ctx)
+{
+       struct events *ev = talloc(ctx, struct events);
+       ev->num = 0;
+       ev->events = NULL;
+       return ev;
+}
+
+struct events *add_events(struct connection *conn,
+                         const char *name,
+                         bool recurse,
+                         struct events *old)
 {
        struct connection *i;
        struct watch *watch;
 
        /* During transactions, don't fire watches. */
-       if (conn && conn->transaction)
-               return;
-
-       /* Create an event for each watch. */
+       if (conn->transaction)
+               return NULL;
+
+       if (!old)
+               old = new_events(name);
+
        list_for_each_entry(i, &connections, list) {
                list_for_each_entry(watch, &i->watches, list) {
                        if (is_child(name, watch->node))
-                               add_event(i, watch, name);
+                               add_event(old, i, watch, name);
                        else if (recurse && is_child(watch->node, name))
-                               add_event(i, watch, watch->node);
-                       else
-                               continue;
-                       /* If connection not doing anything, queue this. */
-                       if (i->state == OK)
-                               queue_next_event(i);
-               }
-       }
+                               add_event(old, i, watch, watch->node);
+               }
+       }
+       return old;
 }
 
 static int destroy_watch(void *_watch)
@@ -158,11 +182,40 @@
        return 0;
 }
 
+/* Create a single event for this watch. */
+static struct events *single_event(void *ctx,
+                                  struct connection *conn,
+                                  struct watch *watch)
+{
+       struct events *ev = new_events(ctx);
+
+       add_event(ev, conn, watch, watch->node);
+       return ev;
+}
+
+/* No convenient context to hang this off, so free immediately. */
+void special_event(const char *eventname)
+{
+       struct connection *i;
+       struct watch *watch;
+       struct events *ev = new_events(talloc_autofree_context());
+
+       list_for_each_entry(i, &connections, list) {
+               list_for_each_entry(watch, &i->watches, list) {
+                       if (streq(eventname, watch->node))
+                               add_event(ev, i, watch, watch->node);
+               }
+       }
+       fire_events(ev);
+       talloc_free(ev);
+}
+
 void do_watch(struct connection *conn, struct buffered_data *in)
 {
        struct watch *watch;
        char *vec[2];
        bool relative;
+       struct events *ev;
 
        if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec)) {
                send_error(conn, EINVAL);
@@ -184,6 +237,7 @@
        watch = talloc(conn, struct watch);
        watch->node = talloc_strdup(watch, vec[0]);
        watch->token = talloc_strdup(watch, vec[1]);
+       watch->conn = conn;
        if (relative)
                watch->relative_path = get_implicit_path(conn);
        else
@@ -194,16 +248,30 @@
        list_add_tail(&watch->list, &conn->watches);
        trace_create(watch, "watch");
        talloc_set_destructor(watch, destroy_watch);
+       /* We fire once up front: simplifies clients and restart. */
+       ev = single_event(in, conn, watch);
        send_ack(conn, XS_WATCH);
-
-       /* We fire once up front: simplifies clients and restart. */
-       add_event(conn, watch, watch->node);
+       fire_events(ev);
+}
+
+void fire_events(struct events *ev)
+{
+       unsigned int i;
+
+       if (!ev)
+               return;
+
+       /* Reparent each event to the corresponding watch. */
+       for (i = 0; i < ev->num; i++) {
+               talloc_steal(ev->events[i]->watch, ev->events[i]);
+               /* If connection not doing anything, queue this. */
+               if (ev->events[i]->watch->conn->state == OK)
+                       queue_next_event(ev->events[i]->watch->conn);
+       }
 }
 
 void do_watch_ack(struct connection *conn, const char *token)
 {
-       struct watch_event *event;
-
        if (!token) {
                send_error(conn, EINVAL);
                return;
@@ -222,10 +290,8 @@
        }
 
        /* Remove event: after ack sent, core will call queue_next_event */
-       event = list_top(&conn->waiting_for_ack->events, struct watch_event,
-                        list);
-       list_del(&event->list);
-       talloc_free(event);
+       talloc_free(list_top(&conn->waiting_for_ack->events,
+                            struct watch_event, list));
 
        conn->waiting_for_ack = NULL;
        send_ack(conn, XS_WATCH_ACK);
diff -r 6ec88332e05d -r 5ea1c70f0adb tools/xenstore/xenstored_watch.h
--- a/tools/xenstore/xenstored_watch.h  Wed Sep 21 05:44:48 2005
+++ b/tools/xenstore/xenstored_watch.h  Thu Sep 22 06:42:54 2005
@@ -32,9 +32,20 @@
 /* Look through our watches: if any of them have an event, queue it. */
 void queue_next_event(struct connection *conn);
 
-/* Fire all watches: recurse means all the children are affected (ie. rm).
- */
-void fire_watches(struct connection *conn, const char *name, bool recurse);
+struct events;
+
+/* Create watch events: recurse means all the children are effected (ie. rm).
+ * Add to old events, if that is non-NULL. */
+struct events *add_events(struct connection *conn,
+                         const char *name,
+                         bool recurse,
+                         struct events *old);
+
+/* Actually fire those watch events */
+void fire_events(struct events *events);
+
+/* Fire a special event (introduce or release). */
+void special_event(const char *eventname);
 
 void dump_watches(struct connection *conn);
 

-- 
A bad analogy is like a leaky screwdriver -- Richard Braakman


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

<Prev in Thread] Current Thread [Next in Thread>