diff --git a/src/connection.c b/src/connection.c index 0029b4dd2..0a87eb7cf 100644 --- a/src/connection.c +++ b/src/connection.c @@ -334,25 +334,45 @@ int conn_unsubscribe(struct connection *conn, void *xprt_ctx, int event_type, st return 0; } -/* Called from the upper layer, to unsubscribe from events - * (undo fcgi_subscribe). The struct is not allowed to differ from the one - * passed to the subscribe() call. It always returns zero. +/* Called from the upper layer, to subscribe to events . + * The struct is not allowed to differ from the one passed during a + * previous call to subscribe(). If the FD is ready, the wait_event is + * immediately woken up and the subcription is cancelled. It always + * returns zero. */ int conn_subscribe(struct connection *conn, void *xprt_ctx, int event_type, struct wait_event *es) { BUG_ON(event_type & ~(SUB_RETRY_SEND|SUB_RETRY_RECV)); - BUG_ON(conn->subs && conn->subs->events & event_type); BUG_ON(conn->subs && conn->subs != es); + if (conn->subs && (conn->subs->events & event_type) == event_type) + return 0; + conn->subs = es; es->events |= event_type; if (conn_ctrl_ready(conn)) { - if (event_type & SUB_RETRY_RECV) - fd_want_recv(conn->handle.fd); + if (event_type & SUB_RETRY_RECV) { + if (fd_recv_ready(conn->handle.fd)) { + tasklet_wakeup(es->tasklet); + es->events &= ~SUB_RETRY_RECV; + if (!es->events) + conn->subs = NULL; + } + else + fd_want_recv(conn->handle.fd); + } - if (event_type & SUB_RETRY_SEND) - fd_want_send(conn->handle.fd); + if (event_type & SUB_RETRY_SEND) { + if (fd_send_ready(conn->handle.fd)) { + tasklet_wakeup(es->tasklet); + es->events &= ~SUB_RETRY_SEND; + if (!es->events) + conn->subs = NULL; + } + else + fd_want_send(conn->handle.fd); + } } return 0; }