#include <sys/socket.h>
#include <limits.h>
#include <errno.h>
-#include <ccan/list/list.h>
#include <ccan/time/time.h>
#include <ccan/timer/timer.h>
static size_t num_fds = 0, max_fds = 0, num_waiting = 0;
static struct pollfd *pollfds = NULL;
static struct fd **fds = NULL;
-static struct io_conn *closing = NULL, *always = NULL;
+static LIST_HEAD(closing);
+static LIST_HEAD(always);
+static struct timemono (*nowfn)(void) = time_mono;
+
+struct timemono (*io_time_override(struct timemono (*now)(void)))(void)
+{
+ struct timemono (*old)(void) = nowfn;
+ nowfn = now;
+ return old;
+}
static bool add_fd(struct fd *fd, short events)
{
}
num_fds--;
fd->backend_info = -1;
+
+ /* Closing a local socket doesn't wake poll() because other end
+ * has them open. See 2.6. When should I use shutdown()?
+ * in http://www.faqs.org/faqs/unix-faq/socket/ */
+ shutdown(fd->fd, SHUT_RDWR);
+
close(fd->fd);
}
void remove_from_always(struct io_conn *conn)
{
- struct io_conn **p = &always;
-
- while (*p != conn)
- p = &(*p)->list;
-
- *p = conn->list;
+ list_del_init(&conn->always);
}
void backend_new_closing(struct io_conn *conn)
{
- /* Already on always list? Remove it. */
- if (conn->list)
- remove_from_always(conn);
-
- conn->list = closing;
- closing = conn;
+ /* In case it's on always list, remove it. */
+ list_del_init(&conn->always);
+ list_add_tail(&closing, &conn->closing);
}
void backend_new_always(struct io_conn *conn)
{
- /* May already be in always list (other plan), or closing. */
- if (!conn->list) {
- conn->list = always;
- always = conn;
- }
+ /* In case it's already in always list. */
+ list_del(&conn->always);
+ list_add_tail(&always, &conn->always);
}
void backend_new_plan(struct io_conn *conn)
c = (void *)fds[i];
if (c->plan[IO_IN].status == IO_WAITING
- && c->plan[IO_IN].u1.const_vp == wait)
- io_do_wakeup(c, &c->plan[IO_IN]);
+ && c->plan[IO_IN].arg.u1.const_vp == wait)
+ io_do_wakeup(c, IO_IN);
if (c->plan[IO_OUT].status == IO_WAITING
- && c->plan[IO_OUT].u1.const_vp == wait)
- io_do_wakeup(c, &c->plan[IO_OUT]);
+ && c->plan[IO_OUT].arg.u1.const_vp == wait)
+ io_do_wakeup(c, IO_OUT);
}
}
del_fd(&conn->fd);
if (conn->finish) {
/* Saved by io_close */
- errno = conn->plan[IO_IN].u1.s;
+ errno = conn->plan[IO_IN].arg.u1.s;
conn->finish(conn, conn->finish_arg);
}
tal_free(conn);
static bool close_conns(void)
{
bool ret = false;
+ struct io_conn *conn;
- while (closing) {
- struct io_conn *conn = closing;
-
+ while ((conn = list_pop(&closing, struct io_conn, closing)) != NULL) {
assert(conn->plan[IO_IN].status == IO_CLOSING);
assert(conn->plan[IO_OUT].status == IO_CLOSING);
- closing = closing->list;
del_conn(conn);
ret = true;
}
static bool handle_always(void)
{
bool ret = false;
+ struct io_conn *conn;
- while (always) {
- struct io_conn *conn = always;
-
+ while ((conn = list_pop(&always, struct io_conn, always)) != NULL) {
assert(conn->plan[IO_IN].status == IO_ALWAYS
|| conn->plan[IO_OUT].status == IO_ALWAYS);
- /* Remove from list, and mark it so it knows that. */
- always = always->list;
- conn->list = NULL;
+ /* Re-initialize, for next time. */
+ list_node_init(&conn->always);
io_do_always(conn);
ret = true;
}
}
/* This is the main loop. */
-void *io_loop(struct timers *timers, struct list_head *expired)
+void *io_loop(struct timers *timers, struct timer **expired)
{
void *ret;
/* if timers is NULL, expired must be. If not, not. */
assert(!timers == !expired);
- /* Make sure this is empty if we exit for some other reason. */
+ /* Make sure this is NULL if we exit for some other reason. */
if (expired)
- list_head_init(expired);
+ *expired = NULL;
while (!io_loop_return) {
int i, r, ms_timeout = -1;
assert(num_waiting);
if (timers) {
- struct timeabs now, first;
+ struct timemono now, first;
- now = time_now();
+ now = nowfn();
/* Call functions for expired timers. */
- timers_expire(timers, now, expired);
- if (!list_empty(expired))
+ *expired = timers_expire(timers, now);
+ if (*expired)
break;
/* Now figure out how long to wait for the next one. */
if (timer_earliest(timers, &first)) {
uint64_t next;
- next = time_to_msec(time_between(first, now));
+ next = time_to_msec(timemono_between(first, now));
if (next < INT_MAX)
ms_timeout = next;
else