Commit 0fbc7909 authored by Rusty Russell's avatar Rusty Russell

ccan/io: add io_out_wait() and io_out_always().

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: default avatarRusty Russell <rusty@rustcorp.com.au>
parent d8ecdc80
...@@ -125,16 +125,28 @@ static struct io_plan *set_always(struct io_conn *conn, ...@@ -125,16 +125,28 @@ static struct io_plan *set_always(struct io_conn *conn,
return io_set_plan(conn, dir, NULL, next, arg); 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 *io_always_(struct io_conn *conn,
struct io_plan *(*next)(struct io_conn *, void *), struct io_plan *(*next)(struct io_conn *, void *),
void *arg) void *arg)
{ {
/* If we're duplex, we want this on the current plan. Otherwise, return io_always_dir(conn, IO_IN, next, arg);
* doesn't matter. */ }
if (conn->plan[IO_IN].status == IO_UNSET)
return set_always(conn, IO_IN, next, arg); struct io_plan *io_out_always_(struct io_conn *conn,
else struct io_plan *(*next)(struct io_conn *,
return set_always(conn, IO_OUT, next, arg); void *),
void *arg)
{
return io_always_dir(conn, IO_OUT, next, arg);
} }
static int do_write(int fd, struct io_plan_arg *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, ...@@ -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); return io_set_plan(conn, IO_IN, do_connect, next, next_arg);
} }
struct io_plan *io_wait_(struct io_conn *conn, static struct io_plan *io_wait_dir(struct io_conn *conn,
const void *wait, const void *wait,
struct io_plan *(*next)(struct io_conn *, void *), enum io_direction dir,
void *next_arg) struct io_plan *(*next)(struct io_conn *,
void *),
void *next_arg)
{ {
enum io_direction dir; struct io_plan_arg *arg = io_plan_arg(conn, 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);
arg->u1.const_vp = wait; arg->u1.const_vp = wait;
conn->plan[dir].status = IO_WAITING; conn->plan[dir].status = IO_WAITING;
...@@ -317,6 +321,22 @@ struct io_plan *io_wait_(struct io_conn *conn, ...@@ -317,6 +321,22 @@ struct io_plan *io_wait_(struct io_conn *conn,
return io_set_plan(conn, dir, NULL, next, next_arg); 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) void io_wake(const void *wait)
{ {
backend_wake(wait); backend_wake(wait);
......
...@@ -362,6 +362,27 @@ struct io_plan *io_always_(struct io_conn *conn, ...@@ -362,6 +362,27 @@ struct io_plan *io_always_(struct io_conn *conn,
struct io_plan *(*next)(struct io_conn *, void *), struct io_plan *(*next)(struct io_conn *, void *),
void *arg); 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. * io_connect - create an asynchronous connection to a listening socket.
* @conn: the connection that plan is for. * @conn: the connection that plan is for.
...@@ -418,12 +439,6 @@ struct io_plan *io_connect_(struct io_conn *conn, const struct addrinfo *addr, ...@@ -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 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: * Example:
* struct buf { * struct buf {
* char in[100]; * char in[100];
...@@ -501,6 +516,30 @@ struct io_plan *io_wait_(struct io_conn *conn, ...@@ -501,6 +516,30 @@ struct io_plan *io_wait_(struct io_conn *conn,
void *arg); 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 * io_wake - wake up any connections waiting on @wait
* @waitaddr: the address to trigger. * @waitaddr: the address to trigger.
......
...@@ -24,10 +24,20 @@ static void finish_ok(struct io_conn *conn, struct data *d) ...@@ -24,10 +24,20 @@ static void finish_ok(struct io_conn *conn, struct data *d)
d->state++; 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++; 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) 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) ...@@ -44,8 +54,8 @@ static struct io_plan *init_conn(struct io_conn *conn, struct data *d)
io_set_finish(conn, finish_ok, d); io_set_finish(conn, finish_ok, d);
return io_duplex(conn, return io_duplex(conn,
io_read(conn, d->buf, sizeof(d->buf), rw_done, d), io_read(conn, d->buf, sizeof(d->buf), r_done, d),
io_write(conn, d->wbuf, sizeof(d->wbuf), rw_done, d)); io_write(conn, d->wbuf, sizeof(d->wbuf), w_done, d));
} }
static int make_listen_fd(const char *port, struct addrinfo **info) static int make_listen_fd(const char *port, struct addrinfo **info)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment