ccan/io: add io_out_wait() and io_out_always().
authorRusty Russell <rusty@rustcorp.com.au>
Mon, 4 Aug 2014 08:17:21 +0000 (17:47 +0930)
committerRusty Russell <rusty@rustcorp.com.au>
Mon, 4 Aug 2014 08:17:21 +0000 (17:47 +0930)
This specificity is required, for example, when doing:

return io_duplex(conn, io_read(...), io_always(...));

The workaround suggested doesn't work, because io_duplex_prepare()
asserts() if the plans aren't UNSET to start.  And pettycoin needs this.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ccan/io/io.c
ccan/io/io.h
ccan/io/test/run-12-bidir.c

index 7f5113d344c4b5ba199121593c2d5903e236dfd9..c0af6bf7e4d66ec761307b49931dcca85f64e3a8 100644 (file)
@@ -125,16 +125,28 @@ static struct io_plan *set_always(struct io_conn *conn,
        return io_set_plan(conn, dir, NULL, next, arg);
 }
 
+static struct io_plan *io_always_dir(struct io_conn *conn,
+                                    enum io_direction dir,
+                                    struct io_plan *(*next)(struct io_conn *,
+                                                            void *),
+                                    void *arg)
+{
+       return set_always(conn, dir, next, arg);
+}
+
 struct io_plan *io_always_(struct io_conn *conn,
                           struct io_plan *(*next)(struct io_conn *, void *),
                           void *arg)
 {
-       /* If we're duplex, we want this on the current plan.  Otherwise,
-        * doesn't matter. */
-       if (conn->plan[IO_IN].status == IO_UNSET)
-               return set_always(conn, IO_IN, next, arg);
-       else
-               return set_always(conn, IO_OUT, next, arg);
+       return io_always_dir(conn, IO_IN, next, arg);
+}
+
+struct io_plan *io_out_always_(struct io_conn *conn,
+                              struct io_plan *(*next)(struct io_conn *,
+                                                      void *),
+                              void *arg)
+{
+       return io_always_dir(conn, IO_OUT, next, arg);
 }
 
 static int do_write(int fd, struct io_plan_arg *arg)
@@ -294,22 +306,14 @@ struct io_plan *io_connect_(struct io_conn *conn, const struct addrinfo *addr,
        return io_set_plan(conn, IO_IN, do_connect, next, next_arg);
 }
 
-struct io_plan *io_wait_(struct io_conn *conn,
-                        const void *wait,
-                        struct io_plan *(*next)(struct io_conn *, void *),
-                        void *next_arg)
+static struct io_plan *io_wait_dir(struct io_conn *conn,
+                                  const void *wait,
+                                  enum io_direction dir,
+                                  struct io_plan *(*next)(struct io_conn *,
+                                                          void *),
+                                  void *next_arg)
 {
-       enum io_direction dir;
-       struct io_plan_arg *arg;
-
-       /* If we're duplex, we want this on the current plan.  Otherwise,
-        * doesn't matter. */
-       if (conn->plan[IO_IN].status == IO_UNSET)
-               dir = IO_IN;
-       else
-               dir = IO_OUT;
-
-       arg = io_plan_arg(conn, dir);
+       struct io_plan_arg *arg = io_plan_arg(conn, dir);
        arg->u1.const_vp = wait;
 
        conn->plan[dir].status = IO_WAITING;
@@ -317,6 +321,22 @@ struct io_plan *io_wait_(struct io_conn *conn,
        return io_set_plan(conn, dir, NULL, next, next_arg);
 }
 
+struct io_plan *io_wait_(struct io_conn *conn,
+                        const void *wait,
+                        struct io_plan *(*next)(struct io_conn *, void *),
+                        void *next_arg)
+{
+       return io_wait_dir(conn, wait, IO_IN, next, next_arg);
+}
+
+struct io_plan *io_out_wait_(struct io_conn *conn,
+                            const void *wait,
+                            struct io_plan *(*next)(struct io_conn *, void *),
+                            void *next_arg)
+{
+       return io_wait_dir(conn, wait, IO_OUT, next, next_arg);
+}
+
 void io_wake(const void *wait)
 {
        backend_wake(wait);
index d0c0a39a3888526df10c4174ed4dd8e69b8e7153..1cf9d609973066c04c696f3412fd735ca241bf5d 100644 (file)
@@ -362,6 +362,27 @@ struct io_plan *io_always_(struct io_conn *conn,
                           struct io_plan *(*next)(struct io_conn *, void *),
                           void *arg);
 
+/**
+ * io_out_always - output plan to immediately call next callback
+ * @conn: the connection that plan is for.
+ * @next: function to call.
+ * @arg: @next argument
+ *
+ * This is a variant of io_always() which uses the output plan; it only
+ * matters if you are using io_duplex, and thus have two plans running at
+ * once.
+ */
+#define io_out_always(conn, next, arg)                                 \
+       io_out_always_((conn), typesafe_cb_preargs(struct io_plan *, void *, \
+                                                  (next), (arg),       \
+                                                  struct io_conn *),   \
+                      (arg))
+
+struct io_plan *io_out_always_(struct io_conn *conn,
+                              struct io_plan *(*next)(struct io_conn *,
+                                                      void *),
+                              void *arg);
+
 /**
  * io_connect - create an asynchronous connection to a listening socket.
  * @conn: the connection that plan is for.
@@ -418,12 +439,6 @@ struct io_plan *io_connect_(struct io_conn *conn, const struct addrinfo *addr,
  *
  * Note that if either plan closes the connection, it will be closed.
  *
- * Note that if one plan is io_wait or io_always, that causes a problem:
- * they look at the input and output plan slots to figure out which to
- * use, but if the other plan hasn't been evaluated yet, that will fail.
- * In this case, you'll need to ensure the other plan is evaluated first,
- * eg. "struct io_plan *r = io_read(...); return io_duplex(r, io_always(...))"
- *
  * Example:
  * struct buf {
  *     char in[100];
@@ -501,6 +516,30 @@ struct io_plan *io_wait_(struct io_conn *conn,
                         void *arg);
 
 
+/**
+ * io_out_wait - leave the output plan idle until something wakes us.
+ * @conn: the connection that plan is for.
+ * @waitaddr: the address to wait on.
+ * @next: function to call after waiting.
+ * @arg: @next argument
+ *
+ * io_wait() makes the input plan idle: if you're not using io_duplex it
+ * doesn't matter which plan is waiting.  Otherwise, you may need to use
+ * io_out_wait() instead, to specify explicitly that the output plan is
+ * waiting.
+ */
+#define io_out_wait(conn, waitaddr, next, arg)                         \
+       io_out_wait_((conn), (waitaddr),                                \
+                    typesafe_cb_preargs(struct io_plan *, void *,      \
+                                        (next), (arg),                 \
+                                        struct io_conn *),             \
+                    (arg))
+
+struct io_plan *io_out_wait_(struct io_conn *conn,
+                            const void *wait,
+                            struct io_plan *(*next)(struct io_conn *, void *),
+                            void *arg);
+
 /**
  * io_wake - wake up any connections waiting on @wait
  * @waitaddr: the address to trigger.
index 10f84295cb8650e6c8566289b9467fd9608561a6..e79acb56db1ce2d68d605b3419e6aa813c4b938c 100644 (file)
@@ -24,10 +24,20 @@ static void finish_ok(struct io_conn *conn, struct data *d)
        d->state++;
 }
 
-static struct io_plan *rw_done(struct io_conn *conn, struct data *d)
+static struct io_plan *r_done(struct io_conn *conn, struct data *d)
 {
        d->state++;
-       return io_halfclose(conn);
+       if (d->state == 3)
+               return io_close(conn);
+       return io_wait(conn, NULL, io_never, NULL);
+}
+
+static struct io_plan *w_done(struct io_conn *conn, struct data *d)
+{
+       d->state++;
+       if (d->state == 3)
+               return io_close(conn);
+       return io_out_wait(conn, NULL, io_never, NULL);
 }
 
 static struct io_plan *init_conn(struct io_conn *conn, struct data *d)
@@ -44,8 +54,8 @@ static struct io_plan *init_conn(struct io_conn *conn, struct data *d)
        io_set_finish(conn, finish_ok, d);
 
        return io_duplex(conn,
-                        io_read(conn, d->buf, sizeof(d->buf), rw_done, d),
-                        io_write(conn, d->wbuf, sizeof(d->wbuf), rw_done, d));
+                        io_read(conn, d->buf, sizeof(d->buf), r_done, d),
+                        io_write(conn, d->wbuf, sizeof(d->wbuf), w_done, d));
 }
 
 static int make_listen_fd(const char *port, struct addrinfo **info)