MEDIUM: connection: add definitions for dual polling mechanisms

The conflicts we're facing with polling is that handshake handlers have
precedence over data handlers and may change the polling requirements
regardless of what is expected by the data layer. This causes issues
such as missed events.

The real need is to have three polling levels :
  - the "current" one, which is effective at any moment
  - the data one, which reflects what the data layer asks for
  - the sock one, which reflects what the socket layer asks for

Depending on whether a handshake is in progress or not, either one of the
last two will replace the current one, and the change will be propagated
to the lower layers.

At the moment, the shutdown status is not considered, and only handshakes
are used to decide which layer to chose. This will probably change.
diff --git a/include/proto/connection.h b/include/proto/connection.h
index 9ee9e9a..6bef475 100644
--- a/include/proto/connection.h
+++ b/include/proto/connection.h
@@ -37,6 +37,241 @@
 		conn->data->close(conn);
 }
 
+/* set polling depending on the change between the CURR part of the
+ * flags and the new flags in connection C. The connection flags are
+ * updated with the new flags at the end of the operation. Only the bits
+ * relevant to CO_FL_CURR_* from <flags> are considered.
+ */
+void conn_set_polling(struct connection *c, unsigned int new);
+
+/* update polling depending on the change between the CURR part of the
+ * flags and the DATA part of the flags in connection C. The connection
+ * is assumed to already be in the data phase.
+ */
+static inline void conn_update_data_polling(struct connection *c)
+{
+	conn_set_polling(c, c->flags << 8);
+}
+
+/* update polling depending on the change between the CURR part of the
+ * flags and the SOCK part of the flags in connection C. The connection
+ * is assumed to already be in the handshake phase.
+ */
+static inline void conn_update_sock_polling(struct connection *c)
+{
+	conn_set_polling(c, c->flags << 4);
+}
+
+/* returns non-zero if data flags from c->flags changes from what is in the
+ * current section of c->flags.
+ */
+static inline unsigned int conn_data_polling_changes(const struct connection *c)
+{
+	return ((c->flags << 8) ^ c->flags) & 0xF0000000;
+}
+
+/* returns non-zero if sock flags from c->flags changes from what is in the
+ * current section of c->flags.
+ */
+static inline unsigned int conn_sock_polling_changes(const struct connection *c)
+{
+	return ((c->flags << 4) ^ c->flags) & 0xF0000000;
+}
+
+/* Automatically updates polling on connection <c> depending on the DATA flags
+ * if no handshake is in progress.
+ */
+static inline void conn_cond_update_data_polling(struct connection *c)
+{
+	if (!(c->flags & CO_FL_POLL_SOCK) && conn_data_polling_changes(c))
+		conn_update_data_polling(c);
+}
+
+/* Automatically updates polling on connection <c> depending on the SOCK flags
+ * if a handshake is in progress.
+ */
+static inline void conn_cond_update_sock_polling(struct connection *c)
+{
+	if ((c->flags & CO_FL_POLL_SOCK) && conn_sock_polling_changes(c))
+		conn_update_sock_polling(c);
+}
+
+/* Automatically update polling on connection <c> depending on the DATA and
+ * SOCK flags, and on whether a handshake is in progress or not. This may be
+ * called at any moment when there is a doubt about the effectiveness of the
+ * polling state, for instance when entering or leaving the handshake state.
+ */
+static inline void conn_cond_update_polling(struct connection *c)
+{
+	if (!(c->flags & CO_FL_POLL_SOCK) && conn_data_polling_changes(c))
+		conn_update_data_polling(c);
+	else if ((c->flags & CO_FL_POLL_SOCK) && conn_sock_polling_changes(c))
+		conn_update_sock_polling(c);
+}
+
+/***** Event manipulation primitives for use by DATA I/O callbacks *****/
+/* The __conn_* versions do not propagate to lower layers and are only meant
+ * to be used by handlers called by the connection handler. The other ones
+ * may be used anywhere.
+ */
+static inline void __conn_data_want_recv(struct connection *c)
+{
+	c->flags |= CO_FL_DATA_RD_ENA;
+}
+
+static inline void __conn_data_stop_recv(struct connection *c)
+{
+	c->flags &= ~CO_FL_DATA_RD_ENA;
+}
+
+static inline void __conn_data_poll_recv(struct connection *c)
+{
+	c->flags |= CO_FL_DATA_RD_POL | CO_FL_DATA_RD_ENA;
+}
+
+static inline void __conn_data_want_send(struct connection *c)
+{
+	c->flags |= CO_FL_DATA_WR_ENA;
+}
+
+static inline void __conn_data_stop_send(struct connection *c)
+{
+	c->flags &= ~CO_FL_DATA_WR_ENA;
+}
+
+static inline void __conn_data_poll_send(struct connection *c)
+{
+	c->flags |= CO_FL_DATA_WR_POL | CO_FL_DATA_WR_ENA;
+}
+
+static inline void __conn_data_stop_both(struct connection *c)
+{
+	c->flags &= ~(CO_FL_DATA_WR_ENA | CO_FL_DATA_RD_ENA);
+}
+
+static inline void conn_data_want_recv(struct connection *c)
+{
+	__conn_data_want_recv(c);
+	conn_cond_update_data_polling(c);
+}
+
+static inline void conn_data_stop_recv(struct connection *c)
+{
+	__conn_data_stop_recv(c);
+	conn_cond_update_data_polling(c);
+}
+
+static inline void conn_data_poll_recv(struct connection *c)
+{
+	__conn_data_poll_recv(c);
+	conn_cond_update_data_polling(c);
+}
+
+static inline void conn_data_want_send(struct connection *c)
+{
+	__conn_data_want_send(c);
+	conn_cond_update_data_polling(c);
+}
+
+static inline void conn_data_stop_send(struct connection *c)
+{
+	__conn_data_stop_send(c);
+	conn_cond_update_data_polling(c);
+}
+
+static inline void conn_data_poll_send(struct connection *c)
+{
+	__conn_data_poll_send(c);
+	conn_cond_update_data_polling(c);
+}
+
+static inline void conn_data_stop_both(struct connection *c)
+{
+	__conn_data_stop_both(c);
+	conn_cond_update_data_polling(c);
+}
+
+/***** Event manipulation primitives for use by handshake I/O callbacks *****/
+/* The __conn_* versions do not propagate to lower layers and are only meant
+ * to be used by handlers called by the connection handler. The other ones
+ * may be used anywhere.
+ */
+static inline void __conn_sock_want_recv(struct connection *c)
+{
+	c->flags |= CO_FL_SOCK_RD_ENA;
+}
+
+static inline void __conn_sock_stop_recv(struct connection *c)
+{
+	c->flags &= ~CO_FL_SOCK_RD_ENA;
+}
+
+static inline void __conn_sock_poll_recv(struct connection *c)
+{
+	c->flags |= CO_FL_SOCK_RD_POL | CO_FL_SOCK_RD_ENA;
+}
+
+static inline void __conn_sock_want_send(struct connection *c)
+{
+	c->flags |= CO_FL_SOCK_WR_ENA;
+}
+
+static inline void __conn_sock_stop_send(struct connection *c)
+{
+	c->flags &= ~CO_FL_SOCK_WR_ENA;
+}
+
+static inline void __conn_sock_poll_send(struct connection *c)
+{
+	c->flags |= CO_FL_SOCK_WR_POL | CO_FL_SOCK_WR_ENA;
+}
+
+static inline void __conn_sock_stop_both(struct connection *c)
+{
+	c->flags &= ~(CO_FL_SOCK_WR_ENA | CO_FL_SOCK_RD_ENA);
+}
+
+static inline void conn_sock_want_recv(struct connection *c)
+{
+	__conn_sock_want_recv(c);
+	conn_cond_update_sock_polling(c);
+}
+
+static inline void conn_sock_stop_recv(struct connection *c)
+{
+	__conn_sock_stop_recv(c);
+	conn_cond_update_sock_polling(c);
+}
+
+static inline void conn_sock_poll_recv(struct connection *c)
+{
+	__conn_sock_poll_recv(c);
+	conn_cond_update_sock_polling(c);
+}
+
+static inline void conn_sock_want_send(struct connection *c)
+{
+	__conn_sock_want_send(c);
+	conn_cond_update_sock_polling(c);
+}
+
+static inline void conn_sock_stop_send(struct connection *c)
+{
+	__conn_sock_stop_send(c);
+	conn_cond_update_sock_polling(c);
+}
+
+static inline void conn_sock_poll_send(struct connection *c)
+{
+	__conn_sock_poll_send(c);
+	conn_cond_update_sock_polling(c);
+}
+
+static inline void conn_sock_stop_both(struct connection *c)
+{
+	__conn_sock_stop_both(c);
+	conn_cond_update_sock_polling(c);
+}
 
 #endif /* _PROTO_CONNECTION_H */
 
diff --git a/include/types/connection.h b/include/types/connection.h
index beaf6bb..8dd4f20 100644
--- a/include/types/connection.h
+++ b/include/types/connection.h
@@ -31,6 +31,27 @@
 struct sock_ops;
 struct protocol;
 
+/* Polling flags that are manipulated by I/O callbacks and handshake callbacks
+ * indicate what they expect from a file descriptor at each layer. For each
+ * direction, we have 2 bits, one stating whether any suspected activity on the
+ * FD induce a call to the iocb, and another one indicating that the FD has
+ * already returned EAGAIN and that polling on it is essential before calling
+ * the iocb again :
+ *   POL ENA  state
+ *    0   0   STOPPED : any activity on this FD is ignored
+ *    0   1   ENABLED : any (suspected) activity may call the iocb
+ *    1   0   STOPPED : as above
+ *    1   1   POLLED  : the FD is being polled for activity
+ *
+ * - Enabling an I/O event consists in ORing with 1.
+ * - Stopping an I/O event consists in ANDing with ~1.
+ * - Polling for an I/O event consists in ORing with ~3.
+ *
+ * The last computed state is remembered in CO_FL_CURR_* so that differential
+ * changes can be applied. For pollers that do not support speculative I/O,
+ * POLLED is the same as ENABLED and the POL flag can safely be ignored.
+ */
+
 /* flags for use in connection->flags */
 enum {
 	CO_FL_NONE          = 0x00000000,
@@ -46,6 +67,38 @@
 
 	/* below we have all handshake flags grouped into one */
 	CO_FL_HANDSHAKE     = CO_FL_SI_SEND_PROXY,
+
+	/* when any of these flags is set, polling is defined by socket-layer
+	 * operations, as opposed to data-layer.
+	 */
+	CO_FL_POLL_SOCK     = CO_FL_HANDSHAKE | CO_FL_WAIT_L4_CONN | CO_FL_WAIT_L6_CONN,
+
+	/* flags used to remember what shutdown have been performed/reported */
+	CO_FL_DATA_RD_SH    = 0x00010000,  /* DATA layer was notified about shutr/read0 */
+	CO_FL_DATA_WR_SH    = 0x00020000,  /* DATA layer asked for shutw */
+	CO_FL_SOCK_RD_SH    = 0x00040000,  /* SOCK layer was notified about shutr/read0 */
+	CO_FL_SOCK_WR_SH    = 0x00080000,  /* SOCK layer asked for shutw */
+
+	/****** NOTE: do not change the values of the flags below ******/
+	CO_FL_RD_ENA = 1, CO_FL_RD_POL = 2, CO_FL_WR_ENA = 4, CO_FL_WR_POL = 8,
+
+	/* flags describing the DATA layer expectations regarding polling */
+	CO_FL_DATA_RD_ENA   = CO_FL_RD_ENA << 20,  /* receiving is allowed */
+	CO_FL_DATA_RD_POL   = CO_FL_RD_POL << 20,  /* receiving needs to poll first */
+	CO_FL_DATA_WR_ENA   = CO_FL_WR_ENA << 20,  /* sending is desired */
+	CO_FL_DATA_WR_POL   = CO_FL_WR_POL << 20,  /* sending needs to poll first */
+
+	/* flags describing the SOCK layer expectations regarding polling */
+	CO_FL_SOCK_RD_ENA   = CO_FL_RD_ENA << 24,  /* receiving is allowed */
+	CO_FL_SOCK_RD_POL   = CO_FL_RD_POL << 24,  /* receiving needs to poll first */
+	CO_FL_SOCK_WR_ENA   = CO_FL_WR_ENA << 24,  /* sending is desired */
+	CO_FL_SOCK_WR_POL   = CO_FL_WR_POL << 24,  /* sending needs to poll first */
+
+	/* flags storing the current polling state */
+	CO_FL_CURR_RD_ENA   = CO_FL_RD_ENA << 28,  /* receiving is allowed */
+	CO_FL_CURR_RD_POL   = CO_FL_RD_POL << 28,  /* receiving needs to poll first */
+	CO_FL_CURR_WR_ENA   = CO_FL_WR_ENA << 28,  /* sending is desired */
+	CO_FL_CURR_WR_POL   = CO_FL_WR_POL << 28,  /* sending needs to poll first */
 };
 
 /* This structure describes a connection with its methods and data.
diff --git a/src/connection.c b/src/connection.c
index 63f143e..712dfba 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -88,3 +88,34 @@
 	fdtab[fd].ev &= ~(FD_POLL_IN | FD_POLL_OUT | FD_POLL_HUP | FD_POLL_ERR);
 	return ret;
 }
+
+/* set polling depending on the change between the CURR part of the
+ * flags and the new flags in connection C. The connection flags are
+ * updated with the new flags at the end of the operation. Only the bits
+ * relevant to CO_FL_CURR_* from <flags> are considered.
+ */
+void conn_set_polling(struct connection *c, unsigned int new)
+{
+	unsigned int old = c->flags; /* for CO_FL_CURR_* */
+
+	/* update read status if needed */
+	if ((old & (CO_FL_CURR_RD_ENA|CO_FL_CURR_RD_POL)) != (CO_FL_CURR_RD_ENA|CO_FL_CURR_RD_POL) &&
+	    (new & (CO_FL_CURR_RD_ENA|CO_FL_CURR_RD_POL)) == (CO_FL_CURR_RD_ENA|CO_FL_CURR_RD_POL))
+		fd_poll_recv(c->t.sock.fd);
+	else if (!(old & CO_FL_CURR_RD_ENA) && (new & CO_FL_CURR_RD_ENA))
+		fd_want_recv(c->t.sock.fd);
+	else if ((old & CO_FL_CURR_RD_ENA) && !(new & CO_FL_CURR_RD_ENA))
+		fd_stop_recv(c->t.sock.fd);
+
+	/* update write status if needed */
+	if ((old & (CO_FL_CURR_WR_ENA|CO_FL_CURR_WR_POL)) != (CO_FL_CURR_WR_ENA|CO_FL_CURR_WR_POL) &&
+	    (new & (CO_FL_CURR_WR_ENA|CO_FL_CURR_WR_POL)) == (CO_FL_CURR_WR_ENA|CO_FL_CURR_WR_POL))
+		fd_poll_send(c->t.sock.fd);
+	else if (!(old & CO_FL_CURR_WR_ENA) && (new & CO_FL_CURR_WR_ENA))
+		fd_want_send(c->t.sock.fd);
+	else if ((old & CO_FL_CURR_WR_ENA) && !(new & CO_FL_CURR_WR_ENA))
+		fd_stop_send(c->t.sock.fd);
+
+	c->flags &= ~(CO_FL_CURR_WR_POL|CO_FL_CURR_WR_ENA|CO_FL_CURR_RD_POL|CO_FL_CURR_RD_ENA);
+	c->flags |= new & (CO_FL_CURR_WR_POL|CO_FL_CURR_WR_ENA|CO_FL_CURR_RD_POL|CO_FL_CURR_RD_ENA);
+}