MAJOR: connection: rearrange the polling flags.
Polling flags were set for data and sock layer, but while this does make
sense for the ENA flag, it does not for the POL flag which translates the
detection of an EAGAIN condition. So now we remove the {DATA,SOCK}_POL*
flags and instead introduce two new layer-independant flags (WANT_RD and
WANT_WR). These flags are only set when an EAGAIN is encountered so that
polling can be enabled.
In order for these flags to have any meaning they are not persistent and
have to be cleared by the connection handler before calling the I/O and
data callbacks. For this reason, changes detection has been slightly
improved. Instead of comparing the WANT_* flags with CURR_*_POL, we only
check if the ENA status changes, or if the polling appears, since we don't
want to detect the useless poll to ena transition. Tests show that this
has eliminated one useless call to __fd_clr().
Finally the conn_set_polling() function which was becoming complex and
required complex operations from the caller was split in two and replaced
its two only callers (conn_update_data_polling and conn_update_sock_polling).
The two functions are now much smaller due to the less complex conditions.
Note that it would be possible to re-merge them and only pass a mask but
this does not appear much interesting.
diff --git a/include/proto/connection.h b/include/proto/connection.h
index a575e69..43f02d7 100644
--- a/include/proto/connection.h
+++ b/include/proto/connection.h
@@ -50,45 +50,40 @@
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.
+/* Update polling on connection <c>'s file descriptor depending on its current
+ * state as reported in the connection's CO_FL_CURR_* flags, reports of EAGAIN
+ * in CO_FL_WAIT_*, and the sock layer expectations indicated by CO_FL_SOCK_*.
+ * The connection flags are updated with the new flags at the end of the
+ * operation.
*/
-void conn_set_polling(struct connection *c, unsigned int new);
+void conn_update_sock_polling(struct connection *c);
-/* 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.
+/* Update polling on connection <c>'s file descriptor depending on its current
+ * state as reported in the connection's CO_FL_CURR_* flags, reports of EAGAIN
+ * in CO_FL_WAIT_*, and the data layer expectations indicated by CO_FL_DATA_*.
+ * The connection flags are updated with the new flags at the end of the
+ * operation.
*/
-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);
-}
+void conn_update_data_polling(struct connection *c);
-/* returns non-zero if data flags from c->flags changes from what is in the
- * current section of c->flags.
+/* inspects c->flags and returns non-zero if DATA ENA changes from the CURR ENA
+ * or if the WAIT flags set new flags that were not in CURR POL.
*/
static inline unsigned int conn_data_polling_changes(const struct connection *c)
{
- return ((c->flags << 8) ^ c->flags) & 0xF0000000;
+ return (((c->flags << 6) ^ (c->flags << 2)) | /* changes in ENA go to bits 30&31 */
+ (((c->flags << 8) & ~c->flags))) & /* new bits in POL go to bits 30&31 */
+ 0xC0000000;
}
-/* returns non-zero if sock flags from c->flags changes from what is in the
- * current section of c->flags.
+/* inspects c->flags and returns non-zero if SOCK ENA changes from the CURR ENA
+ * or if the WAIT flags set new flags that were not in CURR POL.
*/
static inline unsigned int conn_sock_polling_changes(const struct connection *c)
{
- return ((c->flags << 4) ^ c->flags) & 0xF0000000;
+ return (((c->flags << 4) ^ (c->flags << 2)) | /* changes in ENA go to bits 30&31 */
+ (((c->flags << 8) & ~c->flags))) & /* new bits in POL go to bits 30&31 */
+ 0xC0000000;
}
/* Automatically updates polling on connection <c> depending on the DATA flags
@@ -139,7 +134,7 @@
static inline void __conn_data_poll_recv(struct connection *c)
{
- c->flags |= CO_FL_DATA_RD_POL | CO_FL_DATA_RD_ENA;
+ c->flags |= CO_FL_WAIT_RD | CO_FL_DATA_RD_ENA;
}
static inline void __conn_data_want_send(struct connection *c)
@@ -154,7 +149,7 @@
static inline void __conn_data_poll_send(struct connection *c)
{
- c->flags |= CO_FL_DATA_WR_POL | CO_FL_DATA_WR_ENA;
+ c->flags |= CO_FL_WAIT_WR | CO_FL_DATA_WR_ENA;
}
static inline void __conn_data_stop_both(struct connection *c)
@@ -221,7 +216,7 @@
static inline void __conn_sock_poll_recv(struct connection *c)
{
- c->flags |= CO_FL_SOCK_RD_POL | CO_FL_SOCK_RD_ENA;
+ c->flags |= CO_FL_WAIT_RD | CO_FL_SOCK_RD_ENA;
}
static inline void __conn_sock_want_send(struct connection *c)
@@ -236,7 +231,7 @@
static inline void __conn_sock_poll_send(struct connection *c)
{
- c->flags |= CO_FL_SOCK_WR_POL | CO_FL_SOCK_WR_ENA;
+ c->flags |= CO_FL_WAIT_WR | CO_FL_SOCK_WR_ENA;
}
static inline void __conn_sock_stop_both(struct connection *c)
diff --git a/include/types/connection.h b/include/types/connection.h
index 6a36dc7..519eb70 100644
--- a/include/types/connection.h
+++ b/include/types/connection.h
@@ -55,8 +55,16 @@
* - 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.
+ * changes can be applied. After bits are applied, the POLL status bits are
+ * cleared so that it is possible to detect when an EAGAIN was encountered. For
+ * pollers that do not support speculative I/O, POLLED is the same as ENABLED
+ * and the POL flag can safely be ignored. However it makes a difference for
+ * the connection handler.
+ *
+ * The ENA flags are per-layer (one pair for SOCK, another one for DATA).
+ * The POL flags are only for the socket layer since they indicate that EAGAIN
+ * was encountered. Thus, the DATA layer uses its own ENA flag and the socket
+ * layer's POL flag.
*/
/* flags for use in connection->flags */
@@ -83,18 +91,9 @@
*/
CO_FL_POLL_SOCK = CO_FL_HANDSHAKE | CO_FL_WAIT_L4_CONN | CO_FL_WAIT_L6_CONN,
- /* This flag is set if lingering has already been disabled on the connection */
-
/* These flags are used to report whether the from/to addresses are set or not */
- CO_FL_ADDR_FROM_SET = 0x00001000, /* addr.from is set */
- CO_FL_ADDR_TO_SET = 0x00002000, /* addr.to is set */
-
- /* These flags are used by data layers to indicate to their iterators
- * whether they had to stop due to missing data or missing room. Their
- * callers must reset them before calling the data layer handlers.
- */
- CO_FL_WAIT_DATA = 0x00004000, /* data source is empty */
- CO_FL_WAIT_ROOM = 0x00008000, /* data sink is full */
+ CO_FL_ADDR_FROM_SET = 0x00004000, /* addr.from is set */
+ CO_FL_ADDR_TO_SET = 0x00008000, /* addr.to is set */
/* flags used to remember what shutdown have been performed/reported */
CO_FL_DATA_RD_SH = 0x00010000, /* DATA layer was notified about shutr/read0 */
@@ -102,26 +101,41 @@
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 any of the flags below, they're
+ * used with masks and bit shifts to quickly detect multiple changes.
+ */
+
+ /* These flags are used by data layers to indicate to indicate they had
+ * to stop sending data because a buffer was empty (WAIT_DATA) or stop
+ * receiving data because a buffer was full (WAIT_ROOM). The connection
+ * handler clears them before first calling the I/O and data callbacks.
+ */
+ CO_FL_WAIT_DATA = 0x00100000, /* data source is empty */
+ CO_FL_WAIT_ROOM = 0x00200000, /* data sink is full */
+
- /****** 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,
+ /* These flags are used by both socket-level and data-level callbacks
+ * to indicate that they had to stop receiving or sending because a
+ * socket-level operation returned EAGAIN. While setting these flags
+ * is not always absolutely mandatory (eg: when a reader estimates that
+ * trying again soon without polling is OK), it is however forbidden to
+ * set them without really attempting the I/O operation.
+ */
+ CO_FL_WAIT_RD = 0x00400000, /* receiving needs to poll first */
+ CO_FL_WAIT_WR = 0x00800000, /* sending needs to poll first */
/* 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 */
+ CO_FL_DATA_RD_ENA = 0x01000000, /* receiving is allowed */
+ CO_FL_DATA_WR_ENA = 0x02000000, /* sending is desired */
/* 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 */
+ CO_FL_SOCK_RD_ENA = 0x04000000, /* receiving is allowed */
+ CO_FL_SOCK_WR_ENA = 0x08000000, /* sending is desired */
/* 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 */
+ CO_FL_CURR_RD_ENA = 0x10000000, /* receiving is allowed */
+ CO_FL_CURR_WR_ENA = 0x20000000, /* sending is desired */
+ CO_FL_CURR_RD_POL = 0x40000000, /* receiving needs to poll first */
+ CO_FL_CURR_WR_POL = 0x80000000, /* sending needs to poll first */
};
/* target types */
diff --git a/src/connection.c b/src/connection.c
index 44e675b..6ed4408 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -28,6 +28,11 @@
if (unlikely(!conn))
return 0;
+ /* before engaging there, we clear the new WAIT_* flags so that we can
+ * more easily detect an EAGAIN condition from anywhere.
+ */
+ conn->flags &= ~(CO_FL_WAIT_DATA|CO_FL_WAIT_ROOM|CO_FL_WAIT_RD|CO_FL_WAIT_WR);
+
process_handshake:
/* The handshake callbacks are called in sequence. If either of them is
* missing something, it must enable the required polling at the socket
@@ -115,33 +120,86 @@
return 0;
}
-/* 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.
+/* Update polling on connection <c>'s file descriptor depending on its current
+ * state as reported in the connection's CO_FL_CURR_* flags, reports of EAGAIN
+ * in CO_FL_WAIT_*, and the data layer expectations indicated by CO_FL_DATA_*.
+ * The connection flags are updated with the new flags at the end of the
+ * operation.
*/
-void conn_set_polling(struct connection *c, unsigned int new)
+void conn_update_data_polling(struct connection *c)
{
- unsigned int old = c->flags; /* for CO_FL_CURR_* */
+ unsigned int f = c->flags;
/* 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))
+ if (unlikely((f & (CO_FL_CURR_RD_ENA|CO_FL_DATA_RD_ENA)) == CO_FL_CURR_RD_ENA)) {
+ f &= ~(CO_FL_CURR_RD_ENA|CO_FL_CURR_RD_POL);
+ fd_stop_recv(c->t.sock.fd);
+ }
+ else if (unlikely((f & (CO_FL_CURR_RD_ENA|CO_FL_CURR_RD_POL)) != (CO_FL_CURR_RD_ENA|CO_FL_CURR_RD_POL) &&
+ (f & (CO_FL_DATA_RD_ENA|CO_FL_WAIT_RD)) == (CO_FL_DATA_RD_ENA|CO_FL_WAIT_RD))) {
+ f |= (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))
+ }
+ else if (unlikely((f & (CO_FL_CURR_RD_ENA|CO_FL_DATA_RD_ENA)) == CO_FL_DATA_RD_ENA)) {
+ f |= 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))
+ if (unlikely((f & (CO_FL_CURR_WR_ENA|CO_FL_DATA_WR_ENA)) == CO_FL_CURR_WR_ENA)) {
+ f &= ~(CO_FL_CURR_WR_ENA|CO_FL_CURR_WR_POL);
+ fd_stop_send(c->t.sock.fd);
+ }
+ else if (unlikely((f & (CO_FL_CURR_WR_ENA|CO_FL_CURR_WR_POL)) != (CO_FL_CURR_WR_ENA|CO_FL_CURR_WR_POL) &&
+ (f & (CO_FL_DATA_WR_ENA|CO_FL_WAIT_WR)) == (CO_FL_DATA_WR_ENA|CO_FL_WAIT_WR))) {
+ f |= (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))
+ }
+ else if (unlikely((f & (CO_FL_CURR_WR_ENA|CO_FL_DATA_WR_ENA)) == CO_FL_DATA_WR_ENA)) {
+ f |= 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 = f;
+}
+
+/* Update polling on connection <c>'s file descriptor depending on its current
+ * state as reported in the connection's CO_FL_CURR_* flags, reports of EAGAIN
+ * in CO_FL_WAIT_*, and the sock layer expectations indicated by CO_FL_SOCK_*.
+ * The connection flags are updated with the new flags at the end of the
+ * operation.
+ */
+void conn_update_sock_polling(struct connection *c)
+{
+ unsigned int f = c->flags;
- 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);
+ /* update read status if needed */
+ if (unlikely((f & (CO_FL_CURR_RD_ENA|CO_FL_SOCK_RD_ENA)) == CO_FL_CURR_RD_ENA)) {
+ f &= ~(CO_FL_CURR_RD_ENA|CO_FL_CURR_RD_POL);
+ fd_stop_recv(c->t.sock.fd);
+ }
+ else if (unlikely((f & (CO_FL_CURR_RD_ENA|CO_FL_CURR_RD_POL)) != (CO_FL_CURR_RD_ENA|CO_FL_CURR_RD_POL) &&
+ (f & (CO_FL_SOCK_RD_ENA|CO_FL_WAIT_RD)) == (CO_FL_SOCK_RD_ENA|CO_FL_WAIT_RD))) {
+ f |= (CO_FL_CURR_RD_ENA|CO_FL_CURR_RD_POL);
+ fd_poll_recv(c->t.sock.fd);
+ }
+ else if (unlikely((f & (CO_FL_CURR_RD_ENA|CO_FL_SOCK_RD_ENA)) == CO_FL_SOCK_RD_ENA)) {
+ f |= CO_FL_CURR_RD_ENA;
+ fd_want_recv(c->t.sock.fd);
+ }
+
+ /* update write status if needed */
+ if (unlikely((f & (CO_FL_CURR_WR_ENA|CO_FL_SOCK_WR_ENA)) == CO_FL_CURR_WR_ENA)) {
+ f &= ~(CO_FL_CURR_WR_ENA|CO_FL_CURR_WR_POL);
+ fd_stop_send(c->t.sock.fd);
+ }
+ else if (unlikely((f & (CO_FL_CURR_WR_ENA|CO_FL_CURR_WR_POL)) != (CO_FL_CURR_WR_ENA|CO_FL_CURR_WR_POL) &&
+ (f & (CO_FL_SOCK_WR_ENA|CO_FL_WAIT_WR)) == (CO_FL_SOCK_WR_ENA|CO_FL_WAIT_WR))) {
+ f |= (CO_FL_CURR_WR_ENA|CO_FL_CURR_WR_POL);
+ fd_poll_send(c->t.sock.fd);
+ }
+ else if (unlikely((f & (CO_FL_CURR_WR_ENA|CO_FL_SOCK_WR_ENA)) == CO_FL_SOCK_WR_ENA)) {
+ f |= CO_FL_CURR_WR_ENA;
+ fd_want_send(c->t.sock.fd);
+ }
+ c->flags = f;
}