@@ -72,13 +72,35 @@ class WorkQueue
72
72
std::deque<WorkItem*> queue;
73
73
bool running;
74
74
size_t maxDepth;
75
+ int numThreads;
76
+
77
+ /* * RAII object to keep track of number of running worker threads */
78
+ class ThreadCounter
79
+ {
80
+ public:
81
+ WorkQueue &wq;
82
+ ThreadCounter (WorkQueue &w): wq(w)
83
+ {
84
+ boost::lock_guard<boost::mutex> lock (wq.cs );
85
+ wq.numThreads += 1 ;
86
+ }
87
+ ~ThreadCounter ()
88
+ {
89
+ boost::lock_guard<boost::mutex> lock (wq.cs );
90
+ wq.numThreads -= 1 ;
91
+ wq.cond .notify_all ();
92
+ }
93
+ };
75
94
76
95
public:
77
96
WorkQueue (size_t maxDepth) : running(true ),
78
- maxDepth (maxDepth)
97
+ maxDepth (maxDepth),
98
+ numThreads(0 )
79
99
{
80
100
}
81
- /* Precondition: worker threads have all stopped */
101
+ /* ( Precondition: worker threads have all stopped
102
+ * (call WaitExit)
103
+ */
82
104
~WorkQueue ()
83
105
{
84
106
while (!queue.empty ()) {
@@ -100,6 +122,7 @@ class WorkQueue
100
122
/* * Thread function */
101
123
void Run ()
102
124
{
125
+ ThreadCounter count (*this );
103
126
while (running) {
104
127
WorkItem* i = 0 ;
105
128
{
@@ -122,6 +145,13 @@ class WorkQueue
122
145
running = false ;
123
146
cond.notify_all ();
124
147
}
148
+ /* * Wait for worker threads to exit */
149
+ void WaitExit ()
150
+ {
151
+ boost::unique_lock<boost::mutex> lock (cs);
152
+ while (numThreads > 0 )
153
+ cond.wait (lock);
154
+ }
125
155
126
156
/* * Return current depth of queue */
127
157
size_t Depth ()
@@ -155,6 +185,8 @@ static std::vector<CSubNet> rpc_allow_subnets;
155
185
static WorkQueue<HTTPClosure>* workQueue = 0 ;
156
186
// ! Handlers for (sub)paths
157
187
std::vector<HTTPPathHandler> pathHandlers;
188
+ // ! Bound listening sockets
189
+ std::vector<evhttp_bound_socket *> boundSockets;
158
190
159
191
/* * Check if a network address is allowed to access the HTTP server */
160
192
static bool ClientAllowed (const CNetAddr& netaddr)
@@ -264,6 +296,13 @@ static void http_request_cb(struct evhttp_request* req, void* arg)
264
296
}
265
297
}
266
298
299
+ /* * Callback to reject HTTP requests after shutdown. */
300
+ static void http_reject_request_cb (struct evhttp_request * req, void *)
301
+ {
302
+ LogPrint (" http" , " Rejecting request while shutting down\n " );
303
+ evhttp_send_error (req, HTTP_SERVUNAVAIL, NULL );
304
+ }
305
+
267
306
/* * Event dispatcher thread */
268
307
static void ThreadHTTP (struct event_base * base, struct evhttp * http)
269
308
{
@@ -278,7 +317,6 @@ static void ThreadHTTP(struct event_base* base, struct evhttp* http)
278
317
static bool HTTPBindAddresses (struct evhttp * http)
279
318
{
280
319
int defaultPort = GetArg (" -rpcport" , BaseParams ().RPCPort ());
281
- int nBound = 0 ;
282
320
std::vector<std::pair<std::string, uint16_t > > endpoints;
283
321
284
322
// Determine what addresses to bind to
@@ -304,13 +342,14 @@ static bool HTTPBindAddresses(struct evhttp* http)
304
342
// Bind addresses
305
343
for (std::vector<std::pair<std::string, uint16_t > >::iterator i = endpoints.begin (); i != endpoints.end (); ++i) {
306
344
LogPrint (" http" , " Binding RPC on address %s port %i\n " , i->first , i->second );
307
- if (evhttp_bind_socket (http, i->first .empty () ? NULL : i->first .c_str (), i->second ) == 0 ) {
308
- nBound += 1 ;
345
+ evhttp_bound_socket *bind_handle = evhttp_bind_socket_with_handle (http, i->first .empty () ? NULL : i->first .c_str (), i->second );
346
+ if (bind_handle) {
347
+ boundSockets.push_back (bind_handle);
309
348
} else {
310
349
LogPrintf (" Binding RPC on address %s port %i failed.\n " , i->first , i->second );
311
350
}
312
351
}
313
- return nBound > 0 ;
352
+ return !boundSockets. empty () ;
314
353
}
315
354
316
355
/* * Simple wrapper to set thread name and run work queue */
@@ -410,16 +449,33 @@ bool StartHTTPServer(boost::thread_group& threadGroup)
410
449
void InterruptHTTPServer ()
411
450
{
412
451
LogPrint (" http" , " Interrupting HTTP server\n " );
413
- if (eventBase)
414
- event_base_loopbreak (eventBase);
452
+ if (eventHTTP) {
453
+ // Unlisten sockets
454
+ BOOST_FOREACH (evhttp_bound_socket *socket, boundSockets) {
455
+ evhttp_del_accept_socket (eventHTTP, socket);
456
+ }
457
+ // Reject requests on current connections
458
+ evhttp_set_gencb (eventHTTP, http_reject_request_cb, NULL );
459
+ }
460
+ if (eventBase) {
461
+ // Force-exit event loop after predefined time
462
+ struct timeval tv;
463
+ tv.tv_sec = 10 ;
464
+ tv.tv_usec = 0 ;
465
+ event_base_loopexit (eventBase, &tv);
466
+ }
415
467
if (workQueue)
416
468
workQueue->Interrupt ();
417
469
}
418
470
419
471
void StopHTTPServer ()
420
472
{
421
473
LogPrint (" http" , " Stopping HTTP server\n " );
422
- delete workQueue;
474
+ if (workQueue) {
475
+ LogPrint (" http" , " Waiting for HTTP worker threads to exit\n " );
476
+ workQueue->WaitExit ();
477
+ delete workQueue;
478
+ }
423
479
if (eventHTTP) {
424
480
evhttp_free (eventHTTP);
425
481
eventHTTP = 0 ;
0 commit comments