-
Notifications
You must be signed in to change notification settings - Fork 83
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Extract SignalHandler class for re-use (#282)
In preparation of sharing functionality for signal handling, extract what we have right now into a `SignalHandler` class. Preparation for #280 Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
- Loading branch information
Showing
5 changed files
with
133 additions
and
53 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
#include "common/signal_handler.h" | ||
|
||
#include <csignal> | ||
|
||
#include "external/envoy/source/common/common/assert.h" | ||
#include "external/envoy/source/common/common/macros.h" | ||
|
||
namespace Nighthawk { | ||
|
||
namespace { | ||
std::function<void(int)> signal_handler_delegate; | ||
void signal_handler(int signal) { signal_handler_delegate(signal); } | ||
} // namespace | ||
|
||
SignalHandler::SignalHandler(const std::function<void()>& signal_callback) { | ||
pipe_fds_.resize(2); | ||
// The shutdown thread will be notified of by our signal handler and take it from there. | ||
RELEASE_ASSERT(pipe(pipe_fds_.data()) == 0, "pipe failed"); | ||
|
||
shutdown_thread_ = std::thread([this, signal_callback]() { | ||
int tmp; | ||
RELEASE_ASSERT(read(pipe_fds_[0], &tmp, sizeof(int)) >= 0, "read failed"); | ||
RELEASE_ASSERT(close(pipe_fds_[0]) == 0, "read side close failed"); | ||
RELEASE_ASSERT(close(pipe_fds_[1]) == 0, "write side close failed"); | ||
pipe_fds_.clear(); | ||
signal_callback(); | ||
}); | ||
|
||
signal_handler_delegate = [this](int) { onSignal(); }; | ||
signal(SIGTERM, signal_handler); | ||
signal(SIGINT, signal_handler); | ||
} | ||
|
||
SignalHandler::~SignalHandler() { | ||
initiateShutdown(); | ||
if (shutdown_thread_.joinable()) { | ||
shutdown_thread_.join(); | ||
} | ||
} | ||
|
||
void SignalHandler::initiateShutdown() { | ||
if (pipe_fds_.size() == 2) { | ||
const int tmp = 0; | ||
RELEASE_ASSERT(write(pipe_fds_[1], &tmp, sizeof(int)) == sizeof(int), "write failed"); | ||
} | ||
} | ||
|
||
void SignalHandler::onSignal() { initiateShutdown(); } | ||
|
||
} // namespace Nighthawk |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
#pragma once | ||
|
||
#include <functional> | ||
#include <memory> | ||
#include <thread> | ||
#include <vector> | ||
|
||
#include "external/envoy/source/common/common/logger.h" | ||
|
||
namespace Nighthawk { | ||
|
||
/** | ||
* Callback definition for providing a delegate that should be executed after a signal | ||
* is observed. | ||
*/ | ||
using SignalCallback = std::function<void()>; | ||
|
||
/** | ||
* Utility class for handling TERM and INT signals. Allows wiring up a callback that | ||
* should be invoked upon signal reception. This callback implementation does not have to be | ||
* signal safe, as a different thread is used to fire it. | ||
* NOTE: Only the first observed signal will result in the callback being invoked. | ||
* WARNING: only a single instance should be active at any given time in a process, and | ||
* the responsibility for not breaking this rule is not enforced at this time. | ||
* | ||
* Example usage: | ||
* | ||
* Process p; | ||
* { | ||
* // Signals will be handled while in this scope. | ||
* // The provided callback will call cancel(), gracefully terminating | ||
* // execution. | ||
* auto s = SignalHandler([&p]() { log("cancelling!"); p->cancel(); }); | ||
* p->executeInfinitelyOrUntilCancelled(); | ||
* } | ||
* | ||
*/ | ||
class SignalHandler final : public Envoy::Logger::Loggable<Envoy::Logger::Id::main> { | ||
public: | ||
/** | ||
* Constructs a new SignalHandler instance. | ||
* WARNING: Only a single instance is allowed to be active process-wide at any given time. | ||
* @param signal_callback will be invoked after the first signal gets caught. Does not need to be | ||
* signal-safe. | ||
*/ | ||
SignalHandler(const SignalCallback& signal_callback); | ||
|
||
// Not copyable or movable. | ||
SignalHandler(SignalHandler const&) = delete; | ||
void operator=(SignalHandler const&) = delete; | ||
~SignalHandler(); | ||
|
||
private: | ||
/** | ||
* Fires on signal reception. | ||
*/ | ||
void onSignal(); | ||
|
||
/** | ||
* Notifies the thread responsible for shutting down the server that it is time to do so, if | ||
* needed. Safe to use in signal handling, and non-blocking. | ||
*/ | ||
void initiateShutdown(); | ||
|
||
std::thread shutdown_thread_; | ||
|
||
// Signal handling needs to be lean so we can't directly initiate shutdown while handling a | ||
// signal. Therefore we write a bite to a this pipe to propagate signal reception. Subsequently, | ||
// the read side will handle the actual shut down of the gRPC service without having to worry | ||
// about signal-safety. | ||
std::vector<int> pipe_fds_; | ||
}; | ||
|
||
using SignalHandlerPtr = std::unique_ptr<SignalHandler>; | ||
|
||
} // namespace Nighthawk |