blob: 6dc562ca7df1135eccd2f536703e73c9afdb06af [file] [log] [blame]
// Copyright 2023 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef NINJA_INTERRUPT_HANDLING_H_
#define NINJA_INTERRUPT_HANDLING_H_
/// Convenience classes used to control how user interruption
/// is handled by Ninja at various times. This means Ctrl+C and Ctrl+Break
/// events on Win32, and SIGINT/SIGHUP/SIGTERM ones on Posix.
#ifdef _WIN32
#include <windows.h>
/// On Ctrl-C or Ctrl-Break, post en empty i/o completion packet
/// to a given i/o completion queue. Useful to catch signals when
/// waiting for overlapped i/o on Win32.
struct InterruptCompletionPortHandler {
InterruptCompletionPortHandler(HANDLE ioport, ULONG_PTR completion_key = 0);
~InterruptCompletionPortHandler();
private:
static BOOL WINAPI HandlerRoutine(DWORD dwCtrlType);
HANDLE old_ioport_;
ULONG_PTR old_completion_key_;
static HANDLE s_ioport_;
static ULONG_PTR s_completion_key_;
};
/// Forward all Ctrl-C and Ctrl-Break signals to a different
/// process group.
struct InterruptForwarder {
InterruptForwarder(DWORD process_group_id);
~InterruptForwarder();
private:
static BOOL WINAPI HandlerRoutine(DWORD dwCtrlType);
DWORD old_process_group_id_;
static DWORD s_process_group_id_;
};
#else // !_WIN32
#include <signal.h>
/// A class used to block all SIGINT/SIGHUP/SIGTERM signals
/// in the current process. Restores the previous signal
/// mask in the destructor.
struct InterruptBlocker {
InterruptBlocker();
~InterruptBlocker();
const sigset_t& old_mask() const { return prev_signal_mask_; }
private:
sigset_t prev_signal_mask_;
};
/// Base class for all interrupt handlers.
struct InterruptHandlerBase {
/// Constructor sets a new signal handler for SIGINT/SIGHUP/SIGTERM
/// and unblocks these signals!
InterruptHandlerBase(const struct sigaction& action);
/// Destructor restores previous interrupt handlers and signal mask.
~InterruptHandlerBase();
/// Return the signal mask before construction. This will be restored
/// on destruction as well.
sigset_t old_mask() const { return old_mask_; }
private:
struct sigaction old_int_action_;
struct sigaction old_hup_action_;
struct sigaction old_term_action_;
sigset_t old_mask_;
};
/// Catch all SIGINT/SIGHUP/SIGTERM signals and stores the
/// corresponding signal number into a global interrupted()
/// variable. This can also detect pending signals.
struct InterruptCatcher : public InterruptHandlerBase {
InterruptCatcher();
~InterruptCatcher();
/// Return interrupt signal number, or 0 if there were none.
int interrupted() const { return s_interrupted_; }
/// Clear the interrupted signal number.
void Clear() { s_interrupted_ = 0; }
/// Handle any pending interruption signal. This updates
/// the interrupted() value but does not cancel the signals.
static void HandlePendingInterrupt();
private:
static struct sigaction MakeAction();
static volatile sig_atomic_t s_interrupted_;
};
/// Forward all SIGINT/SIGHUP/SIGTERM signal to a different
/// process group
struct InterruptForwarder : public InterruptHandlerBase {
explicit InterruptForwarder(pid_t process_group);
~InterruptForwarder();
private:
static struct sigaction MakeAction();
pid_t old_process_group_ = -1;
static pid_t s_process_group_;
};
#endif // !_WIN32
#endif // NINJA_INTERRUPT_HANDLING_H_