From e950c0d8800156b160d978f2d2f1f3f39c74c6ff Mon Sep 17 00:00:00 2001 From: Hartmut Kaiser Date: Wed, 11 Sep 2024 15:42:58 -0500 Subject: [PATCH] Throwing an exception derived from std::bad_alloc on OOM conditions - flyby: don't register signal(SIGABRT, ...) if hpx.handle_signals == 0 --- .../errors/include/hpx/errors/exception.hpp | 30 +++++++++++- .../include/hpx/errors/throw_exception.hpp | 40 ++++++++++++++-- libs/core/errors/src/exception.cpp | 22 +++++++-- libs/core/errors/src/throw_exception.cpp | 2 +- .../format/include/hpx/modules/format.hpp | 3 ++ libs/core/format/src/format.cpp | 11 ++++- libs/core/runtime_local/src/runtime_local.cpp | 47 ++++++++++++------- .../hpx/schedulers/queue_holder_thread.hpp | 4 +- .../include/hpx/schedulers/thread_queue.hpp | 4 +- .../threading_base/src/thread_helpers.cpp | 3 +- .../src/server/one_size_heap_list.cpp | 3 +- libs/full/init_runtime/src/hpx_init.cpp | 10 ++-- 12 files changed, 137 insertions(+), 42 deletions(-) diff --git a/libs/core/errors/include/hpx/errors/exception.hpp b/libs/core/errors/include/hpx/errors/exception.hpp index bb381dadf2d3..549f113ffa04 100644 --- a/libs/core/errors/include/hpx/errors/exception.hpp +++ b/libs/core/errors/include/hpx/errors/exception.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2007-2022 Hartmut Kaiser +// Copyright (c) 2007-2024 Hartmut Kaiser // Copyright (c) 2011 Bryce Lelbach // // SPDX-License-Identifier: BSL-1.0 @@ -106,6 +106,34 @@ namespace hpx { throwmode mode = throwmode::plain) const noexcept; }; + class bad_alloc_exception + : public hpx::exception + , public std::bad_alloc + { + public: + /// Construct a hpx::bad_alloc_exception. + bad_alloc_exception(); + + /// The function \a get_error() returns hpx::error::out_of_memory + /// + /// \throws nothing + [[nodiscard]] static constexpr error get_error() noexcept + { + return hpx::error::out_of_memory; + } + + /// The function \a get_error_code() returns a hpx::error_code which + /// represents the same error condition as this hpx::exception instance. + /// + /// \param mode The parameter \p mode specifies whether the returned + /// hpx::error_code belongs to the error category + /// \a hpx_category (if mode is \a throwmode::plain, this is the + /// default) or to the category \a hpx_category_rethrow + /// (if mode is \a rethrow). + [[nodiscard]] error_code get_error_code( + throwmode mode = throwmode::plain) const noexcept; + }; + using custom_exception_info_handler_type = std::function; diff --git a/libs/core/errors/include/hpx/errors/throw_exception.hpp b/libs/core/errors/include/hpx/errors/throw_exception.hpp index 78c4f6ab6e1e..717a0956edd0 100644 --- a/libs/core/errors/include/hpx/errors/throw_exception.hpp +++ b/libs/core/errors/include/hpx/errors/throw_exception.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2007-2023 Hartmut Kaiser +// Copyright (c) 2007-2024 Hartmut Kaiser // Copyright (c) 2011 Bryce Lelbach // // SPDX-License-Identifier: BSL-1.0 @@ -6,7 +6,7 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) /// \file throw_exception.hpp -/// \page HPX_THROW_EXCEPTION, HPX_THROWS_IF +/// \page HPX_THROW_EXCEPTION, HPX_THROW_BAD_ALLOC, HPX_THROWS_IF /// \headerfile hpx/exception.hpp #pragma once @@ -37,6 +37,9 @@ namespace hpx::detail { std::string const& msg, std::string const& func, std::string const& file, long line); + [[noreturn]] HPX_CORE_EXPORT void throw_bad_alloc_exception( + char const* func, char const* file, long line); + [[noreturn]] HPX_CORE_EXPORT void rethrow_exception( exception const& e, std::string const& func); @@ -72,7 +75,7 @@ namespace hpx::detail { namespace hpx { /// \cond NOINTERNAL - /// \brief throw an hpx::exception initialized from the given arguments + /// \brief throw a hpx::exception initialized from the given arguments [[noreturn]] inline void throw_exception(error e, std::string const& msg, std::string const& func, std::string const& file = "", long line = -1) { @@ -163,6 +166,37 @@ namespace hpx { hpx::detail::throw_exception( \ errcode, hpx::util::format(__VA_ARGS__), f, __FILE__, __LINE__) /**/ +/////////////////////////////////////////////////////////////////////////////// +/// \def HPX_THROW_BAD_ALLOC(f, msg) +/// \brief Throw a hpx::bad_alloc_exception initialized from the given parameters +/// +/// The macro \a HPX_THROW_BAD_ALLOC can be used to throw a hpx::exception. +/// The purpose of this macro is to prepend the source file name and line number +/// of the position where the exception is thrown to the error message. +/// Moreover, this associates additional diagnostic information with the +/// exception, such as file name and line number, locality id and thread id, +/// and stack backtrace from the point where the exception was thrown. +/// +/// The parameter \p errcode holds the hpx::error code the new exception should +/// encapsulate. The parameter \p f is expected to hold the name of the +/// function exception is thrown from and the parameter \p msg holds the error +/// message the new exception should encapsulate. +/// +/// \par Example: +/// +/// \code +/// void raise_exception() +/// { +/// // Throw a hpx::exception initialized from the given parameters. +/// // Additionally associate with this exception some detailed +/// // diagnostic information about the throw-site. +/// HPX_THROW_BAD_ALLOC("raise_exception", "simulated error"); +/// } +/// \endcode +/// +#define HPX_THROW_BAD_ALLOC(f, ...) \ + hpx::detail::throw_bad_alloc_exception(f, __FILE__, __LINE__) /**/ + /// \def HPX_THROWS_IF(ec, errcode, f, msg) /// \brief Either throw a hpx::exception or initialize \a hpx::error_code from /// the given parameters diff --git a/libs/core/errors/src/exception.cpp b/libs/core/errors/src/exception.cpp index 1f0e33d05f60..34e5b6a9688e 100644 --- a/libs/core/errors/src/exception.cpp +++ b/libs/core/errors/src/exception.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2007-2023 Hartmut Kaiser +// Copyright (c) 2007-2024 Hartmut Kaiser // Copyright (c) 2011 Bryce Lelbach // // SPDX-License-Identifier: BSL-1.0 @@ -140,6 +140,11 @@ namespace hpx { return {this->std::system_error::code().value(), *this}; } + bad_alloc_exception::bad_alloc_exception() + : hpx::exception(hpx::error::out_of_memory) + { + } + static custom_exception_info_handler_type custom_exception_info_handler; void set_custom_exception_info_handler(custom_exception_info_handler_type f) @@ -236,8 +241,7 @@ namespace hpx::detail { /////////////////////////////////////////////////////////////////////////// template - inline constexpr bool is_of_lightweight_hpx_category( - Exception const&) noexcept + constexpr bool is_of_lightweight_hpx_category(Exception const&) noexcept { return false; } @@ -278,6 +282,18 @@ namespace hpx::detail { std::rethrow_exception(get_exception(e, func, file, line)); } + HPX_CORE_EXPORT void throw_bad_alloc_exception( + [[maybe_unused]] char const* func, [[maybe_unused]] char const* file, + [[maybe_unused]] long line) + { + if (pre_exception_handler) + { + pre_exception_handler(); + } + + throw hpx::bad_alloc_exception(); + } + /////////////////////////////////////////////////////////////////////////// template HPX_CORE_EXPORT std::exception_ptr get_exception( hpx::exception const&, std::string const&, std::string const&, long, diff --git a/libs/core/errors/src/throw_exception.cpp b/libs/core/errors/src/throw_exception.cpp index 1032aab4d0ab..1525ebb41be6 100644 --- a/libs/core/errors/src/throw_exception.cpp +++ b/libs/core/errors/src/throw_exception.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2007-2022 Hartmut Kaiser +// Copyright (c) 2007-2024 Hartmut Kaiser // Copyright (c) 2011 Bryce Lelbach // // SPDX-License-Identifier: BSL-1.0 diff --git a/libs/core/format/include/hpx/modules/format.hpp b/libs/core/format/include/hpx/modules/format.hpp index 0e9ca0847e47..545d075bee91 100644 --- a/libs/core/format/include/hpx/modules/format.hpp +++ b/libs/core/format/include/hpx/modules/format.hpp @@ -280,6 +280,9 @@ namespace hpx::util { format_arg const* args, std::size_t count); } // namespace detail + // enable using format in variadic contexts + HPX_CORE_EXPORT std::string const& format(); + template std::string format(std::string_view format_str, Args const&... args) { diff --git a/libs/core/format/src/format.cpp b/libs/core/format/src/format.cpp index 7c091d2e2564..9c76ac7e94b5 100644 --- a/libs/core/format/src/format.cpp +++ b/libs/core/format/src/format.cpp @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// // Copyright (c) 2017-2018 Agustin Berge -// Copyright (c) 2022 Hartmut Kaiser +// Copyright (c) 2024 Hartmut Kaiser // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -129,3 +129,12 @@ namespace hpx::util::detail { return os.str(); } } // namespace hpx::util::detail + +namespace hpx::util { + + std::string const& format() + { + static std::string empty; + return empty; + } +} // namespace hpx::util diff --git a/libs/core/runtime_local/src/runtime_local.cpp b/libs/core/runtime_local/src/runtime_local.cpp index 526d9dd2cac8..c014c63aa3fb 100644 --- a/libs/core/runtime_local/src/runtime_local.cpp +++ b/libs/core/runtime_local/src/runtime_local.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2007-2022 Hartmut Kaiser +// Copyright (c) 2007-2024 Hartmut Kaiser // Copyright (c) 2011 Bryce Lelbach // // SPDX-License-Identifier: BSL-1.0 @@ -45,6 +45,7 @@ #include #include +#include #include #include #include @@ -72,17 +73,19 @@ namespace hpx::detail { if (rt == nullptr) return; - rt->get_thread_manager().enumerate_threads( - [](hpx::threads::thread_id_type id) -> bool { - hpx::threads::thread_data* td = get_thread_id_data(id); - auto sched = td->get_scheduler_base(); - LTM_(debug).format("Logging all runtime threads: pool({}), " - "scheduler({})," - "thread({}), description({}), state({})", - sched->get_parent_pool(), sched, id, - td->get_description(), td->get_state().state()); - return true; - }); + [[maybe_unused]] auto ret = + rt->get_thread_manager().enumerate_threads( + [](hpx::threads::thread_id_type const& id) -> bool { + hpx::threads::thread_data* td = get_thread_id_data(id); + auto sched = td->get_scheduler_base(); + LTM_(debug).format( + "Logging all runtime threads: pool({}), " + "scheduler({})," + "thread({}), description({}), state({})", + sched->get_parent_pool(), sched, id, + td->get_description(), td->get_state().state()); + return true; + }); } catch (...) { @@ -229,8 +232,7 @@ namespace hpx { /////////////////////////////////////////////////////////////////////////// [[noreturn]] HPX_CORE_EXPORT void HPX_CDECL new_handler() { - HPX_THROW_EXCEPTION(hpx::error::out_of_memory, "new_handler", - "new allocator failed to allocate memory"); + HPX_THROW_BAD_ALLOC("new_handler"); } /////////////////////////////////////////////////////////////////////////// @@ -252,7 +254,7 @@ namespace hpx { void on_abort(int) noexcept { exit_called = true; - std::exit(-1); + std::abort(); } } // namespace detail @@ -276,11 +278,22 @@ namespace hpx { #endif #if defined(HPX_WINDOWS) + if (hpx::util::get_entry_as(cfg, "hpx.handle_signals", 1)) + { + [[maybe_unused]] auto const prev_signal = + std::signal(SIGABRT, detail::on_abort); + HPX_ASSERT(prev_signal != SIG_ERR); + } + // Set console control handler to allow server to be stopped. SetConsoleCtrlHandler(hpx::termination_handler, TRUE); #else if (hpx::util::get_entry_as(cfg, "hpx.handle_signals", 1)) { + [[maybe_unused]] auto const prev_signal = + std::signal(SIGABRT, detail::on_abort); + HPX_ASSERT(prev_signal != SIG_ERR); + struct sigaction new_action; new_action.sa_handler = hpx::termination_handler; sigemptyset(&new_action.sa_mask); @@ -622,7 +635,9 @@ namespace hpx { std::uint64_t runtime::get_system_uptime() { std::int64_t const diff = - hpx::chrono::high_resolution_clock::now() - runtime_uptime(); + static_cast( + hpx::chrono::high_resolution_clock::now()) - + runtime_uptime(); return diff < 0LL ? 0ULL : static_cast(diff); } diff --git a/libs/core/schedulers/include/hpx/schedulers/queue_holder_thread.hpp b/libs/core/schedulers/include/hpx/schedulers/queue_holder_thread.hpp index b5de546e3351..15fa333836f1 100644 --- a/libs/core/schedulers/include/hpx/schedulers/queue_holder_thread.hpp +++ b/libs/core/schedulers/include/hpx/schedulers/queue_holder_thread.hpp @@ -585,9 +585,7 @@ namespace hpx::threads::policies { debug::threadinfo(&tid)); lk.unlock(); - HPX_THROW_EXCEPTION(hpx::error::out_of_memory, - "queue_holder_thread::add_to_thread_map", - "Couldn't add new thread to the thread map {}", map_size); + HPX_THROW_BAD_ALLOC("queue_holder_thread::add_to_thread_map"); } ++thread_map_count_.data_; diff --git a/libs/core/schedulers/include/hpx/schedulers/thread_queue.hpp b/libs/core/schedulers/include/hpx/schedulers/thread_queue.hpp index f0092f4998a9..518c9d2b0b46 100644 --- a/libs/core/schedulers/include/hpx/schedulers/thread_queue.hpp +++ b/libs/core/schedulers/include/hpx/schedulers/thread_queue.hpp @@ -259,9 +259,7 @@ namespace hpx::threads::policies { { --addfrom->new_tasks_count_.data_; lk.unlock(); - HPX_THROW_EXCEPTION(hpx::error::out_of_memory, - "thread_queue::add_new", - "Couldn't add new thread to the thread map"); + HPX_THROW_BAD_ALLOC("thread_queue::add_new"); } #if defined(HPX_MSVC) diff --git a/libs/core/threading_base/src/thread_helpers.cpp b/libs/core/threading_base/src/thread_helpers.cpp index 810bf734686e..8978c8110e24 100644 --- a/libs/core/threading_base/src/thread_helpers.cpp +++ b/libs/core/threading_base/src/thread_helpers.cpp @@ -625,8 +625,7 @@ namespace hpx::this_thread { std::ptrdiff_t const remaining_stack = get_available_stack_space(); if (remaining_stack < 0) { - HPX_THROW_EXCEPTION(hpx::error::out_of_memory, - "has_sufficient_stack_space", "Stack overflow"); + HPX_THROW_BAD_ALLOC("has_sufficient_stack_space"); } bool const sufficient_stack_space = static_cast(remaining_stack) >= space_needed; diff --git a/libs/full/components_base/src/server/one_size_heap_list.cpp b/libs/full/components_base/src/server/one_size_heap_list.cpp index 30ed91e74504..d8371c2b16f2 100644 --- a/libs/full/components_base/src/server/one_size_heap_list.cpp +++ b/libs/full/components_base/src/server/one_size_heap_list.cpp @@ -109,8 +109,7 @@ namespace hpx::util { if (HPX_UNLIKELY(!result || nullptr == p)) { // out of memory - HPX_THROW_EXCEPTION(hpx::error::out_of_memory, name() + "::alloc", - "new heap failed to allocate {1} objects", count); + HPX_THROW_BAD_ALLOC("one_size_heap_list::alloc"); } #if defined(HPX_DEBUG) diff --git a/libs/full/init_runtime/src/hpx_init.cpp b/libs/full/init_runtime/src/hpx_init.cpp index eb28a983f2f0..50be6af29c5f 100644 --- a/libs/full/init_runtime/src/hpx_init.cpp +++ b/libs/full/init_runtime/src/hpx_init.cpp @@ -134,11 +134,7 @@ namespace hpx::detail { #if defined(__FreeBSD__) freebsd_environ = env; #endif - // set a handler for std::abort, std::at_quick_exit, and std::atexit - [[maybe_unused]] auto const prev_signal = - std::signal(SIGABRT, detail::on_abort); - HPX_ASSERT(prev_signal != SIG_ERR); - + // set a handler for std::at_quick_exit, and std::atexit [[maybe_unused]] auto const ret_at_exit = std::atexit(detail::on_exit); HPX_ASSERT(ret_at_exit == 0); @@ -715,7 +711,7 @@ namespace hpx { if (!f.empty()) return rt.run(hpx::bind_front(f, vm)); - // Run this runtime instance without an hpx_main + // Run this runtime instance without a hpx_main return rt.run(); } @@ -736,7 +732,7 @@ namespace hpx { return rt.start(hpx::bind_front(f, vm)); } - // Run this runtime instance without an hpx_main + // Run this runtime instance without a hpx_main return rt.start(); }