]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
Refactor signal handling and process exit code
authorJoel Rosdahl <joel@rosdahl.net>
Mon, 18 May 2020 06:26:14 +0000 (08:26 +0200)
committerJoel Rosdahl <joel@rosdahl.net>
Wed, 20 May 2020 18:07:12 +0000 (20:07 +0200)
* Added a SignalHandler class which encapsulates parts related to signal
  handling.
* Moved data referenced by the exit functions to Context (compiler PID,
  pending temporary files and pending debug log files).
* Let the Context destructor do what the exitfn functionality used to
  do.
* Removed the now superfluous exitfn functionality.

Makefile.in
src/Context.cpp
src/Context.hpp
src/File.hpp
src/SignalHandler.cpp [new file with mode: 0644]
src/SignalHandler.hpp [moved from src/exitfn.hpp with 71% similarity]
src/ccache.cpp
src/execute.cpp
src/exitfn.cpp [deleted file]

index 7fe4ed6198f1f0f51a085af841a47521138d4ff2..f7ff91ad80e51e317d72b667592b22a91fa0410a 100644 (file)
@@ -47,6 +47,7 @@ non_third_party_sources = \
     src/NullCompressor.cpp \
     src/NullDecompressor.cpp \
     src/ProgressBar.cpp \
+    src/SignalHandler.cpp \
     src/Stat.cpp \
     src/Util.cpp \
     src/ZstdCompressor.cpp \
@@ -57,7 +58,6 @@ non_third_party_sources = \
     src/compopt.cpp \
     src/compress.cpp \
     src/execute.cpp \
-    src/exitfn.cpp \
     src/hash.cpp \
     src/hashutil.cpp \
     src/language.cpp \
index 7b651e408092c2f708058bffb39daecfe4dbc337..bcbefe9a497b65520b199e9ab83aa41c51bb693c 100644 (file)
 #include "Context.hpp"
 
 #include "Counters.hpp"
+#include "SignalHandler.hpp"
 #include "Util.hpp"
 #include "hashutil.hpp"
+#include "logging.hpp"
+#include "stats.hpp"
 
 using nonstd::string_view;
 
@@ -30,6 +33,18 @@ Context::Context()
 {
 }
 
+Context::~Context()
+{
+  stats_flush(this);
+  unlink_pending_tmp_files();
+
+  // Dump log buffer last to not lose any logs.
+  if (config.debug()) {
+    std::string path = fmt::format("{}.ccache-log", args_info.output_obj);
+    cc_dump_debug_log_buffer(path.c_str());
+  }
+}
+
 void
 Context::set_manifest_name(const struct digest& name)
 {
@@ -71,3 +86,32 @@ Context::set_path_and_stats_file(const struct digest& name,
   stats_file_var =
     fmt::format("{}/{}/stats", config.cache_dir(), name_string[0]);
 }
+
+void
+Context::register_pending_tmp_file(const std::string& path)
+{
+  SignalHandlerBlocker signal_handler_blocker;
+
+  m_pending_tmp_files.push_back(path);
+}
+
+void
+Context::unlink_pending_tmp_files_signal_safe()
+{
+  for (const std::string& path : m_pending_tmp_files) {
+    // Don't call Util::unlink_tmp since its cc_log calls aren't signal safe.
+    unlink(path.c_str());
+  }
+  // Don't clear m_pending_tmp_files since this method must be signal safe.
+}
+
+void
+Context::unlink_pending_tmp_files()
+{
+  SignalHandlerBlocker signal_handler_blocker;
+
+  for (const std::string& path : m_pending_tmp_files) {
+    Util::unlink_tmp(path, Util::UnlinkLog::ignore_failure);
+  }
+  m_pending_tmp_files.clear();
+}
index 0f5b4c9cf72818ad59776518568e89bc29a7d688..b4b393835cf1d83b3786672f2b30397cbcec545c 100644 (file)
@@ -23,6 +23,7 @@
 #include "Args.hpp"
 #include "ArgsInfo.hpp"
 #include "Config.hpp"
+#include "File.hpp"
 #include "MiniTrace.hpp"
 #include "NonCopyable.hpp"
 #include "ccache.hpp"
 #include <unordered_map>
 #include <vector>
 
+class SignalHandler;
+
 class Context : NonCopyable
 {
 public:
   Context();
+  ~Context();
 
   ArgsInfo args_info;
   Config config;
@@ -105,6 +109,13 @@ public:
   // Statistics which get written into the `stats_file` upon exit.
   Counters counter_updates;
 
+  // PID of currently executing compiler that we have started, if any. 0 means
+  // no ongoing compilation.
+  pid_t compiler_pid = 0;
+
+  // Files used by the hash debugging functionality.
+  std::vector<File> hash_debug_files;
+
 #ifdef MTR_ENABLED
   // Internal tracing.
   std::unique_ptr<MiniTrace> mini_trace;
@@ -113,6 +124,9 @@ public:
   void set_manifest_name(const struct digest& name);
   void set_result_name(const struct digest& name);
 
+  // Register a temporary file to remove at program exit.
+  void register_pending_tmp_file(const std::string& path);
+
 private:
   nonstd::optional<struct digest> m_manifest_name;
   std::string m_manifest_path;
@@ -122,6 +136,17 @@ private:
   std::string m_result_path;
   mutable std::string m_result_stats_file;
 
+  // [Start of variables touched by the signal handler]
+
+  // Temporary files to remove at program exit.
+  std::vector<std::string> m_pending_tmp_files;
+
+  // [End of variables touched by the signal handler]
+
+  friend SignalHandler;
+  void unlink_pending_tmp_files();
+  void unlink_pending_tmp_files_signal_safe(); // called from signal handler
+
   void set_path_and_stats_file(const struct digest& name,
                                nonstd::string_view suffix,
                                std::string& path_var,
index 0cac829c441e8226582cc6d1516c9e89a6cbc1c3..9f37f3fbee83d1877d2c783091eb6b46d6ac8a6e 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2019 Joel Rosdahl and other contributors
+// Copyright (C) 2019-2020 Joel Rosdahl and other contributors
 //
 // See doc/AUTHORS.adoc for a complete list of contributors.
 //
@@ -28,6 +28,8 @@
 class File : public NonCopyable
 {
 public:
+  File() = default;
+
   File(const std::string& path, const char* mode)
   {
     open(path, mode);
diff --git a/src/SignalHandler.cpp b/src/SignalHandler.cpp
new file mode 100644 (file)
index 0000000..23521dc
--- /dev/null
@@ -0,0 +1,143 @@
+// Copyright (C) 2020 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#include "SignalHandler.hpp"
+
+#ifndef _WIN32
+
+#  include "Context.hpp"
+
+namespace {
+
+SignalHandler* g_the_signal_handler = nullptr;
+sigset_t g_fatal_signal_set;
+
+void
+register_signal_handler(int signum)
+{
+  struct sigaction act;
+  memset(&act, 0, sizeof(act));
+  act.sa_handler = SignalHandler::on_signal;
+  act.sa_mask = g_fatal_signal_set;
+#  ifdef SA_RESTART
+  act.sa_flags = SA_RESTART;
+#  endif
+  sigaction(signum, &act, nullptr);
+}
+
+} // namespace
+
+SignalHandler::SignalHandler(Context& ctx) : m_ctx(ctx)
+{
+  assert(!g_the_signal_handler);
+  g_the_signal_handler = this;
+
+  sigemptyset(&g_fatal_signal_set);
+  sigaddset(&g_fatal_signal_set, SIGINT);
+  sigaddset(&g_fatal_signal_set, SIGTERM);
+#  ifdef SIGHUP
+  sigaddset(&g_fatal_signal_set, SIGHUP);
+#  endif
+#  ifdef SIGQUIT
+  sigaddset(&g_fatal_signal_set, SIGQUIT);
+#  endif
+
+  register_signal_handler(SIGINT);
+  register_signal_handler(SIGTERM);
+#  ifdef SIGHUP
+  register_signal_handler(SIGHUP);
+#  endif
+#  ifdef SIGQUIT
+  register_signal_handler(SIGQUIT);
+#  endif
+}
+
+SignalHandler::~SignalHandler()
+{
+  assert(g_the_signal_handler);
+  g_the_signal_handler = nullptr;
+}
+
+void
+SignalHandler::on_signal(int signum)
+{
+  assert(g_the_signal_handler);
+  Context& ctx = g_the_signal_handler->m_ctx;
+
+  // Unregister handler for this signal so that we can send the signal to
+  // ourselves at the end of the handler.
+  signal(signum, SIG_DFL);
+
+  // If ccache was killed explicitly, then bring the compiler subprocess (if
+  // any) with us as well.
+  if (signum == SIGTERM && ctx.compiler_pid != 0
+      && waitpid(ctx.compiler_pid, nullptr, WNOHANG) == 0) {
+    kill(ctx.compiler_pid, signum);
+  }
+
+  ctx.unlink_pending_tmp_files_signal_safe();
+
+  if (ctx.compiler_pid != 0) {
+    // Wait for compiler subprocess to exit before we snuff it.
+    waitpid(ctx.compiler_pid, nullptr, 0);
+  }
+
+  // Resend signal to ourselves to exit properly after returning from the
+  // handler.
+  kill(getpid(), signum);
+}
+
+#else // !_WIN32
+
+SignalHandler::SignalHandler(Context& ctx) : m_ctx(ctx)
+{
+}
+
+SignalHandler::~SignalHandler()
+{
+}
+
+#endif // !_WIN32
+
+void
+SignalHandler::block_signals()
+{
+#ifndef _WIN32
+  sigprocmask(SIG_BLOCK, &g_fatal_signal_set, nullptr);
+#endif
+}
+
+void
+SignalHandler::unblock_signals()
+{
+#ifndef _WIN32
+  sigset_t empty;
+  sigemptyset(&empty);
+  sigprocmask(SIG_SETMASK, &empty, nullptr);
+#endif
+}
+
+SignalHandlerBlocker::SignalHandlerBlocker()
+{
+  SignalHandler::block_signals();
+}
+
+SignalHandlerBlocker::~SignalHandlerBlocker()
+{
+  SignalHandler::unblock_signals();
+}
similarity index 71%
rename from src/exitfn.hpp
rename to src/SignalHandler.hpp
index b806de49bdac02c6df6274a23735fc1edf824024..50e7b0e2343c6bc3b6685f2d3516dcae8a7b3e09 100644 (file)
 
 #pragma once
 
+#include "system.hpp"
+
+#include "signal.h"
+
 class Context;
 
-void exitfn_init();
-void exitfn_add_nullary(void (*function)());
-void exitfn_add(void (*function)(void*), void* context);
-void exitfn_add_last(void (*function)(void*), void* context);
-void exitfn_delete_context(Context* ctx);
-void exitfn_call();
+class SignalHandler
+{
+public:
+  SignalHandler(Context& ctx);
+  ~SignalHandler();
+
+  static void on_signal(int signum);
+  static void block_signals();
+  static void unblock_signals();
+
+private:
+  Context& m_ctx;
+};
+
+class SignalHandlerBlocker
+{
+public:
+  SignalHandlerBlocker();
+  ~SignalHandlerBlocker();
+};
index a3582344d31d534bd40c08b1b54730d7a170f5f2..0f3b64e900cee33f60b8d18958b06a952c82ad7b 100644 (file)
@@ -26,6 +26,7 @@
 #include "FormatNonstdStringView.hpp"
 #include "MiniTrace.hpp"
 #include "ProgressBar.hpp"
+#include "SignalHandler.hpp"
 #include "StdMakeUnique.hpp"
 #include "Util.hpp"
 #include "argprocessing.hpp"
@@ -34,7 +35,6 @@
 #include "compress.hpp"
 #include "exceptions.hpp"
 #include "execute.hpp"
-#include "exitfn.hpp"
 #include "hash.hpp"
 #include "hashutil.hpp"
 #include "language.hpp"
@@ -123,21 +123,10 @@ struct pending_tmp_file
   struct pending_tmp_file* next;
 };
 
-// Temporary files to remove at program exit.
-static struct pending_tmp_file* pending_tmp_files = nullptr;
-
 // How often (in seconds) to scan $CCACHE_DIR/tmp for left-over temporary
 // files.
 static const int k_tempdir_cleanup_interval = 2 * 24 * 60 * 60; // 2 days
 
-#ifndef _WIN32
-static sigset_t fatal_signal_set;
-#endif
-
-// PID of currently executing compiler that we have started, if any. 0 means no
-// ongoing compilation. Not used in the _WIN32 case.
-static pid_t compiler_pid = 0;
-
 // This is a string that identifies the current "version" of the hash sum
 // computed by ccache. If, for any reason, we want to force the hash sum to be
 // different for the same input in a new ccache version, we can just change
@@ -180,120 +169,6 @@ failed(enum stats stat, optional<int> exit_code)
   throw Failure(stat, exit_code);
 }
 
-void
-block_signals()
-{
-#ifndef _WIN32
-  sigprocmask(SIG_BLOCK, &fatal_signal_set, nullptr);
-#endif
-}
-
-void
-unblock_signals()
-{
-#ifndef _WIN32
-  sigset_t empty;
-  sigemptyset(&empty);
-  sigprocmask(SIG_SETMASK, &empty, nullptr);
-#endif
-}
-
-static void
-add_pending_tmp_file(const char* path)
-{
-  block_signals();
-  auto e = static_cast<pending_tmp_file*>(x_malloc(sizeof(pending_tmp_file)));
-  e->path = x_strdup(path);
-  e->next = pending_tmp_files;
-  pending_tmp_files = e;
-  unblock_signals();
-}
-
-static void
-do_clean_up_pending_tmp_files()
-{
-  struct pending_tmp_file* p = pending_tmp_files;
-  while (p) {
-    // Can't call tmp_unlink here since its cc_log calls aren't signal safe.
-    unlink(p->path);
-    p = p->next;
-    // Leak p->path and p here because clean_up_pending_tmp_files needs to be
-    // signal safe.
-  }
-}
-
-static void
-clean_up_pending_tmp_files()
-{
-  block_signals();
-  do_clean_up_pending_tmp_files();
-  unblock_signals();
-}
-
-#ifndef _WIN32
-static void
-signal_handler(int signum)
-{
-  // Unregister handler for this signal so that we can send the signal to
-  // ourselves at the end of the handler.
-  signal(signum, SIG_DFL);
-
-  // If ccache was killed explicitly, then bring the compiler subprocess (if
-  // any) with us as well.
-  if (signum == SIGTERM && compiler_pid != 0
-      && waitpid(compiler_pid, nullptr, WNOHANG) == 0) {
-    kill(compiler_pid, signum);
-  }
-
-  do_clean_up_pending_tmp_files();
-
-  if (compiler_pid != 0) {
-    // Wait for compiler subprocess to exit before we snuff it.
-    waitpid(compiler_pid, nullptr, 0);
-  }
-
-  // Resend signal to ourselves to exit properly after returning from the
-  // handler.
-  kill(getpid(), signum);
-}
-
-static void
-register_signal_handler(int signum)
-{
-  struct sigaction act;
-  memset(&act, 0, sizeof(act));
-  act.sa_handler = signal_handler;
-  act.sa_mask = fatal_signal_set;
-#  ifdef SA_RESTART
-  act.sa_flags = SA_RESTART;
-#  endif
-  sigaction(signum, &act, nullptr);
-}
-
-static void
-set_up_signal_handlers()
-{
-  sigemptyset(&fatal_signal_set);
-  sigaddset(&fatal_signal_set, SIGINT);
-  sigaddset(&fatal_signal_set, SIGTERM);
-#  ifdef SIGHUP
-  sigaddset(&fatal_signal_set, SIGHUP);
-#  endif
-#  ifdef SIGQUIT
-  sigaddset(&fatal_signal_set, SIGQUIT);
-#  endif
-
-  register_signal_handler(SIGINT);
-  register_signal_handler(SIGTERM);
-#  ifdef SIGHUP
-  register_signal_handler(SIGHUP);
-#  endif
-#  ifdef SIGQUIT
-  register_signal_handler(SIGQUIT);
-#  endif
-}
-#endif // _WIN32
-
 static void
 clean_up_internal_tempdir(const Config& config)
 {
@@ -323,25 +198,7 @@ clean_up_internal_tempdir(const Config& config)
 }
 
 static void
-fclose_exitfn(void* context)
-{
-  fclose((FILE*)context);
-}
-
-static void
-dump_debug_log_buffer_exitfn(void* context)
-{
-  Context& ctx = *static_cast<Context*>(context);
-  if (!ctx.config.debug()) {
-    return;
-  }
-
-  std::string path = fmt::format("{}.ccache-log", ctx.args_info.output_obj);
-  cc_dump_debug_log_buffer(path.c_str());
-}
-
-static void
-init_hash_debug(const Context& ctx,
+init_hash_debug(Context& ctx,
                 struct hash* hash,
                 const char* obj_path,
                 char type,
@@ -352,15 +209,15 @@ init_hash_debug(const Context& ctx,
     return;
   }
 
-  char* path = format("%s.ccache-input-%c", obj_path, type);
-  FILE* debug_binary_file = fopen(path, "wb");
+  std::string path = fmt::format("{}.ccache-input-{}", obj_path, type);
+  File debug_binary_file(path, "wb");
   if (debug_binary_file) {
-    hash_enable_debug(hash, section_name, debug_binary_file, debug_text_file);
-    exitfn_add(fclose_exitfn, debug_binary_file);
+    hash_enable_debug(
+      hash, section_name, debug_binary_file.get(), debug_text_file);
+    ctx.hash_debug_files.push_back(std::move(debug_binary_file));
   } else {
-    cc_log("Failed to open %s: %s", path, strerror(errno));
+    cc_log("Failed to open %s: %s", path.c_str(), strerror(errno));
   }
-  free(path);
 }
 
 static GuessedCompiler
@@ -1006,7 +863,7 @@ to_cache(Context& ctx,
     tmp_stderr = format("%s/tmp.stderr", ctx.config.temporary_dir().c_str());
     tmp_stderr_fd = create_tmp_fd(&tmp_stderr);
     status = execute(
-      args.to_argv().data(), tmp_stdout_fd, tmp_stderr_fd, &compiler_pid);
+      args.to_argv().data(), tmp_stdout_fd, tmp_stderr_fd, &ctx.compiler_pid);
     args.pop_back(3);
   } else {
     // The cached result path is not known yet, use temporary files.
@@ -1026,7 +883,7 @@ to_cache(Context& ctx,
     status = execute(depend_mode_args.to_argv().data(),
                      tmp_stdout_fd,
                      tmp_stderr_fd,
-                     &compiler_pid);
+                     &ctx.compiler_pid);
   }
   MTR_END("execute", "compiler");
 
@@ -1228,13 +1085,13 @@ get_result_name_from_cpp(Context& ctx, Args& args, struct hash* hash)
       fmt::format("{}/{}.stdout", ctx.config.temporary_dir(), input_base));
     int stdout_fd = stdout_fd_and_path.first;
     stdout_path = stdout_fd_and_path.second;
-    add_pending_tmp_file(stdout_path.c_str());
+    ctx.register_pending_tmp_file(stdout_path);
 
     auto stderr_fd_and_path = Util::create_temp_fd(
       fmt::format("{}/tmp.cpp_stderr", ctx.config.temporary_dir()));
     int stderr_fd = stderr_fd_and_path.first;
     stderr_path = stderr_fd_and_path.second;
-    add_pending_tmp_file(stderr_path.c_str());
+    ctx.register_pending_tmp_file(stderr_path);
 
     size_t args_added = 2;
     args.push_back("-E");
@@ -1247,7 +1104,7 @@ get_result_name_from_cpp(Context& ctx, Args& args, struct hash* hash)
     cc_log("Running preprocessor");
     MTR_BEGIN("execute", "preprocessor");
     status =
-      execute(args.to_argv().data(), stdout_fd, stderr_fd, &compiler_pid);
+      execute(args.to_argv().data(), stdout_fd, stderr_fd, &ctx.compiler_pid);
     MTR_END("execute", "preprocessor");
     args.pop_back(args_added);
   }
@@ -1278,7 +1135,7 @@ get_result_name_from_cpp(Context& ctx, Args& args, struct hash* hash)
     ctx.i_tmpfile =
       fmt::format("{}.{}", stdout_path, ctx.config.cpp_extension());
     x_rename(stdout_path.c_str(), ctx.i_tmpfile.c_str());
-    add_pending_tmp_file(ctx.i_tmpfile.c_str());
+    ctx.register_pending_tmp_file(ctx.i_tmpfile);
   }
 
   if (!ctx.config.run_second_cpp()) {
@@ -2061,34 +1918,23 @@ set_up_context(Context& ctx, int argc, const char* const* argv)
 }
 
 // Initialize ccache, must be called once before anything else is run.
-static Context&
-initialize(int argc, const char* const* argv)
+static void
+initialize(Context& ctx, int argc, const char* const* argv)
 {
-  // This object is placed onto the heap so it is available in exit functions
-  // which run after main(). It is cleaned up by the last exit function.
-  Context* ctx = new Context;
-
-  set_up_config(ctx->config);
-  set_up_context(*ctx, argc, argv);
-  init_log(ctx->config);
-
-  exitfn_init();
-  exitfn_delete_context(ctx);
-  exitfn_add(stats_flush, ctx);
-  exitfn_add_nullary(clean_up_pending_tmp_files);
+  set_up_config(ctx.config);
+  set_up_context(ctx, argc, argv);
+  init_log(ctx.config);
 
   cc_log("=== CCACHE %s STARTED =========================================",
          CCACHE_VERSION);
 
   if (getenv("CCACHE_INTERNAL_TRACE")) {
 #ifdef MTR_ENABLED
-    ctx->mini_trace = std::make_unique<MiniTrace>(ctx->args_info);
+    ctx.mini_trace = std::make_unique<MiniTrace>(ctx->args_info);
 #else
     cc_log("Error: tracing is not enabled!");
 #endif
   }
-
-  return *ctx;
 }
 
 // Make a copy of stderr that will not be cached, so things like distcc can
@@ -2134,26 +1980,25 @@ static enum stats do_cache_compilation(Context& ctx, const char* const* argv);
 static int
 cache_compilation(int argc, const char* const* argv)
 {
-#ifndef _WIN32
-  set_up_signal_handlers();
-#endif
-
   // Needed for portability when using localtime_r.
   tzset();
 
-  Context& ctx = initialize(argc, argv);
+  auto ctx = std::make_unique<Context>();
+  SignalHandler signal_handler(*ctx);
+
+  initialize(*ctx, argc, argv);
 
   MTR_BEGIN("main", "find_compiler");
-  find_compiler(ctx, argv);
+  find_compiler(*ctx, argv);
   MTR_END("main", "find_compiler");
 
   try {
-    enum stats stat = do_cache_compilation(ctx, argv);
-    stats_update(ctx, stat);
+    enum stats stat = do_cache_compilation(*ctx, argv);
+    stats_update(*ctx, stat);
     return EXIT_SUCCESS;
   } catch (const Failure& e) {
     if (e.stat() != STATS_NONE) {
-      stats_update(ctx, e.stat());
+      stats_update(*ctx, e.stat());
     }
 
     if (e.exit_code()) {
@@ -2161,19 +2006,18 @@ cache_compilation(int argc, const char* const* argv)
     }
     // Else: Fall back to running the real compiler.
 
-    assert(!ctx.orig_args.empty());
+    assert(!ctx->orig_args.empty());
 
-    ctx.orig_args.erase_with_prefix("--ccache-");
-    add_prefix(ctx, ctx.orig_args, ctx.config.prefix_command());
+    ctx->orig_args.erase_with_prefix("--ccache-");
+    add_prefix(*ctx, ctx->orig_args, ctx->config.prefix_command());
 
     cc_log("Failed; falling back to running the real compiler");
 
-    // exitfn_call deletes ctx and thereby ctx.orig_orgs, so save it.
-    Args saved_orig_args(std::move(ctx.orig_args));
+    Args saved_orig_args(std::move(ctx->orig_args));
     auto execv_argv = saved_orig_args.to_argv();
 
     cc_log_argv("Executing ", execv_argv.data());
-    exitfn_call();
+    ctx.reset(); // Dump debug logs last thing before executing.
     execv(execv_argv[0], const_cast<char* const*>(execv_argv.data()));
     fatal("execv of %s failed: %s", execv_argv[0], strerror(errno));
   }
@@ -2267,21 +2111,21 @@ do_cache_compilation(Context& ctx, const char* const* argv)
   cc_log("Object file: %s", ctx.args_info.output_obj.c_str());
   MTR_META_THREAD_NAME(ctx.args_info.output_obj.c_str());
 
-  // Need to dump log buffer as the last exit function to not lose any logs.
-  exitfn_add_last(dump_debug_log_buffer_exitfn, &ctx);
-
-  FILE* debug_text_file = nullptr;
   if (ctx.config.debug()) {
     std::string path =
       fmt::format("{}.ccache-input-text", ctx.args_info.output_obj);
-    debug_text_file = fopen(path.c_str(), "w");
+    File debug_text_file(path, "w");
     if (debug_text_file) {
-      exitfn_add(fclose_exitfn, debug_text_file);
+      ctx.hash_debug_files.push_back(std::move(debug_text_file));
     } else {
       cc_log("Failed to open %s: %s", path.c_str(), strerror(errno));
     }
   }
 
+  FILE* debug_text_file = !ctx.hash_debug_files.empty()
+                            ? ctx.hash_debug_files.front().get()
+                            : nullptr;
+
   struct hash* common_hash = hash_init();
   init_hash_debug(ctx,
                   common_hash,
@@ -2444,7 +2288,8 @@ handle_main_options(int argc, const char* const* argv)
     {"zero-stats", no_argument, nullptr, 'z'},
     {nullptr, 0, nullptr, 0}};
 
-  Context& ctx = initialize(argc, argv);
+  Context ctx;
+  initialize(ctx, argc, argv);
 
   int c;
   while ((c = getopt_long(argc,
index f8a89d723df972661f6d280fe4aeb51a75b69885..b9ad4d823fee7b0095fe1fbe9a6ed82e25c16857 100644 (file)
@@ -21,6 +21,7 @@
 
 #include "Config.hpp"
 #include "Context.hpp"
+#include "SignalHandler.hpp"
 #include "Stat.hpp"
 #include "Util.hpp"
 #include "ccache.hpp"
@@ -247,9 +248,10 @@ execute(const char* const* argv, int fd_out, int fd_err, pid_t* pid)
 {
   cc_log_argv("Executing ", argv);
 
-  block_signals();
-  *pid = fork();
-  unblock_signals();
+  {
+    SignalHandlerBlocker signal_handler_blocker;
+    *pid = fork();
+  }
 
   if (*pid == -1) {
     fatal("Failed to fork: %s", strerror(errno));
@@ -272,9 +274,10 @@ execute(const char* const* argv, int fd_out, int fd_err, pid_t* pid)
     fatal("waitpid failed: %s", strerror(errno));
   }
 
-  block_signals();
-  *pid = 0;
-  unblock_signals();
+  {
+    SignalHandlerBlocker signal_handler_blocker;
+    *pid = 0;
+  }
 
   if (WEXITSTATUS(status) == 0 && WIFSIGNALED(status)) {
     return -1;
diff --git a/src/exitfn.cpp b/src/exitfn.cpp
deleted file mode 100644 (file)
index d02376d..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright (C) 2010-2020 Joel Rosdahl and other contributors
-//
-// See doc/AUTHORS.adoc for a complete list of contributors.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation; either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program; if not, write to the Free Software Foundation, Inc., 51
-// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-#include "exitfn.hpp"
-
-#include "Context.hpp"
-#include "legacy_util.hpp"
-#include "logging.hpp"
-
-struct exit_function
-{
-  void (*function)(void*);
-  void* context;
-  struct exit_function* next;
-};
-
-struct nullary_exit_function
-{
-  void (*function)();
-};
-
-static struct exit_function* exit_functions;
-static class Context* context_to_clean_up;
-
-static void
-call_nullary_exit_function(void* context)
-{
-  struct nullary_exit_function* p = (struct nullary_exit_function*)context;
-  p->function();
-  free(p);
-}
-
-// Initialize exit functions. Must be called once before exitfn_add* are used.
-void
-exitfn_init()
-{
-  if (atexit(exitfn_call) != 0) {
-    fatal("atexit failed: %s", strerror(errno));
-  }
-}
-
-// Add a nullary function to be called when ccache exits. Functions are called
-// in reverse order.
-void
-exitfn_add_nullary(void (*function)())
-{
-  auto p = static_cast<exit_function*>(x_malloc(sizeof(exit_function)));
-  p->function = reinterpret_cast<void (*)(void*)>(function);
-  exitfn_add(call_nullary_exit_function, p);
-}
-
-// Add a function to be called with a context parameter when ccache exits.
-// Functions are called in LIFO order except when added via exitfn_add_last.
-void
-exitfn_add(void (*function)(void*), void* context)
-{
-  auto p = static_cast<exit_function*>(x_malloc(sizeof(exit_function)));
-  p->function = function;
-  p->context = context;
-  p->next = exit_functions;
-  exit_functions = p;
-}
-
-// Add a function to be called with a context parameter when ccache exits. In
-// contrast to exitfn_add, exitfn_add_last sets up the function to be called
-// last.
-void
-exitfn_add_last(void (*function)(void*), void* context)
-{
-  auto p = static_cast<exit_function*>(x_malloc(sizeof(exit_function)));
-  p->function = function;
-  p->context = context;
-  p->next = nullptr;
-
-  struct exit_function** q = &exit_functions;
-  while (*q) {
-    q = &(*q)->next;
-  }
-  *q = p;
-}
-
-// Remember a Context pointer to delete after all exit functions have run.
-// This function can only be called once.
-void
-exitfn_delete_context(Context* ctx)
-{
-  assert(context_to_clean_up == nullptr);
-  context_to_clean_up = ctx;
-}
-
-// Call added functions.
-void
-exitfn_call()
-{
-  struct exit_function* p = exit_functions;
-  exit_functions = nullptr;
-  while (p) {
-    p->function(p->context);
-    struct exit_function* q = p;
-    p = p->next;
-    free(q);
-  }
-
-  delete context_to_clean_up;
-}