#include <stdbool.h>
#include <unistd.h>
-enum io_direction {
- IO_IN,
- IO_OUT
-};
+struct timers;
+struct timer;
+struct list_head;
/**
* struct io_plan - a plan for input or output.
* ...
* struct io_listener *l = do_listen("8111");
* if (l) {
- * io_loop();
+ * io_loop(NULL, NULL);
* io_close_listener(l);
* }
*/
/**
* io_always - plan to immediately call next callback
* @conn: the connection that plan is for.
- * @dir: IO_IN or IO_OUT
* @next: function to call.
* @arg: @next argument
*
* void *unused)
* {
* // Silly example: close on next time around loop.
- * return io_always(conn, IO_IN, io_close_cb, NULL);
+ * return io_always(conn, io_close_cb, NULL);
* }
*/
-#define io_always(conn, dir, next, arg) \
- io_always_((conn), dir, typesafe_cb_preargs(struct io_plan *, void *, \
- (next), (arg), \
- struct io_conn *), \
+#define io_always(conn, next, arg) \
+ io_always_((conn), typesafe_cb_preargs(struct io_plan *, void *, \
+ (next), (arg), \
+ struct io_conn *), \
(arg))
-struct io_plan *io_always_(struct io_conn *conn, enum io_direction dir,
+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.
struct io_plan *(*next)(struct io_conn *, void *),
void *arg);
+/**
+ * io_duplex - set plans for both input and output.
+ * @conn: the connection that plan is for.
+ * @in: the input plan
+ * @out: the output plan
+ *
+ * Most plans are either for input or output; io_duplex creates a plan
+ * which does both. This is often used in the init function to create
+ * two independent streams, though it can be used once on any connection.
+ *
+ * Note that if either plan closes the connection, it will be closed.
+ *
+ * Example:
+ * struct buf {
+ * char in[100];
+ * char out[100];
+ * };
+ *
+ * static struct io_plan *read_and_write(struct io_conn *conn, struct buf *b)
+ * {
+ * return io_duplex(conn,
+ * io_read(conn, b->in, sizeof(b->in), io_close_cb, b),
+ * io_write(conn, b->out, sizeof(b->out), io_close_cb,b));
+ * }
+ */
+#define io_duplex(conn, in_plan, out_plan) \
+ (io_duplex_prepare(conn), io_duplex_(in_plan, out_plan))
+
+struct io_plan *io_duplex_(struct io_plan *in_plan, struct io_plan *out_plan);
+void io_duplex_prepare(struct io_conn *conn);
+
+/**
+ * io_halfclose - close half of an io_duplex connection.
+ * @conn: the connection that plan is for.
+ *
+ * It's common to want to close a duplex connection after both input and
+ * output plans have completed. If either calls io_close() the connection
+ * closes immediately. Instead, io_halfclose() needs to be called twice.
+ *
+ * Example:
+ * struct buf {
+ * char in[100];
+ * char out[100];
+ * };
+ *
+ * static struct io_plan *finish(struct io_conn *conn, struct buf *b)
+ * {
+ * return io_halfclose(conn);
+ * }
+ *
+ * static struct io_plan *read_and_write(struct io_conn *conn, struct buf *b)
+ * {
+ * return io_duplex(conn,
+ * io_read(conn, b->in, sizeof(b->in), finish, b),
+ * io_write(conn, b->out, sizeof(b->out), finish, b));
+ * }
+ */
+struct io_plan *io_halfclose(struct io_conn *conn);
+
/**
* io_wait - leave a plan idle until something wakes us.
* @conn: the connection that plan is for.
* @waitaddr: the address to wait on.
- * @dir: IO_IN or IO_OUT
* @next: function to call after waiting.
* @arg: @next argument
*
* // Silly example to wait then close.
* static struct io_plan *wait(struct io_conn *conn, void *b)
* {
- * return io_wait(conn, b, IO_IN, io_close_cb, NULL);
+ * return io_wait(conn, b, io_close_cb, NULL);
* }
*/
-#define io_wait(conn, waitaddr, dir, next, arg) \
- io_wait_((conn), (waitaddr), (dir), \
+#define io_wait(conn, waitaddr, next, arg) \
+ io_wait_((conn), (waitaddr), \
typesafe_cb_preargs(struct io_plan *, void *, \
(next), (arg), \
struct io_conn *), \
(arg))
struct io_plan *io_wait_(struct io_conn *conn,
- const void *wait, enum io_direction dir,
+ const void *wait,
struct io_plan *(*next)(struct io_conn *, void *),
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.
/**
* io_never - assert if callback is called.
* @conn: the connection that plan is for.
+ * @unused: an unused parameter to make this suitable for use as a callback.
*
* Sometimes you want to make it clear that a callback should never happen
* (eg. for io_break). This will assert() if called.
* {
* io_break(conn);
* // We won't ever return from io_break
- * return io_never(conn);
+ * return io_never(conn, NULL);
* }
*/
-struct io_plan *io_never(struct io_conn *conn);
+struct io_plan *io_never(struct io_conn *conn, void *unused);
/* FIXME: io_recvfrom/io_sendto */
/**
* io_loop - process fds until all closed on io_break.
+ * @timers - timers which are waiting to go off (or NULL for none)
+ * @expired - an expired timer (can be NULL if @timers is)
*
* This is the core loop; it exits with the io_break() arg, or NULL if
- * all connections and listeners are closed.
+ * all connections and listeners are closed, or with @expired set to an
+ * expired timer (if @timers isn't NULL).
*
* Example:
- * io_loop();
+ * io_loop(NULL, NULL);
*/
-void *io_loop(void);
+void *io_loop(struct timers *timers, struct timer **expired);
/**
* io_conn_fd - get the fd from a connection.
* Sometimes useful, eg for getsockname().
*/
int io_conn_fd(const struct io_conn *conn);
+
+/**
+ * io_set_debug - set synchronous mode on a connection.
+ * @conn: the connection.
+ * @debug: whether to enable or disable debug.
+ *
+ * Once @debug is true on a connection, all I/O is done synchronously
+ * as soon as it is set, until it is unset or @conn is closed. This
+ * makes it easy to debug what's happening with a connection, but note
+ * that other connections are starved while this is being done.
+ *
+ * See also: io_debug_complete()
+ *
+ * Example:
+ * // Dumb init function to set debug and tell conn to close.
+ * static struct io_plan *conn_init(struct io_conn *conn, const char *msg)
+ * {
+ * io_set_debug(conn, true);
+ * return io_close(conn);
+ * }
+ */
+void io_set_debug(struct io_conn *conn, bool debug);
+
+/**
+ * io_debug_complete - empty function called when conn is closing/waiting.
+ * @conn: the connection.
+ *
+ * This is for putting a breakpoint onto, when debugging. It is called
+ * when a conn with io_set_debug() true can no longer be synchronous:
+ * 1) It is io_close()'d
+ * 2) It enters io_wait() (sychronous debug will resume after io_wake())
+ * 3) io_break() is called (sychronous debug will resume after io_loop())
+ */
+void io_debug_complete(struct io_conn *conn);
#endif /* CCAN_IO_H */