+ 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);
+}
+
+/* Returns false if this has been freed. */
+static bool do_plan(struct io_conn *conn, struct io_plan *plan)
+{
+ /* We shouldn't have polled for this event if this wasn't true! */
+ assert(plan->status == IO_POLLING);
+
+ switch (plan->io(conn->fd.fd, &plan->arg)) {
+ case -1:
+ io_close(conn);
+ return false;
+ case 0:
+ return true;
+ case 1:
+ return next_plan(conn, plan);
+ default:
+ /* IO should only return -1, 0 or 1 */
+ abort();
+ }
+}
+
+void io_ready(struct io_conn *conn, int pollflags)
+{
+ if (pollflags & POLLIN)
+ if (!do_plan(conn, &conn->plan[IO_IN]))
+ return;
+
+ if (pollflags & POLLOUT)
+ do_plan(conn, &conn->plan[IO_OUT]);