server-mode: Improve shutdown behavior

Add a signal handler to trigger shutdown and be more paranoid about
libuv doing things asynchronously.  This should fix test cases not
shutting down properly.
This commit is contained in:
Tobias Hunger 2016-10-04 13:28:38 +02:00 committed by Brad King
parent b99bbfe88d
commit 68277e16c4
3 changed files with 130 additions and 51 deletions

View File

@ -12,7 +12,7 @@
namespace { namespace {
void on_directory_change(uv_fs_event_t* handle, const char* filename, void on_directory_change(uv_fs_event_t* handle, const char* filename,
int events, int status); int events, int status);
void on_handle_close(uv_handle_t* handle); void on_fs_close(uv_handle_t* handle);
} // namespace } // namespace
class cmIBaseWatcher class cmIBaseWatcher
@ -177,7 +177,7 @@ public:
{ {
if (this->Handle) { if (this->Handle) {
uv_fs_event_stop(this->Handle); uv_fs_event_stop(this->Handle);
uv_close(reinterpret_cast<uv_handle_t*>(this->Handle), &on_handle_close); uv_close(reinterpret_cast<uv_handle_t*>(this->Handle), &on_fs_close);
this->Handle = nullptr; this->Handle = nullptr;
} }
cmVirtualDirectoryWatcher::StopWatching(); cmVirtualDirectoryWatcher::StopWatching();
@ -292,9 +292,9 @@ void on_directory_change(uv_fs_event_t* handle, const char* filename,
watcher->Trigger(pathSegment, events, status); watcher->Trigger(pathSegment, events, status);
} }
void on_handle_close(uv_handle_t* handle) void on_fs_close(uv_handle_t* handle)
{ {
delete (reinterpret_cast<uv_fs_event_t*>(handle)); delete reinterpret_cast<uv_fs_event_t*>(handle);
} }
} // namespace } // namespace

View File

@ -30,7 +30,7 @@ void on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf)
if (nread >= 0) { if (nread >= 0) {
conn->ReadData(std::string(buf->base, buf->base + nread)); conn->ReadData(std::string(buf->base, buf->base + nread));
} else { } else {
conn->HandleEof(); conn->TriggerShutdown();
} }
delete[](buf->base); delete[](buf->base);
@ -56,6 +56,28 @@ void on_new_connection(uv_stream_t* stream, int status)
conn->Connect(stream); conn->Connect(stream);
} }
void on_signal(uv_signal_t* signal, int signum)
{
auto conn = reinterpret_cast<cmServerConnection*>(signal->data);
(void)(signum);
conn->TriggerShutdown();
}
void on_signal_close(uv_handle_t* handle)
{
delete reinterpret_cast<uv_signal_t*>(handle);
}
void on_pipe_close(uv_handle_t* handle)
{
delete reinterpret_cast<uv_pipe_t*>(handle);
}
void on_tty_close(uv_handle_t* handle)
{
delete reinterpret_cast<uv_tty_t*>(handle);
}
} // namespace } // namespace
class LoopGuard class LoopGuard
@ -64,19 +86,25 @@ public:
LoopGuard(cmServerConnection* connection) LoopGuard(cmServerConnection* connection)
: Connection(connection) : Connection(connection)
{ {
Connection->mLoop = uv_default_loop(); this->Connection->mLoop = uv_default_loop();
if (Connection->mLoop) { if (!this->Connection->mLoop) {
Connection->mFileMonitor = new cmFileMonitor(Connection->mLoop); return;
} }
this->Connection->mFileMonitor =
new cmFileMonitor(this->Connection->mLoop);
} }
~LoopGuard() ~LoopGuard()
{ {
if (Connection->mFileMonitor) { if (!this->Connection->mLoop) {
delete Connection->mFileMonitor; return;
} }
uv_loop_close(Connection->mLoop);
Connection->mLoop = nullptr; if (this->Connection->mFileMonitor) {
delete this->Connection->mFileMonitor;
}
uv_loop_close(this->Connection->mLoop);
this->Connection->mLoop = nullptr;
} }
private: private:
@ -111,6 +139,16 @@ bool cmServerConnection::ProcessEvents(std::string* errorMessage)
return false; return false;
} }
this->SIGINTHandler = new uv_signal_t;
uv_signal_init(this->mLoop, this->SIGINTHandler);
this->SIGINTHandler->data = static_cast<void*>(this);
uv_signal_start(this->SIGINTHandler, &on_signal, SIGINT);
this->SIGHUPHandler = new uv_signal_t;
uv_signal_init(this->mLoop, this->SIGHUPHandler);
this->SIGHUPHandler->data = static_cast<void*>(this);
uv_signal_start(this->SIGHUPHandler, &on_signal, SIGHUP);
if (!DoSetup(errorMessage)) { if (!DoSetup(errorMessage)) {
return false; return false;
} }
@ -162,9 +200,21 @@ void cmServerConnection::ReadData(const std::string& data)
} }
} }
void cmServerConnection::HandleEof() void cmServerConnection::TriggerShutdown()
{ {
this->FileMonitor()->StopMonitoring(); this->FileMonitor()->StopMonitoring();
uv_signal_stop(this->SIGINTHandler);
uv_signal_stop(this->SIGHUPHandler);
uv_close(reinterpret_cast<uv_handle_t*>(this->SIGINTHandler),
&on_signal_close); // delete handle
uv_close(reinterpret_cast<uv_handle_t*>(this->SIGHUPHandler),
&on_signal_close); // delete handle
this->SIGINTHandler = nullptr;
this->SIGHUPHandler = nullptr;
this->TearDown(); this->TearDown();
} }
@ -194,30 +244,42 @@ void cmServerConnection::SendGreetings()
Server->PrintHello(); Server->PrintHello();
} }
cmServerStdIoConnection::cmServerStdIoConnection()
{
this->Input.tty = nullptr;
this->Output.tty = nullptr;
}
bool cmServerStdIoConnection::DoSetup(std::string* errorMessage) bool cmServerStdIoConnection::DoSetup(std::string* errorMessage)
{ {
(void)(errorMessage); (void)(errorMessage);
if (uv_guess_handle(1) == UV_TTY) { if (uv_guess_handle(1) == UV_TTY) {
uv_tty_init(this->Loop(), &this->Input.tty, 0, 1); usesTty = true;
uv_tty_set_mode(&this->Input.tty, UV_TTY_MODE_NORMAL); this->Input.tty = new uv_tty_t;
Input.tty.data = this; uv_tty_init(this->Loop(), this->Input.tty, 0, 1);
this->ReadStream = reinterpret_cast<uv_stream_t*>(&this->Input.tty); uv_tty_set_mode(this->Input.tty, UV_TTY_MODE_NORMAL);
Input.tty->data = this;
this->ReadStream = reinterpret_cast<uv_stream_t*>(this->Input.tty);
uv_tty_init(this->Loop(), &this->Output.tty, 1, 0); this->Output.tty = new uv_tty_t;
uv_tty_set_mode(&this->Output.tty, UV_TTY_MODE_NORMAL); uv_tty_init(this->Loop(), this->Output.tty, 1, 0);
Output.tty.data = this; uv_tty_set_mode(this->Output.tty, UV_TTY_MODE_NORMAL);
this->WriteStream = reinterpret_cast<uv_stream_t*>(&this->Output.tty); Output.tty->data = this;
this->WriteStream = reinterpret_cast<uv_stream_t*>(this->Output.tty);
} else { } else {
uv_pipe_init(this->Loop(), &this->Input.pipe, 0); usesTty = false;
uv_pipe_open(&this->Input.pipe, 0); this->Input.pipe = new uv_pipe_t;
Input.pipe.data = this; uv_pipe_init(this->Loop(), this->Input.pipe, 0);
this->ReadStream = reinterpret_cast<uv_stream_t*>(&this->Input.pipe); uv_pipe_open(this->Input.pipe, 0);
Input.pipe->data = this;
this->ReadStream = reinterpret_cast<uv_stream_t*>(this->Input.pipe);
uv_pipe_init(this->Loop(), &this->Output.pipe, 0); this->Output.pipe = new uv_pipe_t;
uv_pipe_open(&this->Output.pipe, 1); uv_pipe_init(this->Loop(), this->Output.pipe, 0);
Output.pipe.data = this; uv_pipe_open(this->Output.pipe, 1);
this->WriteStream = reinterpret_cast<uv_stream_t*>(&this->Output.pipe); Output.pipe->data = this;
this->WriteStream = reinterpret_cast<uv_stream_t*>(this->Output.pipe);
} }
SendGreetings(); SendGreetings();
@ -228,26 +290,35 @@ bool cmServerStdIoConnection::DoSetup(std::string* errorMessage)
void cmServerStdIoConnection::TearDown() void cmServerStdIoConnection::TearDown()
{ {
uv_close(reinterpret_cast<uv_handle_t*>(this->ReadStream), nullptr); if (usesTty) {
uv_close(reinterpret_cast<uv_handle_t*>(this->Input.tty), &on_tty_close);
uv_close(reinterpret_cast<uv_handle_t*>(this->Output.tty), &on_tty_close);
this->Input.tty = nullptr;
this->Output.tty = nullptr;
} else {
uv_close(reinterpret_cast<uv_handle_t*>(this->Input.pipe), &on_pipe_close);
uv_close(reinterpret_cast<uv_handle_t*>(this->Output.pipe),
&on_pipe_close);
this->Input.pipe = nullptr;
this->Input.pipe = nullptr;
}
this->ReadStream = nullptr; this->ReadStream = nullptr;
uv_close(reinterpret_cast<uv_handle_t*>(this->WriteStream), nullptr);
this->WriteStream = nullptr; this->WriteStream = nullptr;
} }
cmServerPipeConnection::cmServerPipeConnection(const std::string& name) cmServerPipeConnection::cmServerPipeConnection(const std::string& name)
: PipeName(name) : PipeName(name)
{ {
this->ServerPipe.data = nullptr;
this->ClientPipe.data = nullptr;
} }
bool cmServerPipeConnection::DoSetup(std::string* errorMessage) bool cmServerPipeConnection::DoSetup(std::string* errorMessage)
{ {
uv_pipe_init(this->Loop(), &this->ServerPipe, 0); this->ServerPipe = new uv_pipe_t;
this->ServerPipe.data = this; uv_pipe_init(this->Loop(), this->ServerPipe, 0);
this->ServerPipe->data = this;
int r; int r;
if ((r = uv_pipe_bind(&this->ServerPipe, this->PipeName.c_str())) != 0) { if ((r = uv_pipe_bind(this->ServerPipe, this->PipeName.c_str())) != 0) {
*errorMessage = std::string("Internal Error with ") + this->PipeName + *errorMessage = std::string("Internal Error with ") + this->PipeName +
": " + uv_err_name(r); ": " + uv_err_name(r);
return false; return false;
@ -265,31 +336,34 @@ bool cmServerPipeConnection::DoSetup(std::string* errorMessage)
void cmServerPipeConnection::TearDown() void cmServerPipeConnection::TearDown()
{ {
if (this->WriteStream->data) { if (this->ClientPipe) {
uv_close(reinterpret_cast<uv_handle_t*>(this->WriteStream), nullptr); uv_close(reinterpret_cast<uv_handle_t*>(this->ClientPipe), &on_pipe_close);
this->WriteStream->data = nullptr; this->WriteStream->data = nullptr;
} }
uv_close(reinterpret_cast<uv_handle_t*>(&this->ServerPipe), nullptr); uv_close(reinterpret_cast<uv_handle_t*>(&this->ServerPipe), &on_pipe_close);
this->ClientPipe = nullptr;
this->ServerPipe = nullptr;
this->WriteStream = nullptr; this->WriteStream = nullptr;
this->ReadStream = nullptr; this->ReadStream = nullptr;
} }
void cmServerPipeConnection::Connect(uv_stream_t* server) void cmServerPipeConnection::Connect(uv_stream_t* server)
{ {
if (this->ClientPipe.data == this) { if (this->ClientPipe) {
// Accept and close all pipes but the first: // Accept and close all pipes but the first:
uv_pipe_t rejectPipe; uv_pipe_t* rejectPipe = new uv_pipe_t;
uv_pipe_init(this->Loop(), &rejectPipe, 0); uv_pipe_init(this->Loop(), rejectPipe, 0);
auto rejecter = reinterpret_cast<uv_stream_t*>(&rejectPipe); auto rejecter = reinterpret_cast<uv_stream_t*>(rejectPipe);
uv_accept(server, rejecter); uv_accept(server, rejecter);
uv_close(reinterpret_cast<uv_handle_t*>(rejecter), nullptr); uv_close(reinterpret_cast<uv_handle_t*>(rejecter), &on_pipe_close);
return; return;
} }
uv_pipe_init(this->Loop(), &this->ClientPipe, 0); this->ClientPipe = new uv_pipe_t;
this->ClientPipe.data = this; uv_pipe_init(this->Loop(), this->ClientPipe, 0);
this->ClientPipe->data = this;
auto client = reinterpret_cast<uv_stream_t*>(&this->ClientPipe); auto client = reinterpret_cast<uv_stream_t*>(&this->ClientPipe);
if (uv_accept(server, client) != 0) { if (uv_accept(server, client) != 0) {
uv_close(reinterpret_cast<uv_handle_t*>(client), nullptr); uv_close(reinterpret_cast<uv_handle_t*>(client), nullptr);

View File

@ -24,7 +24,7 @@ public:
bool ProcessEvents(std::string* errorMessage); bool ProcessEvents(std::string* errorMessage);
void ReadData(const std::string& data); void ReadData(const std::string& data);
void HandleEof(); void TriggerShutdown();
void WriteData(const std::string& data); void WriteData(const std::string& data);
void ProcessNextRequest(); void ProcessNextRequest();
@ -51,6 +51,8 @@ private:
uv_loop_t* mLoop = nullptr; uv_loop_t* mLoop = nullptr;
cmFileMonitor* mFileMonitor = nullptr; cmFileMonitor* mFileMonitor = nullptr;
cmServer* Server = nullptr; cmServer* Server = nullptr;
uv_signal_t* SIGINTHandler = nullptr;
uv_signal_t* SIGHUPHandler = nullptr;
friend class LoopGuard; friend class LoopGuard;
}; };
@ -58,6 +60,7 @@ private:
class cmServerStdIoConnection : public cmServerConnection class cmServerStdIoConnection : public cmServerConnection
{ {
public: public:
cmServerStdIoConnection();
bool DoSetup(std::string* errorMessage) override; bool DoSetup(std::string* errorMessage) override;
void TearDown() override; void TearDown() override;
@ -65,10 +68,12 @@ public:
private: private:
typedef union typedef union
{ {
uv_tty_t tty; uv_tty_t* tty;
uv_pipe_t pipe; uv_pipe_t* pipe;
} InOutUnion; } InOutUnion;
bool usesTty = false;
InOutUnion Input; InOutUnion Input;
InOutUnion Output; InOutUnion Output;
}; };
@ -85,6 +90,6 @@ public:
private: private:
const std::string PipeName; const std::string PipeName;
uv_pipe_t ServerPipe; uv_pipe_t* ServerPipe = nullptr;
uv_pipe_t ClientPipe; uv_pipe_t* ClientPipe = nullptr;
}; };