From 327a6be24f1689975396b44895e82c91da6addf9 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Fri, 28 Mar 2008 15:02:46 -0700 Subject: [PATCH] Clean up and comment poll-loop module. --- include/poll-loop.h | 34 +++++++-- lib/poll-loop.c | 167 ++++++++++++++++++++++++++++---------------- lib/vconn-netlink.c | 2 +- lib/vconn-ssl.c | 12 ++-- lib/vconn-tcp.c | 8 +-- switch/netdev.c | 4 +- 6 files changed, 147 insertions(+), 80 deletions(-) diff --git a/include/poll-loop.h b/include/poll-loop.h index 6ef5d9bd..c56bc066 100644 --- a/include/poll-loop.h +++ b/include/poll-loop.h @@ -31,20 +31,42 @@ * derivatives without specific, written prior permission. */ +/* High-level wrapper around the "poll" system call. + * + * Intended usage is for the program's main loop to go about its business + * servicing whatever events it needs to. Then, when it runs out of immediate + * tasks, it calls each subordinate module's "wait" function, which in turn + * calls one (or more) of the functions poll_fd_wait(), poll_immediate_wake(), + * and poll_timer_wait() to register to be awakened when the appropriate event + * occurs. Then the main loop calls poll_block(), which blocks until one of + * the registered events happens. + * + * There is also some support for autonomous subroutines that are executed by + * poll_block() when a file descriptor becomes ready. To prevent these + * routines from starving if events are continously ready, the application + * should bound the amount of work it does between poll_block() calls. */ + #ifndef POLL_LOOP_H #define POLL_LOOP_H 1 #include -typedef void poll_fd_func(int fd, short int revents, void *aux); +struct poll_waiter; + +/* Schedule events to wake up the following poll_block(). */ +struct poll_waiter *poll_fd_wait(int fd, short int events); +void poll_timer_wait(int msec); +void poll_immediate_wake(void); + +/* Wait until an event occurs. */ +void poll_block(void); +/* Autonomous function callbacks. */ +typedef void poll_fd_func(int fd, short int revents, void *aux); struct poll_waiter *poll_fd_callback(int fd, short int events, poll_fd_func *, void *aux); -struct poll_waiter *poll_fd_wait(int fd, short int events, short int *revents); -void poll_cancel(struct poll_waiter *); -void poll_immediate_wake(void); -void poll_timer_wait(int msec); -void poll_block(void); +/* Cancel a file descriptor callback or event. */ +void poll_cancel(struct poll_waiter *); #endif /* poll-loop.h */ diff --git a/lib/poll-loop.c b/lib/poll-loop.c index 8cb0c55e..556521ab 100644 --- a/lib/poll-loop.c +++ b/lib/poll-loop.c @@ -42,83 +42,80 @@ #define THIS_MODULE VLM_poll_loop #include "vlog.h" +/* An event that will wake the following call to poll_block(). */ struct poll_waiter { - struct list node; - int fd; - short int events; - struct pollfd *pollfd; - - short int *revents; - - poll_fd_func *function; - void *aux; + /* Set when the waiter is created. */ + struct list node; /* Element in global waiters list. */ + int fd; /* File descriptor. */ + short int events; /* Events to wait for (POLLIN, POLLOUT). */ + poll_fd_func *function; /* Callback function, if any, or null. */ + void *aux; /* Argument to callback function. */ + + /* Set only when poll_block() is called. */ + struct pollfd *pollfd; /* Pointer to element of the pollfds array + (null if added from a callback). */ }; +/* All active poll waiters. */ static struct list waiters = LIST_INITIALIZER(&waiters); + +/* Number of elements in the waiters list. */ static size_t n_waiters; + +/* Max time to wait in next call to poll_block(), in milliseconds, or -1 to + * wait forever. */ static int timeout = -1; +/* Callback currently running, to allow verifying that poll_cancel() is not + * being called on a running callback. */ #ifndef NDEBUG static struct poll_waiter *running_cb; #endif -static struct poll_waiter * -new_waiter(int fd, short int events) -{ - struct poll_waiter *waiter = xcalloc(1, sizeof *waiter); - assert(fd >= 0); - waiter->fd = fd; - waiter->events = events; - list_push_back(&waiters, &waiter->node); - n_waiters++; - return waiter; -} - -struct poll_waiter * -poll_fd_callback(int fd, short int events, poll_fd_func *function, void *aux) -{ - struct poll_waiter *pw = new_waiter(fd, events); - pw->function = function; - pw->aux = aux; - return pw; -} +static struct poll_waiter *new_waiter(int fd, short int events); +/* Registers 'fd' as waiting for the specified 'events' (which should be POLLIN + * or POLLOUT or POLLIN | POLLOUT). The following call to poll_block() will + * wake up when 'fd' becomes ready for one or more of the requested events. + * + * The event registration is one-shot: only the following call to poll_block() + * is affected. The event will need to be re-registered after poll_block() is + * called if it is to persist. */ struct poll_waiter * -poll_fd_wait(int fd, short int events, short int *revents) +poll_fd_wait(int fd, short int events) { - struct poll_waiter *pw = new_waiter(fd, events); - pw->revents = revents; - if (revents) { - *revents = 0; - } - return pw; + return new_waiter(fd, events); } +/* Causes the following call to poll_block() to block for no more than 'msec' + * milliseconds. If 'msec' is nonpositive, the following call to poll_block() + * will not block at all. + * + * The timer registration is one-shot: only the following call to poll_block() + * is affected. The timer will need to be re-registered after poll_block() is + * called if it is to persist. */ void -poll_cancel(struct poll_waiter *pw) +poll_timer_wait(int msec) { - if (pw) { - assert(pw != running_cb); - list_remove(&pw->node); - free(pw); - n_waiters--; + if (timeout < 0 || msec < timeout) { + timeout = MAX(0, msec); } } +/* Causes the following call to poll_block() to wake up immediately, without + * blocking. */ void poll_immediate_wake(void) { timeout = 0; } -void -poll_timer_wait(int msec) -{ - if (timeout < 0 || msec < timeout) { - timeout = MAX(0, msec); - } -} - +/* Blocks until one or more of the events registered with poll_fd_wait() + * occurs, or until the minimum duration registered with poll_timer_wait() + * elapses, or not at all if poll_immediate_wake() has been called. + * + * Also executes any autonomous subroutines registered with poll_fd_callback(), + * if their file descriptor have become ready. */ void poll_block(void) { @@ -159,23 +156,71 @@ poll_block(void) node = node->next; continue; } - } else { - if (pw->function) { + } else if (pw->function) { #ifndef NDEBUG - running_cb = pw; + running_cb = pw; #endif - pw->function(pw->fd, pw->pollfd->revents, pw->aux); + pw->function(pw->fd, pw->pollfd->revents, pw->aux); #ifndef NDEBUG - running_cb = NULL; + running_cb = NULL; #endif - } else if (pw->revents) { - *pw->revents = pw->pollfd->revents; - } } - node = list_remove(node); - free(pw); - n_waiters--; + node = node->next; + poll_cancel(pw); } timeout = -1; } + +/* Registers 'function' to be called with argument 'aux' by poll_block() when + * 'fd' becomes ready for one of the events in 'events', which should be POLLIN + * or POLLOUT or POLLIN | POLLOUT. + * + * The callback registration persists until the event actually occurs. At that + * point, it is automatically de-registered. The callback function must + * re-register the event by calling poll_fd_callback() again within the + * callback, if it wants to be called back again later. */ +struct poll_waiter * +poll_fd_callback(int fd, short int events, poll_fd_func *function, void *aux) +{ + struct poll_waiter *pw = new_waiter(fd, events); + pw->function = function; + pw->aux = aux; + return pw; +} + +/* Cancels the file descriptor event registered with poll_fd_wait() or + * poll_fd_callback(). 'pw' must be the struct poll_waiter returned by one of + * those functions. + * + * An event registered with poll_fd_wait() may be canceled from its time of + * registration until the next call to poll_block(). At that point, the event + * is automatically canceled by the system and its poll_waiter is freed. + * + * An event registered with poll_fd_callback() may be canceled from its time of + * registration until its callback is actually called. At that point, the + * event is automatically canceled by the system and its poll_waiter is + * freed. */ +void +poll_cancel(struct poll_waiter *pw) +{ + if (pw) { + assert(pw != running_cb); + list_remove(&pw->node); + free(pw); + n_waiters--; + } +} + +/* Creates and returns a new poll_waiter for 'fd' and 'events'. */ +static struct poll_waiter * +new_waiter(int fd, short int events) +{ + struct poll_waiter *waiter = xcalloc(1, sizeof *waiter); + assert(fd >= 0); + waiter->fd = fd; + waiter->events = events; + list_push_back(&waiters, &waiter->node); + n_waiters++; + return waiter; +} diff --git a/lib/vconn-netlink.c b/lib/vconn-netlink.c index 625b524d..05c7ab07 100644 --- a/lib/vconn-netlink.c +++ b/lib/vconn-netlink.c @@ -134,7 +134,7 @@ netlink_wait(struct vconn *vconn, enum vconn_wait_type wait) default: NOT_REACHED(); } - poll_fd_wait(nl_sock_fd(netlink->dp.sock), events, NULL); + poll_fd_wait(nl_sock_fd(netlink->dp.sock), events); } struct vconn_class netlink_vconn_class = { diff --git a/lib/vconn-ssl.c b/lib/vconn-ssl.c index be19c014..30d8caf6 100644 --- a/lib/vconn-ssl.c +++ b/lib/vconn-ssl.c @@ -473,10 +473,10 @@ static bool ssl_needs_wait(struct ssl_vconn *sslv) { if (SSL_want_read(sslv->ssl)) { - poll_fd_wait(sslv->fd, POLLIN, NULL); + poll_fd_wait(sslv->fd, POLLIN); return true; } else if (SSL_want_write(sslv->ssl)) { - poll_fd_wait(sslv->fd, POLLOUT, NULL); + poll_fd_wait(sslv->fd, POLLOUT); return true; } else { return false; @@ -493,7 +493,7 @@ ssl_wait(struct vconn *vconn, enum vconn_wait_type wait) if (vconn_connect(vconn) != EAGAIN) { poll_immediate_wake(); } else if (sslv->state == STATE_TCP_CONNECTING) { - poll_fd_wait(sslv->fd, POLLOUT, NULL); + poll_fd_wait(sslv->fd, POLLOUT); } else if (!ssl_needs_wait(sslv)) { NOT_REACHED(); } @@ -504,14 +504,14 @@ ssl_wait(struct vconn *vconn, enum vconn_wait_type wait) if (SSL_pending(sslv->ssl)) { poll_immediate_wake(); } else { - poll_fd_wait(sslv->fd, POLLIN, NULL); + poll_fd_wait(sslv->fd, POLLIN); } } break; case WAIT_SEND: if (!sslv->txbuf && !ssl_needs_wait(sslv)) { - poll_fd_wait(sslv->fd, POLLOUT, NULL); + poll_fd_wait(sslv->fd, POLLOUT); } break; @@ -646,7 +646,7 @@ pssl_wait(struct vconn *vconn, enum vconn_wait_type wait) { struct pssl_vconn *pssl = pssl_vconn_cast(vconn); assert(wait == WAIT_ACCEPT); - poll_fd_wait(pssl->fd, POLLIN, NULL); + poll_fd_wait(pssl->fd, POLLIN); } struct vconn_class pssl_vconn_class = { diff --git a/lib/vconn-tcp.c b/lib/vconn-tcp.c index 0053faf3..1fe5919f 100644 --- a/lib/vconn-tcp.c +++ b/lib/vconn-tcp.c @@ -276,19 +276,19 @@ tcp_wait(struct vconn *vconn, enum vconn_wait_type wait) struct tcp_vconn *tcp = tcp_vconn_cast(vconn); switch (wait) { case WAIT_CONNECT: - poll_fd_wait(tcp->fd, POLLOUT, NULL); + poll_fd_wait(tcp->fd, POLLOUT); break; case WAIT_SEND: if (!tcp->txbuf) { - poll_fd_wait(tcp->fd, POLLOUT, NULL); + poll_fd_wait(tcp->fd, POLLOUT); } else { /* Nothing to do: need to drain txbuf first. */ } break; case WAIT_RECV: - poll_fd_wait(tcp->fd, POLLIN, NULL); + poll_fd_wait(tcp->fd, POLLIN); break; default: @@ -414,7 +414,7 @@ ptcp_wait(struct vconn *vconn, enum vconn_wait_type wait) { struct ptcp_vconn *ptcp = ptcp_vconn_cast(vconn); assert(wait == WAIT_ACCEPT); - poll_fd_wait(ptcp->fd, POLLIN, NULL); + poll_fd_wait(ptcp->fd, POLLIN); } struct vconn_class ptcp_vconn_class = { diff --git a/switch/netdev.c b/switch/netdev.c index 8aa8ddf7..bad89c41 100644 --- a/switch/netdev.c +++ b/switch/netdev.c @@ -428,7 +428,7 @@ netdev_recv(struct netdev *netdev, struct buffer *buffer) void netdev_recv_wait(struct netdev *netdev) { - poll_fd_wait(netdev->fd, POLLIN, NULL); + poll_fd_wait(netdev->fd, POLLIN); } /* Sends 'buffer' on 'netdev'. Returns 0 if successful, otherwise a positive @@ -494,7 +494,7 @@ netdev_send(struct netdev *netdev, struct buffer *buffer) void netdev_send_wait(struct netdev *netdev) { - poll_fd_wait(netdev->fd, POLLOUT, NULL); + poll_fd_wait(netdev->fd, POLLOUT); } /* Returns a pointer to 'netdev''s MAC address. The caller must not modify or -- 2.30.2