diff options
author | Azat Khuzhin <a3at.mail@gmail.com> | 2018-10-17 23:21:17 +0300 |
---|---|---|
committer | Azat Khuzhin <a3at.mail@gmail.com> | 2018-10-17 23:21:17 +0300 |
commit | 5a455acd3bdf9a0117f909450ac7ea693ca93912 (patch) | |
tree | d0cbcc7f803d93d0c89b90d600cc76a6e1ba8d2e /bufferevent.c | |
parent | a5b2ed56c3da47e4815bea9b20a5a97567b53dde (diff) | |
download | libevent-5a455acd3bdf9a0117f909450ac7ea693ca93912.tar.gz |
Fix hangs due to watermarks overruns in bufferevents implementations
Some implementations of bufferevents (for example openssl) can overrun
read high watermark.
And after this if user callback will not drain enough data it will be
suspended (i.e. it will not be runned again anymore).
This is not the expecting behaviour as one may guess, since in this case
the data will never be read. Hence once we detected that the watermark
exceeded (even after calling user callback) we will schedule the
callback again.
This also can be fixed in bufferevent openssl implementation (by
strictly limiting how much data is added to the read buffer according to
read high watermark), but since this data is already available (and in
memory) there is no point in doing so.
Diffstat (limited to 'bufferevent.c')
-rw-r--r-- | bufferevent.c | 25 |
1 files changed, 25 insertions, 0 deletions
diff --git a/bufferevent.c b/bufferevent.c index 490b5983..89ad6e2b 100644 --- a/bufferevent.c +++ b/bufferevent.c @@ -111,6 +111,28 @@ bufferevent_unsuspend_write_(struct bufferevent *bufev, bufferevent_suspend_flag BEV_UNLOCK(bufev); } +/** + * Sometimes bufferevent's implementation can overrun high watermarks + * (one of examples is openssl) and in this case if the read callback + * will not handle enough data do over condition above the read + * callback will never be called again (due to suspend above). + * + * To avoid this we are scheduling read callback again here, but only + * from the user callback to avoid multiple scheduling: + * - when the data had been added to it + * - when the data had been drained from it (user specified read callback) + */ +static void bufferevent_inbuf_wm_check(struct bufferevent *bev) +{ + if (!bev->wm_read.high) + return; + if (!(bev->enabled & EV_READ)) + return; + if (evbuffer_get_length(bev->input) < bev->wm_read.high) + return; + + bufferevent_trigger(bev, EV_READ, BEV_OPT_DEFER_CALLBACKS); +} /* Callback to implement watermarks on the input buffer. Only enabled * if the watermark is set. */ @@ -147,6 +169,7 @@ bufferevent_run_deferred_callbacks_locked(struct event_callback *cb, void *arg) if (bufev_private->readcb_pending && bufev->readcb) { bufev_private->readcb_pending = 0; bufev->readcb(bufev, bufev->cbarg); + bufferevent_inbuf_wm_check(bufev); } if (bufev_private->writecb_pending && bufev->writecb) { bufev_private->writecb_pending = 0; @@ -187,6 +210,7 @@ bufferevent_run_deferred_callbacks_unlocked(struct event_callback *cb, void *arg void *cbarg = bufev->cbarg; bufev_private->readcb_pending = 0; UNLOCKED(readcb(bufev, cbarg)); + bufferevent_inbuf_wm_check(bufev); } if (bufev_private->writecb_pending && bufev->writecb) { bufferevent_data_cb writecb = bufev->writecb; @@ -230,6 +254,7 @@ bufferevent_run_readcb_(struct bufferevent *bufev, int options) SCHEDULE_DEFERRED(p); } else { bufev->readcb(bufev, bufev->cbarg); + bufferevent_inbuf_wm_check(bufev); } } |