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)
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;
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);
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.
*
* 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];
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.
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)
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)