/* Callback to be used by connection I/O handlers upon completion. It propagates * connection flags to the stream interface, updates the stream (which may or * may not take this opportunity to try to forward data), then update the * connection's polling based on the channels and stream interface's final * states. The function always returns 0. */ static int si_conn_wake_cb(struct connection *conn) { struct stream_interface *si = conn->owner; struct channel *ic = si_ic(si); struct channel *oc = si_oc(si); /* First step, report to the stream-int what was detected at the * connection layer : errors and connection establishment. */ if (conn->flags & CO_FL_ERROR) si->flags |= SI_FL_ERR; if (unlikely(!(conn->flags & (CO_FL_WAIT_L4_CONN | CO_FL_WAIT_L6_CONN | CO_FL_CONNECTED)))) { si->exp = TICK_ETERNITY; oc->flags |= CF_WRITE_NULL; } /* Second step : update the stream-int and channels, try to forward any * pending data, then possibly wake the stream up based on the new * stream-int status. */ stream_int_notify(si); /* Third step : update the connection's polling status based on what * was done above (eg: maybe some buffers got emptied). */ if (channel_is_empty(oc)) __conn_data_stop_send(conn); if (si->flags & SI_FL_WAIT_ROOM) { __conn_data_stop_recv(conn); } else if ((ic->flags & (CF_SHUTR|CF_READ_PARTIAL|CF_DONT_READ)) == CF_READ_PARTIAL && channel_may_recv(ic)) { __conn_data_want_recv(conn); } return 0; }
/* Updates the polling status of a connection outside of the connection handler * based on the channel's flags and the stream interface's flags. It needs to be * called once after the channels' flags have settled down and the stream has * been updated. It is not designed to be called from within the connection * handler itself. */ void stream_int_update_conn(struct stream_interface *si) { struct channel *ic = si_ic(si); struct channel *oc = si_oc(si); struct connection *conn = __objt_conn(si->end); if (!(ic->flags & CF_SHUTR)) { /* Read not closed */ if ((ic->flags & CF_DONT_READ) || !channel_may_recv(ic)) __conn_data_stop_recv(conn); else __conn_data_want_recv(conn); } if (!(oc->flags & CF_SHUTW)) { /* Write not closed */ if (channel_is_empty(oc)) __conn_data_stop_send(conn); else __conn_data_want_send(conn); } conn_cond_update_data_polling(conn); }
/* This function is used for inter-stream-interface calls. It is called by the * producer to inform the consumer side that it may be interested in checking * for data in the buffer. Note that it intentionally does not update timeouts, * so that we can still check them later at wake-up. */ static void stream_int_chk_snd_conn(struct stream_interface *si) { struct channel *ob = si->ob; struct connection *conn = __objt_conn(si->end); if (unlikely(si->state > SI_ST_EST || (ob->flags & CF_SHUTW))) return; if (unlikely(channel_is_empty(ob))) /* called with nothing to send ! */ return; if (!ob->pipe && /* spliced data wants to be forwarded ASAP */ !(si->flags & SI_FL_WAIT_DATA)) /* not waiting for data */ return; if (conn->flags & (CO_FL_DATA_WR_ENA|CO_FL_CURR_WR_ENA)) { /* already subscribed to write notifications, will be called * anyway, so let's avoid calling it especially if the reader * is not ready. */ return; } /* Before calling the data-level operations, we have to prepare * the polling flags to ensure we properly detect changes. */ conn_refresh_polling_flags(conn); __conn_data_want_send(conn); if (!(conn->flags & (CO_FL_HANDSHAKE|CO_FL_WAIT_L4_CONN|CO_FL_WAIT_L6_CONN))) { si_conn_send(conn); if (conn->flags & CO_FL_ERROR) { /* Write error on the file descriptor */ __conn_data_stop_both(conn); si->flags |= SI_FL_ERR; goto out_wakeup; } } /* OK, so now we know that some data might have been sent, and that we may * have to poll first. We have to do that too if the buffer is not empty. */ if (channel_is_empty(ob)) { /* the connection is established but we can't write. Either the * buffer is empty, or we just refrain from sending because the * ->o limit was reached. Maybe we just wrote the last * chunk and need to close. */ __conn_data_stop_send(conn); if (((ob->flags & (CF_SHUTW|CF_AUTO_CLOSE|CF_SHUTW_NOW)) == (CF_AUTO_CLOSE|CF_SHUTW_NOW)) && (si->state == SI_ST_EST)) { si_shutw(si); goto out_wakeup; } if ((ob->flags & (CF_SHUTW|CF_SHUTW_NOW)) == 0) si->flags |= SI_FL_WAIT_DATA; ob->wex = TICK_ETERNITY; } else { /* Otherwise there are remaining data to be sent in the buffer, * which means we have to poll before doing so. */ __conn_data_want_send(conn); si->flags &= ~SI_FL_WAIT_DATA; if (!tick_isset(ob->wex)) ob->wex = tick_add_ifset(now_ms, ob->wto); } if (likely(ob->flags & CF_WRITE_ACTIVITY)) { /* update timeout if we have written something */ if ((ob->flags & (CF_SHUTW|CF_WRITE_PARTIAL)) == CF_WRITE_PARTIAL && !channel_is_empty(ob)) ob->wex = tick_add_ifset(now_ms, ob->wto); if (tick_isset(si->ib->rex) && !(si->flags & SI_FL_INDEP_STR)) { /* Note: to prevent the client from expiring read timeouts * during writes, we refresh it. We only do this if the * interface is not configured for "independent streams", * because for some applications it's better not to do this, * for instance when continuously exchanging small amounts * of data which can full the socket buffers long before a * write timeout is detected. */ si->ib->rex = tick_add_ifset(now_ms, si->ib->rto); } } /* in case of special condition (error, shutdown, end of write...), we * have to notify the task. */ if (likely((ob->flags & (CF_WRITE_NULL|CF_WRITE_ERROR|CF_SHUTW)) || ((ob->flags & CF_WAKE_WRITE) && ((channel_is_empty(si->ob) && !ob->to_forward) || si->state != SI_ST_EST)))) { out_wakeup: if (!(si->flags & SI_FL_DONT_WAKE) && si->owner) task_wakeup(si->owner, TASK_WOKEN_IO); } /* commit possible polling changes */ conn_cond_update_polling(conn); }
/* Callback to be used by connection I/O handlers upon completion. It differs from * the update function in that it is designed to be called by lower layers after I/O * events have been completed. It will also try to wake the associated task up if * an important event requires special handling. It relies on the connection handler * to commit any polling updates. The function always returns 0. */ static int si_conn_wake_cb(struct connection *conn) { struct stream_interface *si = conn->owner; DPRINTF(stderr, "%s: si=%p, si->state=%d ib->flags=%08x ob->flags=%08x\n", __FUNCTION__, si, si->state, si->ib->flags, si->ob->flags); if (conn->flags & CO_FL_ERROR) si->flags |= SI_FL_ERR; /* check for recent connection establishment */ if (unlikely(!(conn->flags & (CO_FL_WAIT_L4_CONN | CO_FL_WAIT_L6_CONN | CO_FL_CONNECTED)))) { si->exp = TICK_ETERNITY; si->ob->flags |= CF_WRITE_NULL; } /* process consumer side */ if (channel_is_empty(si->ob)) { if (((si->ob->flags & (CF_SHUTW|CF_SHUTW_NOW)) == CF_SHUTW_NOW) && (si->state == SI_ST_EST)) stream_int_shutw_conn(si); __conn_data_stop_send(conn); si->ob->wex = TICK_ETERNITY; } if ((si->ob->flags & (CF_SHUTW|CF_SHUTW_NOW)) == 0 && !channel_full(si->ob)) si->flags |= SI_FL_WAIT_DATA; if (si->ob->flags & CF_WRITE_ACTIVITY) { /* update timeouts if we have written something */ if ((si->ob->flags & (CF_SHUTW|CF_WRITE_PARTIAL)) == CF_WRITE_PARTIAL && !channel_is_empty(si->ob)) if (tick_isset(si->ob->wex)) si->ob->wex = tick_add_ifset(now_ms, si->ob->wto); if (!(si->flags & SI_FL_INDEP_STR)) if (tick_isset(si->ib->rex)) si->ib->rex = tick_add_ifset(now_ms, si->ib->rto); if (likely((si->ob->flags & (CF_SHUTW|CF_WRITE_PARTIAL|CF_DONT_READ)) == CF_WRITE_PARTIAL && !channel_full(si->ob) && (si->ob->prod->flags & SI_FL_WAIT_ROOM))) si_chk_rcv(si->ob->prod); } /* process producer side. * We might have some data the consumer is waiting for. * We can do fast-forwarding, but we avoid doing this for partial * buffers, because it is very likely that it will be done again * immediately afterwards once the following data is parsed (eg: * HTTP chunking). */ if (((si->ib->flags & CF_READ_PARTIAL) && !channel_is_empty(si->ib)) && (si->ib->pipe /* always try to send spliced data */ || (si->ib->buf->i == 0 && (si->ib->cons->flags & SI_FL_WAIT_DATA)))) { int last_len = si->ib->pipe ? si->ib->pipe->data : 0; si_chk_snd(si->ib->cons); /* check if the consumer has freed some space either in the * buffer or in the pipe. */ if (!channel_full(si->ib) && (!last_len || !si->ib->pipe || si->ib->pipe->data < last_len)) si->flags &= ~SI_FL_WAIT_ROOM; } if (si->flags & SI_FL_WAIT_ROOM) { __conn_data_stop_recv(conn); si->ib->rex = TICK_ETERNITY; } else if ((si->ib->flags & (CF_SHUTR|CF_READ_PARTIAL|CF_DONT_READ)) == CF_READ_PARTIAL && !channel_full(si->ib)) { /* we must re-enable reading if si_chk_snd() has freed some space */ __conn_data_want_recv(conn); if (!(si->ib->flags & CF_READ_NOEXP) && tick_isset(si->ib->rex)) si->ib->rex = tick_add_ifset(now_ms, si->ib->rto); } /* wake the task up only when needed */ if (/* changes on the production side */ (si->ib->flags & (CF_READ_NULL|CF_READ_ERROR)) || si->state != SI_ST_EST || (si->flags & SI_FL_ERR) || ((si->ib->flags & CF_READ_PARTIAL) && (!si->ib->to_forward || si->ib->cons->state != SI_ST_EST)) || /* changes on the consumption side */ (si->ob->flags & (CF_WRITE_NULL|CF_WRITE_ERROR)) || ((si->ob->flags & CF_WRITE_ACTIVITY) && ((si->ob->flags & CF_SHUTW) || ((si->ob->flags & CF_WAKE_WRITE) && (si->ob->prod->state != SI_ST_EST || (channel_is_empty(si->ob) && !si->ob->to_forward)))))) { task_wakeup(si->owner, TASK_WOKEN_IO); } if (si->ib->flags & CF_READ_ACTIVITY) si->ib->flags &= ~CF_READ_DONTWAIT; return 0; }