-void io_set_alloc(void *(*allocfn)(size_t size),
- void *(*reallocfn)(void *ptr, size_t size),
- void (*freefn)(void *ptr))
+struct io_plan *io_duplex(struct io_conn *conn,
+ struct io_plan *in_plan, struct io_plan *out_plan)
+{
+ assert(conn == container_of(in_plan, struct io_conn, plan[IO_IN]));
+ /* in_plan must be conn->plan[IO_IN], out_plan must be [IO_OUT] */
+ assert(out_plan == in_plan + 1);
+ return in_plan;
+}
+
+struct io_plan *io_halfclose(struct io_conn *conn)
+{
+ /* Both unset? OK. */
+ if (conn->plan[IO_IN].status == IO_UNSET
+ && conn->plan[IO_OUT].status == IO_UNSET)
+ return io_close(conn);
+
+ /* We leave this unset then. */
+ if (conn->plan[IO_IN].status == IO_UNSET)
+ return &conn->plan[IO_IN];
+ else
+ return &conn->plan[IO_OUT];
+}
+
+struct io_plan *io_set_plan(struct io_conn *conn, enum io_direction dir,
+ int (*io)(int fd, struct io_plan_arg *arg),
+ struct io_plan *(*next)(struct io_conn *, void *),
+ void *next_arg)
+{
+ struct io_plan *plan = &conn->plan[dir];
+
+ plan->io = io;
+ plan->next = next;
+ plan->next_arg = next_arg;
+ assert(next != NULL);
+
+ return plan;
+}
+
+bool io_plan_in_started(const struct io_conn *conn)
+{
+ return conn->plan[IO_IN].status == IO_POLLING_STARTED;
+}
+
+bool io_plan_out_started(const struct io_conn *conn)
+{
+ return conn->plan[IO_OUT].status == IO_POLLING_STARTED;
+}
+
+bool io_flush_sync(struct io_conn *conn)