Browse Source

rpc: work-around an upstream libevent bug

A rare race condition may trigger while awaiting the body of a message, see
upsteam commit 5ff8eb26371c4dc56f384b2de35bea2d87814779 for details.

This may fix some reported rpc hangs/crashes.
0.16
Cory Fields 7 years ago
parent
commit
6b58360f9b
  1. 28
      src/httpserver.cpp

28
src/httpserver.cpp

@ -24,6 +24,7 @@
#include <event2/thread.h> #include <event2/thread.h>
#include <event2/buffer.h> #include <event2/buffer.h>
#include <event2/bufferevent.h>
#include <event2/util.h> #include <event2/util.h>
#include <event2/keyvalq_struct.h> #include <event2/keyvalq_struct.h>
@ -239,6 +240,16 @@ static std::string RequestMethodString(HTTPRequest::RequestMethod m)
/** HTTP request callback */ /** HTTP request callback */
static void http_request_cb(struct evhttp_request* req, void* arg) static void http_request_cb(struct evhttp_request* req, void* arg)
{ {
// Disable reading to work around a libevent bug, fixed in 2.2.0.
if (event_get_version_number() < 0x02020001) {
evhttp_connection* conn = evhttp_request_get_connection(req);
if (conn) {
bufferevent* bev = evhttp_connection_get_bufferevent(conn);
if (bev) {
bufferevent_disable(bev, EV_READ);
}
}
}
std::unique_ptr<HTTPRequest> hreq(new HTTPRequest(req)); std::unique_ptr<HTTPRequest> hreq(new HTTPRequest(req));
LogPrint(BCLog::HTTP, "Received a %s request for %s from %s\n", LogPrint(BCLog::HTTP, "Received a %s request for %s from %s\n",
@ -601,8 +612,21 @@ void HTTPRequest::WriteReply(int nStatus, const std::string& strReply)
struct evbuffer* evb = evhttp_request_get_output_buffer(req); struct evbuffer* evb = evhttp_request_get_output_buffer(req);
assert(evb); assert(evb);
evbuffer_add(evb, strReply.data(), strReply.size()); evbuffer_add(evb, strReply.data(), strReply.size());
HTTPEvent* ev = new HTTPEvent(eventBase, true, auto req_copy = req;
std::bind(evhttp_send_reply, req, nStatus, (const char*)nullptr, (struct evbuffer *)nullptr)); HTTPEvent* ev = new HTTPEvent(eventBase, true, [req_copy, nStatus]{
evhttp_send_reply(req_copy, nStatus, nullptr, nullptr);
// Re-enable reading from the socket. This is the second part of the libevent
// workaround above.
if (event_get_version_number() < 0x02020001) {
evhttp_connection* conn = evhttp_request_get_connection(req_copy);
if (conn) {
bufferevent* bev = evhttp_connection_get_bufferevent(conn);
if (bev) {
bufferevent_enable(bev, EV_READ | EV_WRITE);
}
}
}
});
ev->trigger(nullptr); ev->trigger(nullptr);
replySent = true; replySent = true;
req = nullptr; // transferred back to main thread req = nullptr; // transferred back to main thread

Loading…
Cancel
Save