From: Willy Tarreau Date: Sun, 19 Apr 2015 15:20:03 +0000 (+0200) Subject: MAJOR: applet: now call si_applet_done() instead of si_update() in I/O handlers X-Git-Tag: v1.6-dev2~191 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=828824af05c142eaf8796865e9bce94d978a9951;p=thirdparty%2Fhaproxy.git MAJOR: applet: now call si_applet_done() instead of si_update() in I/O handlers The applet I/O handlers now rely on si_applet_done() which itself decides to wake up or sleep the appctx. Now it becomes critical that applte handlers properly call this on every exit path so that the appctx is removed from the active list after I/O have been handled. One such call was added to the Lua socket handler. It used to work without it probably because the main task is woken up by the parent task but now it's needed. --- diff --git a/src/dumpstats.c b/src/dumpstats.c index 0a2bd6747d..809025f2ba 100644 --- a/src/dumpstats.c +++ b/src/dumpstats.c @@ -2428,14 +2428,14 @@ static void cli_io_handler(struct appctx *appctx) res->flags |= CF_READ_NULL; } + out: /* update all other flags and resync with the other side */ - si_update(si); + si_applet_done(si); /* we don't want to expire timeouts while we're processing requests */ si_ic(si)->rex = TICK_ETERNITY; si_oc(si)->wex = TICK_ETERNITY; - out: DPRINTF(stderr, "%s@%d: st=%d, rqf=%x, rpf=%x, rqh=%d, rqs=%d, rh=%d, rs=%d\n", __FUNCTION__, __LINE__, si->state, req->flags, res->flags, req->buf->i, req->buf->o, res->buf->i, res->buf->o); @@ -4897,7 +4897,7 @@ static void http_stats_io_handler(struct appctx *appctx) if (bi_putchk(si_ic(si), &trash) == -1) { si->flags |= SI_FL_WAIT_ROOM; si_ic(si)->to_forward = last_fwd; - goto fail; + goto out; } } @@ -4948,7 +4948,7 @@ static void http_stats_io_handler(struct appctx *appctx) chunk_printf(&trash, "\r\n0\r\n\r\n"); if (bi_putchk(si_ic(si), &trash) == -1) { si->flags |= SI_FL_WAIT_ROOM; - goto fail; + goto out; } } /* eat the whole request */ @@ -4967,15 +4967,14 @@ static void http_stats_io_handler(struct appctx *appctx) } } - fail: + out: /* update all other flags and resync with the other side */ - si_update(si); + si_applet_done(si); /* we don't want to expire timeouts while we're processing requests */ si_ic(si)->rex = TICK_ETERNITY; si_oc(si)->wex = TICK_ETERNITY; - out: if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO)) { /* check that we have released everything then unregister */ stream_int_unregister_handler(si); diff --git a/src/hlua.c b/src/hlua.c index 68f0a3928e..fb006e6e1e 100644 --- a/src/hlua.c +++ b/src/hlua.c @@ -1446,11 +1446,11 @@ static void hlua_socket_handler(struct appctx *appctx) si_ic(si)->flags |= CF_READ_NULL; hlua_com_wake(&appctx->ctx.hlua.wake_on_read); hlua_com_wake(&appctx->ctx.hlua.wake_on_write); - return; + goto leave; } if (!(c->flags & CO_FL_CONNECTED)) - return; + goto leave; /* This function is called after the connect. */ appctx->ctx.hlua.connected = 1; @@ -1462,6 +1462,9 @@ static void hlua_socket_handler(struct appctx *appctx) /* Wake the tasks which wants to read if the buffer contains data. */ if (channel_is_empty(si_ic(si))) hlua_com_wake(&appctx->ctx.hlua.wake_on_read); + + leave: + si_applet_done(si); } /* This function is called when the "struct stream" is destroyed. @@ -1631,7 +1634,7 @@ __LJMP static int hlua_socket_receive_yield(struct lua_State *L, int status, lua bo_skip(oc, len + skip_at_end); /* Don't wait anything. */ - si_update(&socket->s->si[0]); + si_applet_done(&socket->s->si[0]); /* If the pattern reclaim to read all the data * in the connection, got out. @@ -1808,7 +1811,7 @@ static int hlua_socket_write_yield(struct lua_State *L,int status, lua_KContext } /* update buffers. */ - si_update(&socket->s->si[0]); + si_applet_done(&socket->s->si[0]); socket->s->req.rex = TICK_ETERNITY; socket->s->res.wex = TICK_ETERNITY; diff --git a/src/peers.c b/src/peers.c index 2a927e0da8..97a87289da 100644 --- a/src/peers.c +++ b/src/peers.c @@ -1038,17 +1038,16 @@ incomplete: si_shutw(si); si_shutr(si); si_ic(si)->flags |= CF_READ_NULL; - goto quit; + goto out; } } } out: - si_update(si); + si_applet_done(si); si_oc(si)->flags |= CF_READ_DONTWAIT; /* we don't want to expire timeouts while we're processing requests */ si_ic(si)->rex = TICK_ETERNITY; si_oc(si)->wex = TICK_ETERNITY; -quit: return; full: si->flags |= SI_FL_WAIT_ROOM; diff --git a/src/stream_interface.c b/src/stream_interface.c index 37dad7ec0d..2fdd94f6bd 100644 --- a/src/stream_interface.c +++ b/src/stream_interface.c @@ -394,6 +394,7 @@ struct appctx *stream_int_register_handler(struct stream_interface *si, struct a return NULL; si->flags |= SI_FL_WAIT_DATA; + appctx_wakeup(appctx); return si_appctx(si); } @@ -1569,6 +1570,8 @@ static void stream_int_shutr_applet(struct stream_interface *si) ic->rex = TICK_ETERNITY; si->flags &= ~SI_FL_WAIT_ROOM; + /* Note: on shutr, we don't call the applet */ + if (si->state != SI_ST_EST && si->state != SI_ST_CON) return; @@ -1581,10 +1584,6 @@ static void stream_int_shutr_applet(struct stream_interface *si) /* we want to immediately forward this close to the write side */ return stream_int_shutw_applet(si); } - - /* note that if the task exists, it must unregister itself once it runs */ - if (!(si->flags & SI_FL_DONT_WAKE)) - task_wakeup(si_task(si), TASK_WOKEN_IO); } /* @@ -1606,6 +1605,9 @@ static void stream_int_shutw_applet(struct stream_interface *si) oc->wex = TICK_ETERNITY; si->flags &= ~SI_FL_WAIT_DATA; + /* on shutw we always wake the applet up */ + appctx_wakeup(si_appctx(si)); + switch (si->state) { case SI_ST_EST: /* we have to shut before closing, otherwise some short messages @@ -1633,10 +1635,6 @@ static void stream_int_shutw_applet(struct stream_interface *si) ic->rex = TICK_ETERNITY; si->exp = TICK_ETERNITY; } - - /* note that if the task exists, it must unregister itself once it runs */ - if (!(si->flags & SI_FL_DONT_WAKE)) - task_wakeup(si_task(si), TASK_WOKEN_IO); } /* chk_rcv function for applets */ @@ -1650,17 +1648,14 @@ static void stream_int_chk_rcv_applet(struct stream_interface *si) if (unlikely(si->state != SI_ST_EST || (ic->flags & (CF_SHUTR|CF_DONT_READ)))) return; + /* here we only wake the applet up if it was waiting for some room */ + if (!(si->flags & SI_FL_WAIT_ROOM)) + return; - if (!channel_may_recv(ic) || ic->pipe) { - /* stop reading */ - si->flags |= SI_FL_WAIT_ROOM; - } - else { + if (channel_may_recv(ic) && !ic->pipe) { /* (re)start reading */ - si->flags &= ~SI_FL_WAIT_ROOM; - if (!(si->flags & SI_FL_DONT_WAKE)) - task_wakeup(si_task(si), TASK_WOKEN_IO); - } + appctx_wakeup(si_appctx(si)); + } } /* chk_snd function for applets */ @@ -1675,19 +1670,18 @@ static void stream_int_chk_snd_applet(struct stream_interface *si) 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 ! */ + /* we only wake the applet up if it was waiting for some data */ + + if (!(si->flags & SI_FL_WAIT_DATA)) 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); + if (!channel_is_empty(oc)) { + /* (re)start sending */ + appctx_wakeup(si_appctx(si)); + } } /*