Skip to content

Commit

Permalink
Add wait(CtrlCHandler&)
Browse files Browse the repository at this point in the history
  • Loading branch information
bernardnormier committed Jan 26, 2025
1 parent ad7819c commit bd1804c
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 40 deletions.
70 changes: 30 additions & 40 deletions cpp/include/Ice/CtrlCHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,59 +9,49 @@

namespace Ice
{
/**
* Invoked when a signal occurs. The callback must not raise exceptions.
* On Linux and macOS, the callback is NOT a signal handler and can call
* functions that are not async-signal safe.
* @param sig The signal number that occurred.
*/
/// The function called by CtrlCHandler when it catches a signal. This function must not throw exceptions. On Linux
/// and macOS, this function is NOT a signal handler and can call functions that are not async-signal safe.
/// @param sig The signal number that occurred.
/// \headerfile Ice/Ice.h
using CtrlCHandlerCallback = std::function<void(int sig)>;

/**
* Provides a portable way to handle Ctrl-C and Ctrl-C like signals.
* On Linux and macOS, the CtrlCHandler handles SIGHUP, SIGINT and SIGTERM.
* On Windows, it is essentially a wrapper for SetConsoleCtrlHandler().
*
* \headerfile Ice/Ice.h
*/
/// Provides a portable way to handle Ctrl-C and Ctrl-C like signals. On Linux and macOS, the CtrlCHandler handles
/// SIGHUP, SIGINT and SIGTERM. On Windows, it is essentially a wrapper for SetConsoleCtrlHandler.
/// \headerfile Ice/Ice.h
class ICE_API CtrlCHandler
{
public:
/**
* Registers a callback function that handles Ctrl-C like signals.
* On Linux and macOS, this constructor masks the SIGHUP, SIGINT and SIGTERM
* signals and then creates a thread that waits for these signals using sigwait.
* On Windows, this constructor calls SetConsoleCtrlCHandler to register a handler
* routine that calls the supplied callback function.
* Only a single CtrlCHandler object can exist in a process at a give time.
* @param cb The callback function to invoke when a signal is received.
*/
/// Constructs a CtrlCHandler.
/// On Linux and macOS, this constructor masks the SIGHUP, SIGINT and SIGTERM signals and then creates a thread
/// that waits for these signals using sigwait. On Windows, this constructor calls SetConsoleCtrlCHandler to
/// register a handler routine that calls the supplied callback function.
/// Only a single CtrlCHandler object can exist in a process at a give time.
/// @param cb The callback function to invoke when a signal is received. The default (nullptr) means do nothing.
explicit CtrlCHandler(CtrlCHandlerCallback cb = nullptr);

/**
* Unregisters the callback function.
* On Linux and macOS, this destructor joins and terminates the thread created
* by the constructor but does not "unmask" SIGHUP, SIGINT and SIGTERM. As a result,
* these signals are ignored after this destructor completes.
* On Windows, this destructor unregisters the SetConsoleCtrlHandler handler
* routine, and as a result a Ctrl-C or similar signal will terminate the application
* after this destructor completes.
*/
/// Unregisters the callback function.
/// On Linux and macOS, this destructor joins and terminates the thread created by the constructor but does not
/// "unmask" SIGHUP, SIGINT and SIGTERM. As a result, these signals are ignored after this destructor completes.
/// On Windows, this destructor unregisters the SetConsoleCtrlHandler handler routine, and as a result a
/// Ctrl-C or similar signal will terminate the application after this destructor completes.
~CtrlCHandler();

/**
* Replaces the signal callback.
* @param cb The new callback.
* @return The old callback, or nil if no callback is currently set.
*/
/// Replaces the signal callback.
/// @param cb The new callback.
/// @return The old callback, which may be nullptr.
CtrlCHandlerCallback setCallback(CtrlCHandlerCallback cb);

/**
* Obtains the signal callback.
* @return The callback.
*/
/// Obtains the current signal callback.
/// @return The callback.
[[nodiscard]] CtrlCHandlerCallback getCallback() const;
};

/// Waits until the CtrlC-C handler catches a signal.
/// @param handler The Ctrl-C handler.
/// @return The signal number that was caught.
/// @remark This function installs a callback in the CtrlCHandler object. It must not be called when a non-null
/// callback is already installed.
ICE_API int wait(CtrlCHandler& handler);
}

#endif
22 changes: 22 additions & 0 deletions cpp/src/Ice/CtrlCHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#endif

#include <cassert>
#include <future>
#include <mutex>

#ifndef _WIN32
Expand Down Expand Up @@ -202,3 +203,24 @@ CtrlCHandler::~CtrlCHandler()
}

#endif

int
Ice::wait(CtrlCHandler& handler)
{
promise<int> promise;

CtrlCHandlerCallback oldCallback = handler.setCallback(
[&promise, &handler](int sig)
{
handler.setCallback(nullptr); // ignore further signals
promise.set_value(sig);
});

if (oldCallback)
{
handler.setCallback(oldCallback);
throw Ice::LocalException{__FILE__, __LINE__, "do not call wait on a CtrlCHandler with a registered callback"};
}

return promise.get_future().get();
}

0 comments on commit bd1804c

Please sign in to comment.