Fix race condition between starting HTTP server thread and setting EventBase()

Split StartHTTPServer into InitHTTPServer and StartHTTPServer to give
clients a window to register their handlers without race conditions.

Thanks @ajweiss for figuring this out.
This commit is contained in:
Wladimir J. van der Laan 2015-08-28 16:55:16 +02:00
parent 6d2bc22146
commit 3a174cd400
3 changed files with 26 additions and 11 deletions

View file

@ -320,7 +320,7 @@ static void HTTPWorkQueueRun(WorkQueue<HTTPClosure>* queue)
queue->Run(); queue->Run();
} }
bool StartHTTPServer(boost::thread_group& threadGroup) bool InitHTTPServer()
{ {
struct evhttp* http = 0; struct evhttp* http = 0;
struct event_base* base = 0; struct event_base* base = 0;
@ -366,19 +366,25 @@ bool StartHTTPServer(boost::thread_group& threadGroup)
return false; return false;
} }
LogPrint("http", "Starting HTTP server\n"); LogPrint("http", "Initialized HTTP server\n");
int workQueueDepth = std::max((long)GetArg("-rpcworkqueue", DEFAULT_HTTP_WORKQUEUE), 1L); int workQueueDepth = std::max((long)GetArg("-rpcworkqueue", DEFAULT_HTTP_WORKQUEUE), 1L);
int rpcThreads = std::max((long)GetArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L); LogPrintf("HTTP: creating work queue of depth %d\n", workQueueDepth);
LogPrintf("HTTP: creating work queue of depth %d and %d worker threads\n", workQueueDepth, rpcThreads);
workQueue = new WorkQueue<HTTPClosure>(workQueueDepth);
threadGroup.create_thread(boost::bind(&ThreadHTTP, base, http)); workQueue = new WorkQueue<HTTPClosure>(workQueueDepth);
eventBase = base;
eventHTTP = http;
return true;
}
bool StartHTTPServer(boost::thread_group& threadGroup)
{
LogPrint("http", "Starting HTTP server\n");
int rpcThreads = std::max((long)GetArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L);
LogPrintf("HTTP: starting %d worker threads\n", rpcThreads);
threadGroup.create_thread(boost::bind(&ThreadHTTP, eventBase, eventHTTP));
for (int i = 0; i < rpcThreads; i++) for (int i = 0; i < rpcThreads; i++)
threadGroup.create_thread(boost::bind(&HTTPWorkQueueRun, workQueue)); threadGroup.create_thread(boost::bind(&HTTPWorkQueueRun, workQueue));
eventBase = base;
eventHTTP = http;
return true; return true;
} }

View file

@ -20,7 +20,14 @@ struct event_base;
class CService; class CService;
class HTTPRequest; class HTTPRequest;
/** Start HTTP server */ /** Initialize HTTP server.
* Call this before RegisterHTTPHandler or EventBase().
*/
bool InitHTTPServer();
/** Start HTTP server.
* This is separate from InitHTTPServer to give users race-condition-free time
* to register their handlers between InitHTTPServer and StartHTTPServer.
*/
bool StartHTTPServer(boost::thread_group& threadGroup); bool StartHTTPServer(boost::thread_group& threadGroup);
/** Interrupt HTTP server threads */ /** Interrupt HTTP server threads */
void InterruptHTTPServer(); void InterruptHTTPServer();

View file

@ -618,7 +618,7 @@ bool AppInitServers(boost::thread_group& threadGroup)
{ {
RPCServer::OnStopped(&OnRPCStopped); RPCServer::OnStopped(&OnRPCStopped);
RPCServer::OnPreCommand(&OnRPCPreCommand); RPCServer::OnPreCommand(&OnRPCPreCommand);
if (!StartHTTPServer(threadGroup)) if (!InitHTTPServer())
return false; return false;
if (!StartRPC()) if (!StartRPC())
return false; return false;
@ -626,6 +626,8 @@ bool AppInitServers(boost::thread_group& threadGroup)
return false; return false;
if (GetBoolArg("-rest", false) && !StartREST()) if (GetBoolArg("-rest", false) && !StartREST())
return false; return false;
if (!StartHTTPServer(threadGroup))
return false;
return true; return true;
} }