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
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)
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)
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)
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)
* - 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 */
*/
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 */
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,
+ /* 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 */
+
+ /* 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 */
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
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;
}