Skip to content

Commit 86bacd7

Browse files
committed
Merge bitcoin/bitcoin#26742: http: Track active requests and wait for last to finish - 2nd attempt
60978c8 test: Reduce extended timeout on abortnode test (Fabian Jahr) 660bdbf http: Release server before waiting for event base loop exit (João Barbosa) 8c6d007 http: Track active requests and wait for last to finish (João Barbosa) Pull request description: This revives #19420. Since promag is not so active at the moment, I can support this to finally get it merged. The PR is rebased and comments by jonatack have been addressed. Once this is merged, I will also reopen #19434. ACKs for top commit: achow101: ACK 60978c8 stickies-v: re-ACK [60978c8](bitcoin/bitcoin@60978c8) hebasto: ACK 60978c8 Tree-SHA512: eef0fe1081e9331b95cfafc71d82f2398abd1d3439dac5b2fa5c6d9c0a3f63ef19adde1c38c88d3b4e7fb41ce7c097943f1815c10e33d165918ccbdec512fe1c
2 parents 4ea3a8b + 60978c8 commit 86bacd7

File tree

2 files changed

+36
-7
lines changed

2 files changed

+36
-7
lines changed

src/httpserver.cpp

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,22 @@
2121
#include <util/threadnames.h>
2222
#include <util/translation.h>
2323

24+
#include <condition_variable>
2425
#include <cstdio>
2526
#include <cstdlib>
2627
#include <deque>
2728
#include <memory>
2829
#include <optional>
2930
#include <string>
31+
#include <unordered_set>
3032

3133
#include <sys/types.h>
3234
#include <sys/stat.h>
3335

3436
#include <event2/buffer.h>
3537
#include <event2/bufferevent.h>
3638
#include <event2/http.h>
39+
#include <event2/http_struct.h>
3740
#include <event2/keyvalq_struct.h>
3841
#include <event2/thread.h>
3942
#include <event2/util.h>
@@ -146,6 +149,10 @@ static GlobalMutex g_httppathhandlers_mutex;
146149
static std::vector<HTTPPathHandler> pathHandlers GUARDED_BY(g_httppathhandlers_mutex);
147150
//! Bound listening sockets
148151
static std::vector<evhttp_bound_socket *> boundSockets;
152+
//! Track active requests
153+
static GlobalMutex g_requests_mutex;
154+
static std::condition_variable g_requests_cv;
155+
static std::unordered_set<evhttp_request*> g_requests GUARDED_BY(g_requests_mutex);
149156

150157
/** Check if a network address is allowed to access the HTTP server */
151158
static bool ClientAllowed(const CNetAddr& netaddr)
@@ -207,6 +214,17 @@ std::string RequestMethodString(HTTPRequest::RequestMethod m)
207214
/** HTTP request callback */
208215
static void http_request_cb(struct evhttp_request* req, void* arg)
209216
{
217+
// Track requests and notify when a request is completed.
218+
{
219+
WITH_LOCK(g_requests_mutex, g_requests.insert(req));
220+
g_requests_cv.notify_all();
221+
evhttp_request_set_on_complete_cb(req, [](struct evhttp_request* req, void*) {
222+
auto n{WITH_LOCK(g_requests_mutex, return g_requests.erase(req))};
223+
assert(n == 1);
224+
g_requests_cv.notify_all();
225+
}, nullptr);
226+
}
227+
210228
// Disable reading to work around a libevent bug, fixed in 2.2.0.
211229
if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) {
212230
evhttp_connection* conn = evhttp_request_get_connection(req);
@@ -458,15 +476,27 @@ void StopHTTPServer()
458476
evhttp_del_accept_socket(eventHTTP, socket);
459477
}
460478
boundSockets.clear();
461-
if (eventBase) {
462-
LogPrint(BCLog::HTTP, "Waiting for HTTP event thread to exit\n");
463-
if (g_thread_http.joinable()) g_thread_http.join();
479+
{
480+
WAIT_LOCK(g_requests_mutex, lock);
481+
if (!g_requests.empty()) {
482+
LogPrint(BCLog::HTTP, "Waiting for %d requests to stop HTTP server\n", g_requests.size());
483+
}
484+
g_requests_cv.wait(lock, []() EXCLUSIVE_LOCKS_REQUIRED(g_requests_mutex) {
485+
return g_requests.empty();
486+
});
464487
}
465488
if (eventHTTP) {
466-
evhttp_free(eventHTTP);
467-
eventHTTP = nullptr;
489+
// Schedule a callback to call evhttp_free in the event base thread, so
490+
// that evhttp_free does not need to be called again after the handling
491+
// of unfinished request connections that follows.
492+
event_base_once(eventBase, -1, EV_TIMEOUT, [](evutil_socket_t, short, void*) {
493+
evhttp_free(eventHTTP);
494+
eventHTTP = nullptr;
495+
}, nullptr, nullptr);
468496
}
469497
if (eventBase) {
498+
LogPrint(BCLog::HTTP, "Waiting for HTTP event thread to exit\n");
499+
if (g_thread_http.joinable()) g_thread_http.join();
470500
event_base_free(eventBase);
471501
eventBase = nullptr;
472502
}

test/functional/feature_abortnode.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ class AbortNodeTest(BitcoinTestFramework):
1919
def set_test_params(self):
2020
self.setup_clean_chain = True
2121
self.num_nodes = 2
22-
self.rpc_timeout = 240
2322

2423
def setup_network(self):
2524
self.setup_nodes()
@@ -41,7 +40,7 @@ def run_test(self):
4140

4241
# Check that node0 aborted
4342
self.log.info("Waiting for crash")
44-
self.nodes[0].wait_until_stopped(timeout=200)
43+
self.nodes[0].wait_until_stopped(timeout=5)
4544
self.log.info("Node crashed - now verifying restart fails")
4645
self.nodes[0].assert_start_raises_init_error()
4746

0 commit comments

Comments
 (0)