From: Joel Rosdahl Date: Mon, 18 May 2020 06:26:14 +0000 (+0200) Subject: Refactor signal handling and process exit code X-Git-Tag: v4.0~441 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=91aa70754c3816537b080fef1c6630313b400721;p=thirdparty%2Fccache.git Refactor signal handling and process exit code * 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. --- diff --git a/Makefile.in b/Makefile.in index 7fe4ed619..f7ff91ad8 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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 \ diff --git a/src/Context.cpp b/src/Context.cpp index 7b651e408..bcbefe9a4 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -19,8 +19,11 @@ #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(); +} diff --git a/src/Context.hpp b/src/Context.hpp index 0f5b4c9cf..b4b393835 100644 --- a/src/Context.hpp +++ b/src/Context.hpp @@ -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" @@ -35,10 +36,13 @@ #include #include +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 hash_debug_files; + #ifdef MTR_ENABLED // Internal tracing. std::unique_ptr 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 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 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, diff --git a/src/File.hpp b/src/File.hpp index 0cac829c4..9f37f3fbe 100644 --- a/src/File.hpp +++ b/src/File.hpp @@ -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 index 000000000..23521dca9 --- /dev/null +++ b/src/SignalHandler.cpp @@ -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(); +} diff --git a/src/exitfn.hpp b/src/SignalHandler.hpp similarity index 71% rename from src/exitfn.hpp rename to src/SignalHandler.hpp index b806de49b..50e7b0e23 100644 --- a/src/exitfn.hpp +++ b/src/SignalHandler.hpp @@ -18,11 +18,29 @@ #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(); +}; diff --git a/src/ccache.cpp b/src/ccache.cpp index a3582344d..0f3b64e90 100644 --- a/src/ccache.cpp +++ b/src/ccache.cpp @@ -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 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(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); - 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(ctx->args_info); + ctx.mini_trace = std::make_unique(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(); + 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(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, diff --git a/src/execute.cpp b/src/execute.cpp index f8a89d723..b9ad4d823 100644 --- a/src/execute.cpp +++ b/src/execute.cpp @@ -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 index d02376d62..000000000 --- a/src/exitfn.cpp +++ /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(x_malloc(sizeof(exit_function))); - p->function = reinterpret_cast(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(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(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; -}