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

Re: [Xen-tools] b0893b876c8c4c4eb507d48fc1c4af4268ddecde and watch behaviour



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


 


Rackspace

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