/* This function is designed to be called from within the stream handler to * update the channels' expiration timers and the stream interface's flags * based on the channels' flags. It needs to be called only once after the * channels' flags have settled down, and before they are cleared, though it * doesn't harm to call it as often as desired (it just slightly hurts * performance). It must not be called from outside of the stream handler, * as what it does will be used to compute the stream task's expiration. */ void stream_int_update(struct stream_interface *si) { struct channel *ic = si_ic(si); struct channel *oc = si_oc(si); if (!(ic->flags & CF_SHUTR)) { /* Read not closed, update FD status and timeout for reads */ if ((ic->flags & CF_DONT_READ) || !channel_may_recv(ic)) { /* stop reading */ if (!(si->flags & SI_FL_WAIT_ROOM)) { if (!(ic->flags & CF_DONT_READ)) /* full */ si->flags |= SI_FL_WAIT_ROOM; ic->rex = TICK_ETERNITY; } } else { /* (re)start reading and update timeout. Note: we don't recompute the timeout * everytime we get here, otherwise it would risk never to expire. We only * update it if is was not yet set. The stream socket handler will already * have updated it if there has been a completed I/O. */ si->flags &= ~SI_FL_WAIT_ROOM; if (!(ic->flags & (CF_READ_NOEXP|CF_DONT_READ)) && !tick_isset(ic->rex)) ic->rex = tick_add_ifset(now_ms, ic->rto); } } if (!(oc->flags & CF_SHUTW)) { /* Write not closed, update FD status and timeout for writes */ if (channel_is_empty(oc)) { /* stop writing */ if (!(si->flags & SI_FL_WAIT_DATA)) { if ((oc->flags & CF_SHUTW_NOW) == 0) si->flags |= SI_FL_WAIT_DATA; oc->wex = TICK_ETERNITY; } } else { /* (re)start writing and update timeout. Note: we don't recompute the timeout * everytime we get here, otherwise it would risk never to expire. We only * update it if is was not yet set. The stream socket handler will already * have updated it if there has been a completed I/O. */ si->flags &= ~SI_FL_WAIT_DATA; if (!tick_isset(oc->wex)) { oc->wex = tick_add_ifset(now_ms, oc->wto); if (tick_isset(ic->rex) && !(si->flags & SI_FL_INDEP_STR)) { /* Note: depending on the protocol, we don't know if we're waiting * for incoming data or not. So in order to prevent the socket from * expiring read timeouts during writes, we refresh the read timeout, * except if it was already infinite or if we have explicitly setup * independent streams. */ ic->rex = tick_add_ifset(now_ms, ic->rto); } } } } }
/* default chk_snd function for scheduled tasks */ void stream_int_chk_snd(struct stream_interface *si) { struct buffer *ob = si->ob; 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 (unlikely(si->state != SI_ST_EST || (si->ob->flags & BF_SHUTW))) return; if (!(si->flags & SI_FL_WAIT_DATA) || /* not waiting for data */ (ob->flags & BF_OUT_EMPTY)) /* called with nothing to send ! */ return; /* Otherwise there are remaining data to be sent in the buffer, * so we tell the handler. */ si->flags &= ~SI_FL_WAIT_DATA; if (!tick_isset(ob->wex)) ob->wex = tick_add_ifset(now_ms, ob->wto); if (!(si->flags & SI_FL_DONT_WAKE) && si->owner) task_wakeup(si->owner, TASK_WOKEN_IO); }
/* default chk_snd function for scheduled tasks */ static void stream_int_chk_snd(struct stream_interface *si) { struct channel *oc = si_oc(si); DPRINTF(stderr, "%s: si=%p, si->state=%d ic->flags=%08x oc->flags=%08x\n", __FUNCTION__, si, si->state, si_ic(si)->flags, oc->flags); if (unlikely(si->state != SI_ST_EST || (oc->flags & CF_SHUTW))) return; if (!(si->flags & SI_FL_WAIT_DATA) || /* not waiting for data */ channel_is_empty(oc)) /* called with nothing to send ! */ return; /* Otherwise there are remaining data to be sent in the buffer, * so we tell the handler. */ si->flags &= ~SI_FL_WAIT_DATA; if (!tick_isset(oc->wex)) oc->wex = tick_add_ifset(now_ms, oc->wto); if (!(si->flags & SI_FL_DONT_WAKE)) task_wakeup(si_task(si), TASK_WOKEN_IO); }
/* chk_snd function for applets */ static void stream_int_chk_snd_applet(struct stream_interface *si) { struct channel *oc = si_oc(si); DPRINTF(stderr, "%s: si=%p, si->state=%d ic->flags=%08x oc->flags=%08x\n", __FUNCTION__, si, si->state, si_ic(si)->flags, oc->flags); if (unlikely(si->state != SI_ST_EST || (oc->flags & CF_SHUTW))) return; /* we only wake the applet up if it was waiting for some data */ if (!(si->flags & SI_FL_WAIT_DATA)) return; if (!tick_isset(oc->wex)) oc->wex = tick_add_ifset(now_ms, oc->wto); if (!channel_is_empty(oc)) { /* (re)start sending */ appctx_wakeup(si_appctx(si)); } }
/* default update function for embedded tasks, to be used at the end of the i/o handler */ void stream_int_update_embedded(struct stream_interface *si) { int old_flags = si->flags; 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 (si->state != SI_ST_EST) return; if ((si->ob->flags & (BF_OUT_EMPTY|BF_SHUTW|BF_HIJACK|BF_SHUTW_NOW)) == (BF_OUT_EMPTY|BF_SHUTW_NOW)) si->shutw(si); if ((si->ob->flags & (BF_FULL|BF_SHUTW|BF_SHUTW_NOW|BF_HIJACK)) == 0) si->flags |= SI_FL_WAIT_DATA; /* we're almost sure that we need some space if the buffer is not * empty, even if it's not full, because the applets can't fill it. */ if ((si->ib->flags & (BF_SHUTR|BF_OUT_EMPTY|BF_DONT_READ)) == 0) si->flags |= SI_FL_WAIT_ROOM; if (si->ob->flags & BF_WRITE_ACTIVITY) { if (tick_isset(si->ob->wex)) si->ob->wex = tick_add_ifset(now_ms, si->ob->wto); } if (si->ib->flags & BF_READ_ACTIVITY || (si->ob->flags & BF_WRITE_ACTIVITY && !(si->flags & SI_FL_INDEP_STR))) { if (tick_isset(si->ib->rex)) si->ib->rex = tick_add_ifset(now_ms, si->ib->rto); } /* save flags to detect changes */ old_flags = si->flags; if (likely((si->ob->flags & (BF_SHUTW|BF_WRITE_PARTIAL|BF_FULL|BF_DONT_READ)) == BF_WRITE_PARTIAL && (si->ob->prod->flags & SI_FL_WAIT_ROOM))) si->ob->prod->chk_rcv(si->ob->prod); if (((si->ib->flags & (BF_READ_PARTIAL|BF_OUT_EMPTY)) == BF_READ_PARTIAL) && (si->ib->cons->flags & SI_FL_WAIT_DATA)) { si->ib->cons->chk_snd(si->ib->cons); /* check if the consumer has freed some space */ if (!(si->ib->flags & BF_FULL)) si->flags &= ~SI_FL_WAIT_ROOM; } /* Note that we're trying to wake up in two conditions here : * - special event, which needs the holder task attention * - status indicating that the applet can go on working. This * is rather hard because we might be blocking on output and * don't want to wake up on input and vice-versa. The idea is * to only rely on the changes the chk_* might have performed. */ if (/* check stream interface changes */ ((old_flags & ~si->flags) & (SI_FL_WAIT_ROOM|SI_FL_WAIT_DATA)) || /* changes on the production side */ (si->ib->flags & (BF_READ_NULL|BF_READ_ERROR)) || si->state != SI_ST_EST || (si->flags & SI_FL_ERR) || ((si->ib->flags & BF_READ_PARTIAL) && (!si->ib->to_forward || si->ib->cons->state != SI_ST_EST)) || /* changes on the consumption side */ (si->ob->flags & (BF_WRITE_NULL|BF_WRITE_ERROR)) || ((si->ob->flags & BF_WRITE_ACTIVITY) && ((si->ob->flags & BF_SHUTW) || si->ob->prod->state != SI_ST_EST || ((si->ob->flags & BF_OUT_EMPTY) && !si->ob->to_forward)))) { if (!(si->flags & SI_FL_DONT_WAKE) && si->owner) task_wakeup(si->owner, TASK_WOKEN_IO); } if (si->ib->flags & BF_READ_ACTIVITY) si->ib->flags &= ~BF_READ_DONTWAIT; }
/* * Updates a connected stream_sock file descriptor status and timeouts * according to the buffers' flags. It should only be called once after the * buffer flags have settled down, and before they are cleared. It doesn't * harm to call it as often as desired (it just slightly hurts performance). */ void stream_sock_data_finish(struct stream_interface *si) { struct buffer *ib = si->ib; struct buffer *ob = si->ob; int fd = si->fd; DPRINTF(stderr,"[%u] %s: fd=%d owner=%p ib=%p, ob=%p, exp(r,w)=%u,%u ibf=%08x obf=%08x ibl=%d obl=%d si=%d\n", now_ms, __FUNCTION__, fd, fdtab[fd].owner, ib, ob, ib->rex, ob->wex, ib->flags, ob->flags, ib->l, ob->l, si->state); /* Check if we need to close the read side */ if (!(ib->flags & BF_SHUTR)) { /* Read not closed, update FD status and timeout for reads */ if (ib->flags & (BF_FULL|BF_HIJACK|BF_DONT_READ)) { /* stop reading */ if (!(si->flags & SI_FL_WAIT_ROOM)) { if ((ib->flags & (BF_FULL|BF_HIJACK|BF_DONT_READ)) == BF_FULL) si->flags |= SI_FL_WAIT_ROOM; EV_FD_COND_C(fd, DIR_RD); ib->rex = TICK_ETERNITY; } } else { /* (re)start reading and update timeout. Note: we don't recompute the timeout * everytime we get here, otherwise it would risk never to expire. We only * update it if is was not yet set. The stream socket handler will already * have updated it if there has been a completed I/O. */ si->flags &= ~SI_FL_WAIT_ROOM; EV_FD_COND_S(fd, DIR_RD); if (!(ib->flags & (BF_READ_NOEXP|BF_DONT_READ)) && !tick_isset(ib->rex)) ib->rex = tick_add_ifset(now_ms, ib->rto); } } /* Check if we need to close the write side */ if (!(ob->flags & BF_SHUTW)) { /* Write not closed, update FD status and timeout for writes */ if (ob->flags & BF_OUT_EMPTY) { /* stop writing */ if (!(si->flags & SI_FL_WAIT_DATA)) { if ((ob->flags & (BF_FULL|BF_HIJACK|BF_SHUTW_NOW)) == 0) si->flags |= SI_FL_WAIT_DATA; EV_FD_COND_C(fd, DIR_WR); ob->wex = TICK_ETERNITY; } } else { /* (re)start writing and update timeout. Note: we don't recompute the timeout * everytime we get here, otherwise it would risk never to expire. We only * update it if is was not yet set. The stream socket handler will already * have updated it if there has been a completed I/O. */ si->flags &= ~SI_FL_WAIT_DATA; EV_FD_COND_S(fd, DIR_WR); if (!tick_isset(ob->wex)) { ob->wex = tick_add_ifset(now_ms, ob->wto); if (tick_isset(ib->rex) && !(si->flags & SI_FL_INDEP_STR)) { /* Note: depending on the protocol, we don't know if we're waiting * for incoming data or not. So in order to prevent the socket from * expiring read timeouts during writes, we refresh the read timeout, * except if it was already infinite or if we have explicitly setup * independant streams. */ ib->rex = tick_add_ifset(now_ms, ib->rto); } } } } }
/* * This function is called on a write event from a stream socket. * It returns 0 if the caller needs to poll before calling it again, otherwise * non-zero. */ int stream_sock_write(int fd) { struct stream_interface *si = fdtab[fd].owner; struct buffer *b = si->ob; int retval = 1; #ifdef DEBUG_FULL fprintf(stderr,"stream_sock_write : fd=%d, owner=%p\n", fd, fdtab[fd].owner); #endif retval = 1; if (fdtab[fd].state == FD_STERROR) goto out_error; /* we might have been called just after an asynchronous shutw */ if (b->flags & BF_SHUTW) goto out_wakeup; if (likely(!(b->flags & BF_OUT_EMPTY) || si->send_proxy_ofs)) { /* OK there are data waiting to be sent */ retval = stream_sock_write_loop(si, b); if (retval < 0) goto out_error; else if (retval == 0 && si->send_proxy_ofs) goto out_may_wakeup; /* we failed to send the PROXY string */ } else { /* may be we have received a connection acknowledgement in TCP mode without data */ if (likely(fdtab[fd].state == FD_STCONN)) { /* We have no data to send to check the connection, and * getsockopt() will not inform us whether the connection * is still pending. So we'll reuse connect() to check the * state of the socket. This has the advantage of givig us * the following info : * - error * - connecting (EALREADY, EINPROGRESS) * - connected (EISCONN, 0) */ if ((connect(fd, fdinfo[fd].peeraddr, fdinfo[fd].peerlen) == 0)) errno = 0; if (errno == EALREADY || errno == EINPROGRESS) { retval = 0; goto out_may_wakeup; } if (errno && errno != EISCONN) goto out_error; /* OK we just need to indicate that we got a connection * and that we wrote nothing. */ b->flags |= BF_WRITE_NULL; fdtab[fd].state = FD_STREADY; } /* Funny, we were called to write something but there wasn't * anything. We can get there, for example if we were woken up * on a write event to finish the splice, but the send_max is 0 * so we cannot write anything from the buffer. Let's disable * the write event and pretend we never came there. */ } if (b->flags & BF_OUT_EMPTY) { /* the connection is established but we can't write. Either the * buffer is empty, or we just refrain from sending because the * send_max limit was reached. Maybe we just wrote the last * chunk and need to close. */ if (((b->flags & (BF_SHUTW|BF_HIJACK|BF_SHUTW_NOW)) == BF_SHUTW_NOW) && (si->state == SI_ST_EST)) { stream_sock_shutw(si); goto out_wakeup; } if ((b->flags & (BF_SHUTW|BF_SHUTW_NOW|BF_FULL|BF_HIJACK)) == 0) si->flags |= SI_FL_WAIT_DATA; EV_FD_CLR(fd, DIR_WR); b->wex = TICK_ETERNITY; } out_may_wakeup: if (b->flags & BF_WRITE_ACTIVITY) { /* update timeout if we have written something */ if ((b->flags & (BF_OUT_EMPTY|BF_SHUTW|BF_WRITE_PARTIAL)) == BF_WRITE_PARTIAL) b->wex = tick_add_ifset(now_ms, b->wto); out_wakeup: 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 "independant 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); } /* the producer might be waiting for more room to store data */ if (likely((b->flags & (BF_SHUTW|BF_WRITE_PARTIAL|BF_FULL|BF_DONT_READ)) == BF_WRITE_PARTIAL && (b->prod->flags & SI_FL_WAIT_ROOM))) b->prod->chk_rcv(b->prod); /* we have to wake up if there is a special event or if we don't have * any more data to forward and it's not planned to send any more. */ if (likely((b->flags & (BF_WRITE_NULL|BF_WRITE_ERROR|BF_SHUTW)) || ((b->flags & BF_OUT_EMPTY) && !b->to_forward) || si->state != SI_ST_EST || b->prod->state != SI_ST_EST)) task_wakeup(si->owner, TASK_WOKEN_IO); } fdtab[fd].ev &= ~FD_POLL_OUT; return retval; out_error: /* Write error on the file descriptor. We mark the FD as STERROR so * that we don't use it anymore. The error is reported to the stream * interface which will take proper action. We must not perturbate the * buffer because the stream interface wants to ensure transparent * connection retries. */ fdtab[fd].state = FD_STERROR; fdtab[fd].ev &= ~FD_POLL_STICKY; EV_FD_REM(fd); si->flags |= SI_FL_ERR; task_wakeup(si->owner, TASK_WOKEN_IO); return 1; }
/* 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. */ void stream_sock_chk_snd(struct stream_interface *si) { struct buffer *ob = si->ob; int retval; DPRINTF(stderr,"[%u] %s: fd=%d owner=%p ib=%p, ob=%p, exp(r,w)=%u,%u ibf=%08x obf=%08x ibl=%d obl=%d si=%d\n", now_ms, __FUNCTION__, si->fd, fdtab[si->fd].owner, si->ib, ob, si->ib->rex, ob->wex, si->ib->flags, ob->flags, si->ib->l, ob->l, si->state); if (unlikely(si->state != SI_ST_EST || (ob->flags & BF_SHUTW))) return; if (unlikely((ob->flags & BF_OUT_EMPTY) && !(si->send_proxy_ofs))) /* 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 */ (fdtab[si->fd].ev & FD_POLL_OUT))) /* we'll be called anyway */ return; retval = stream_sock_write_loop(si, ob); /* here, we have : * retval < 0 if an error was encountered during write. * retval = 0 if we can't write anymore without polling * retval = 1 if we're invited to come back when desired */ if (retval < 0) { /* Write error on the file descriptor. We mark the FD as STERROR so * that we don't use it anymore and we notify the task. */ fdtab[si->fd].state = FD_STERROR; fdtab[si->fd].ev &= ~FD_POLL_STICKY; EV_FD_REM(si->fd); si->flags |= SI_FL_ERR; goto out_wakeup; } else if (retval == 0 && si->send_proxy_ofs) goto out_may_wakeup; /* we failed to send the PROXY string */ /* OK, so now we know that retval >= 0 means 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 (ob->flags & BF_OUT_EMPTY) { /* the connection is established but we can't write. Either the * buffer is empty, or we just refrain from sending because the * send_max limit was reached. Maybe we just wrote the last * chunk and need to close. */ if (((ob->flags & (BF_SHUTW|BF_HIJACK|BF_AUTO_CLOSE|BF_SHUTW_NOW)) == (BF_AUTO_CLOSE|BF_SHUTW_NOW)) && (si->state == SI_ST_EST)) { stream_sock_shutw(si); goto out_wakeup; } if ((ob->flags & (BF_SHUTW|BF_SHUTW_NOW|BF_FULL|BF_HIJACK)) == 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. */ EV_FD_COND_S(si->fd, DIR_WR); si->flags &= ~SI_FL_WAIT_DATA; if (!tick_isset(ob->wex)) ob->wex = tick_add_ifset(now_ms, ob->wto); } out_may_wakeup: if (likely(ob->flags & BF_WRITE_ACTIVITY)) { /* update timeout if we have written something */ if ((ob->flags & (BF_OUT_EMPTY|BF_SHUTW|BF_WRITE_PARTIAL)) == BF_WRITE_PARTIAL) 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 "independant 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 & (BF_WRITE_NULL|BF_WRITE_ERROR|BF_SHUTW)) || ((ob->flags & BF_OUT_EMPTY) && !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); } }
REGPRM3 static void _do_poll(struct poller *p, int exp, int wake) { int i; int wait_time; struct timespec timeout_ts; unsigned int nevlist; int fd, old_fd; int status; /* * Scan the list of file descriptors with an updated status: */ for (i = 0; i < fd_nbupdt; i++) { fd = fd_updt[i]; _HA_ATOMIC_AND(&fdtab[fd].update_mask, ~tid_bit); if (fdtab[fd].owner == NULL) { activity[tid].poll_drop++; continue; } _update_fd(fd); } fd_nbupdt = 0; /* Scan the global update list */ for (old_fd = fd = update_list.first; fd != -1; fd = fdtab[fd].update.next) { if (fd == -2) { fd = old_fd; continue; } else if (fd <= -3) fd = -fd -4; if (fd == -1) break; if (fdtab[fd].update_mask & tid_bit) done_update_polling(fd); else continue; if (!fdtab[fd].owner) continue; _update_fd(fd); } thread_harmless_now(); /* * Determine how long to wait for events to materialise on the port. */ wait_time = wake ? 0 : compute_poll_timeout(exp); tv_entering_poll(); activity_count_runtime(); do { int timeout = (global.tune.options & GTUNE_BUSY_POLLING) ? 0 : wait_time; int interrupted = 0; nevlist = 1; /* desired number of events to be retrieved */ timeout_ts.tv_sec = (timeout / 1000); timeout_ts.tv_nsec = (timeout % 1000) * 1000000; status = port_getn(evports_fd[tid], evports_evlist, evports_evlist_max, &nevlist, /* updated to the number of events retrieved */ &timeout_ts); if (status != 0) { int e = errno; switch (e) { case ETIME: /* * Though the manual page has not historically made it * clear, port_getn() can return -1 with an errno of * ETIME and still have returned some number of events. */ /* nevlist >= 0 */ break; default: nevlist = 0; interrupted = 1; break; } } tv_update_date(timeout, nevlist); if (nevlist || interrupted) break; if (timeout || !wait_time) break; if (signal_queue_len || wake) break; if (tick_isset(exp) && tick_is_expired(exp, now_ms)) break; } while(1); tv_leaving_poll(wait_time, nevlist); thread_harmless_end(); for (i = 0; i < nevlist; i++) { unsigned int n = 0; int events, rebind_events; fd = evports_evlist[i].portev_object; events = evports_evlist[i].portev_events; if (fdtab[fd].owner == NULL) { activity[tid].poll_dead++; continue; } if (!(fdtab[fd].thread_mask & tid_bit)) { activity[tid].poll_skip++; continue; } /* * By virtue of receiving an event for this file descriptor, it * is no longer associated with the port in question. Store * the previous event mask so that we may reassociate after * processing is complete. */ rebind_events = evports_state_to_events(fdtab[fd].state); /* rebind_events != 0 */ /* * Set bits based on the events we received from the port: */ if (events & POLLIN) n |= FD_POLL_IN; if (events & POLLOUT) n |= FD_POLL_OUT; if (events & POLLERR) n |= FD_POLL_ERR; if (events & POLLHUP) n |= FD_POLL_HUP; /* * Call connection processing callbacks. Note that it's * possible for this processing to alter the required event * port assocation; i.e., the "state" member of the "fdtab" * entry. If it changes, the fd will be placed on the updated * list for processing the next time we are called. */ fd_update_events(fd, n); /* * This file descriptor was closed during the processing of * polled events. No need to reassociate. */ if (fdtab[fd].owner == NULL) continue; /* * Reassociate with the port, using the same event mask as * before. This call will not result in a dissociation as we * asserted that _some_ events needed to be rebound above. * * Reassociating with the same mask allows us to mimic the * level-triggered behaviour of poll(2). In the event that we * are interested in the same events on the next turn of the * loop, this represents no extra work. * * If this additional port_associate(3C) call becomes a * performance problem, we would need to verify that we can * correctly interact with the file descriptor cache and update * list (see "src/fd.c") to avoid reassociating here, or to use * a different events mask. */ evports_resync_fd(fd, rebind_events); } }
/* 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; }
/* This function is the equivalent to stream_int_update() except that it's * designed to be called from outside the stream handlers, typically the lower * layers (applets, connections) after I/O completion. After updating the stream * interface and timeouts, it will try to forward what can be forwarded, then to * wake the associated task up if an important event requires special handling. * It should not be called from within the stream itself, stream_int_update() * is designed for this. */ void stream_int_notify(struct stream_interface *si) { struct channel *ic = si_ic(si); struct channel *oc = si_oc(si); /* process consumer side */ if (channel_is_empty(oc)) { if (((oc->flags & (CF_SHUTW|CF_SHUTW_NOW)) == CF_SHUTW_NOW) && (si->state == SI_ST_EST)) si_shutw(si); oc->wex = TICK_ETERNITY; } /* indicate that we may be waiting for data from the output channel */ if ((oc->flags & (CF_SHUTW|CF_SHUTW_NOW)) == 0 && channel_may_recv(oc)) si->flags |= SI_FL_WAIT_DATA; /* update OC timeouts and wake the other side up if it's waiting for room */ if (oc->flags & CF_WRITE_ACTIVITY) { if ((oc->flags & (CF_SHUTW|CF_WRITE_PARTIAL)) == CF_WRITE_PARTIAL && !channel_is_empty(oc)) if (tick_isset(oc->wex)) oc->wex = tick_add_ifset(now_ms, oc->wto); if (!(si->flags & SI_FL_INDEP_STR)) if (tick_isset(ic->rex)) ic->rex = tick_add_ifset(now_ms, ic->rto); if (likely((oc->flags & (CF_SHUTW|CF_WRITE_PARTIAL|CF_DONT_READ)) == CF_WRITE_PARTIAL && channel_may_recv(oc) && (si_opposite(si)->flags & SI_FL_WAIT_ROOM))) si_chk_rcv(si_opposite(si)); } /* Notify the other side when we've injected data into the IC that * needs to be forwarded. We can do fast-forwarding as soon as there * are output data, but we avoid doing this if some of the data are * not yet scheduled for being forwarded, because it is very likely * that it will be done again immediately afterwards once the following * data are parsed (eg: HTTP chunking). We only SI_FL_WAIT_ROOM once * we've emptied *some* of the output buffer, and not just when there * is available room, because applets are often forced to stop before * the buffer is full. We must not stop based on input data alone because * an HTTP parser might need more data to complete the parsing. */ if (!channel_is_empty(ic) && (si_opposite(si)->flags & SI_FL_WAIT_DATA) && (ic->buf->i == 0 || ic->pipe)) { int new_len, last_len; last_len = ic->buf->o; if (ic->pipe) last_len += ic->pipe->data; si_chk_snd(si_opposite(si)); new_len = ic->buf->o; if (ic->pipe) new_len += ic->pipe->data; /* check if the consumer has freed some space either in the * buffer or in the pipe. */ if (channel_may_recv(ic) && new_len < last_len) si->flags &= ~SI_FL_WAIT_ROOM; } if (si->flags & SI_FL_WAIT_ROOM) { ic->rex = TICK_ETERNITY; } else if ((ic->flags & (CF_SHUTR|CF_READ_PARTIAL|CF_DONT_READ)) == CF_READ_PARTIAL && channel_may_recv(ic)) { /* we must re-enable reading if si_chk_snd() has freed some space */ if (!(ic->flags & CF_READ_NOEXP) && tick_isset(ic->rex)) ic->rex = tick_add_ifset(now_ms, ic->rto); } /* wake the task up only when needed */ if (/* changes on the production side */ (ic->flags & (CF_READ_NULL|CF_READ_ERROR)) || si->state != SI_ST_EST || (si->flags & SI_FL_ERR) || ((ic->flags & CF_READ_PARTIAL) && (!ic->to_forward || si_opposite(si)->state != SI_ST_EST)) || /* changes on the consumption side */ (oc->flags & (CF_WRITE_NULL|CF_WRITE_ERROR)) || ((oc->flags & CF_WRITE_ACTIVITY) && ((oc->flags & CF_SHUTW) || ((oc->flags & CF_WAKE_WRITE) && (si_opposite(si)->state != SI_ST_EST || (channel_is_empty(oc) && !oc->to_forward)))))) { task_wakeup(si_task(si), TASK_WOKEN_IO); } if (ic->flags & CF_READ_ACTIVITY) ic->flags &= ~CF_READ_DONTWAIT; stream_release_buffers(si_strm(si)); }
/* default update function for embedded tasks, to be used at the end of the i/o handler */ static void stream_int_update_embedded(struct stream_interface *si) { int old_flags = si->flags; struct channel *ic = si_ic(si); struct channel *oc = si_oc(si); DPRINTF(stderr, "%s: si=%p, si->state=%d ic->flags=%08x oc->flags=%08x\n", __FUNCTION__, si, si->state, ic->flags, oc->flags); if (si->state != SI_ST_EST) return; if ((oc->flags & (CF_SHUTW|CF_SHUTW_NOW)) == CF_SHUTW_NOW && channel_is_empty(oc)) si_shutw(si); if ((oc->flags & (CF_SHUTW|CF_SHUTW_NOW)) == 0 && channel_may_recv(oc)) si->flags |= SI_FL_WAIT_DATA; /* we're almost sure that we need some space if the buffer is not * empty, even if it's not full, because the applets can't fill it. */ if ((ic->flags & (CF_SHUTR|CF_DONT_READ)) == 0 && !channel_is_empty(ic)) si->flags |= SI_FL_WAIT_ROOM; if (oc->flags & CF_WRITE_ACTIVITY) { if (tick_isset(oc->wex)) oc->wex = tick_add_ifset(now_ms, oc->wto); } if (ic->flags & CF_READ_ACTIVITY || (oc->flags & CF_WRITE_ACTIVITY && !(si->flags & SI_FL_INDEP_STR))) { if (tick_isset(ic->rex)) ic->rex = tick_add_ifset(now_ms, ic->rto); } /* save flags to detect changes */ old_flags = si->flags; if (likely((oc->flags & (CF_SHUTW|CF_WRITE_PARTIAL|CF_DONT_READ)) == CF_WRITE_PARTIAL && channel_may_recv(oc) && (si_opposite(si)->flags & SI_FL_WAIT_ROOM))) si_chk_rcv(si_opposite(si)); if (((ic->flags & CF_READ_PARTIAL) && !channel_is_empty(ic)) && (ic->pipe /* always try to send spliced data */ || (ic->buf->i == 0 && (si_opposite(si)->flags & SI_FL_WAIT_DATA)))) { si_chk_snd(si_opposite(si)); /* check if the consumer has freed some space */ if (channel_may_recv(ic) && !ic->pipe) si->flags &= ~SI_FL_WAIT_ROOM; } /* Note that we're trying to wake up in two conditions here : * - special event, which needs the holder task attention * - status indicating that the applet can go on working. This * is rather hard because we might be blocking on output and * don't want to wake up on input and vice-versa. The idea is * to only rely on the changes the chk_* might have performed. */ if (/* check stream interface changes */ ((old_flags & ~si->flags) & (SI_FL_WAIT_ROOM|SI_FL_WAIT_DATA)) || /* changes on the production side */ (ic->flags & (CF_READ_NULL|CF_READ_ERROR)) || si->state != SI_ST_EST || (si->flags & SI_FL_ERR) || ((ic->flags & CF_READ_PARTIAL) && (!ic->to_forward || si_opposite(si)->state != SI_ST_EST)) || /* changes on the consumption side */ (oc->flags & (CF_WRITE_NULL|CF_WRITE_ERROR)) || ((oc->flags & CF_WRITE_ACTIVITY) && ((oc->flags & CF_SHUTW) || ((oc->flags & CF_WAKE_WRITE) && (si_opposite(si)->state != SI_ST_EST || (channel_is_empty(oc) && !oc->to_forward)))))) { if (!(si->flags & SI_FL_DONT_WAKE)) task_wakeup(si_task(si), TASK_WOKEN_IO); } if (ic->flags & CF_READ_ACTIVITY) ic->flags &= ~CF_READ_DONTWAIT; }