Skip to content

Commit

Permalink
Throwing an exception derived from std::bad_alloc on OOM conditions
Browse files Browse the repository at this point in the history
- flyby: don't register signal(SIGABRT, ...) if hpx.handle_signals == 0
- flyby: adding hpx.handle_failed_new to disable defining new-handlers
  • Loading branch information
hkaiser committed Sep 14, 2024
1 parent f4ff6e8 commit 171f695
Show file tree
Hide file tree
Showing 14 changed files with 186 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ The |hpx| configuration section
exception_verbosity = ${HPX_EXCEPTION_VERBOSITY:2}
trace_depth = ${HPX_TRACE_DEPTH:20}
handle_signals = ${HPX_HANDLE_SIGNALS:1}
handle_failed_new = ${HPX_HANDLE_FAILED_NEW:1}
[hpx.stacks]
small_size = ${HPX_SMALL_STACK_SIZE:<hpx_small_stack_size>}
Expand Down Expand Up @@ -295,8 +296,14 @@ The |hpx| configuration section
print the configuration information (stack backtrace, system information,
etc.) whenever a signal is raised. The default is ``1``. Setting this
value to ``0`` can be useful in cases when generating a core-dump on
segmentation faults or similar signals is desired. This setting has no
effects on non-Linux platforms.
segmentation faults or similar signals is desired.
* * ``hpx.handle_failed_new``
* This setting defines whether HPX will register a handler for failed
allocationsthat will print the configuration information
(stack backtrace, system information, etc.) whenever an allocation fails.
The default is ``1``. Setting this value to ``0`` can be useful in cases
when generating a core-dump on segmentation faults or similar signals
is desired.
* * ``hpx.stacks.small_size``
* This is initialized to the small stack size to be used by |hpx| threads.
Set by default to the value of the compile time preprocessor constant
Expand Down
30 changes: 29 additions & 1 deletion libs/core/errors/include/hpx/errors/exception.hpp
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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<hpx::exception_info(
std::string const&, std::string const&, long, std::string const&)>;
Expand Down
56 changes: 53 additions & 3 deletions libs/core/errors/include/hpx/errors/throw_exception.hpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Copyright (c) 2007-2023 Hartmut Kaiser
// Copyright (c) 2007-2024 Hartmut Kaiser
// Copyright (c) 2011 Bryce Lelbach
//
// SPDX-License-Identifier: BSL-1.0
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// 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
Expand Down Expand Up @@ -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);

Expand All @@ -62,6 +65,9 @@ namespace hpx::detail {
std::string const& msg, std::string const& func,
std::string const& file, long line);

HPX_CORE_EXPORT void throws_bad_alloc_if(
hpx::error_code& ec, char const* func, char const* file, long line);

HPX_CORE_EXPORT void rethrows_if(
hpx::error_code& ec, exception const& e, std::string const& func);

Expand All @@ -72,7 +78,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)
{
Expand Down Expand Up @@ -163,6 +169,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
Expand All @@ -182,4 +219,17 @@ namespace hpx {
hpx::detail::throws_if(ec, errcode, hpx::util::format(__VA_ARGS__), f, \
__FILE__, __LINE__) /**/

/// \def HPX_THROWS_BAD_ALLOC_IF(ec, f)
/// \brief Either throw a hpx::bad_alloc_exception or \a hpx::error_code to out_of_memory
///
/// The macro \a HPX_THROWS_BAD_ALLOC_IF can be used to either throw a
/// \a hpx::bad_alloc_exception or to initialize a \a hpx::error_code to
/// \a hpx::error::out_of_memory. If
/// &ec == &hpx::throws, the semantics of this macro are equivalent to
/// \a HPX_THROW_BAD_ALLOC. If &ec != &hpx::throws, the \a hpx::error_code
/// instance \p ec is initialized instead.
///
#define HPX_THROWS_BAD_ALLOC_IF(ec, f) \
hpx::detail::throws_bad_alloc_if(ec, f, __FILE__, __LINE__) /**/

#include <hpx/config/warnings_suffix.hpp>
22 changes: 19 additions & 3 deletions libs/core/errors/src/exception.cpp
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -236,8 +241,7 @@ namespace hpx::detail {

///////////////////////////////////////////////////////////////////////////
template <typename Exception>
inline constexpr bool is_of_lightweight_hpx_category(
Exception const&) noexcept
constexpr bool is_of_lightweight_hpx_category(Exception const&) noexcept
{
return false;
}
Expand Down Expand Up @@ -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,
Expand Down
19 changes: 18 additions & 1 deletion libs/core/errors/src/throw_exception.cpp
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -68,6 +68,23 @@ namespace hpx::detail {
}
}

void throws_bad_alloc_if(
hpx::error_code& ec, char const* func, char const* file, long line)
{
if (&ec == &hpx::throws)
{
throw hpx::bad_alloc_exception();
}
else
{
ec = make_error_code(hpx::error::out_of_memory, "out of memory",
func, file, line,
(ec.category() == hpx::get_lightweight_hpx_category()) ?
hpx::throwmode::lightweight :
hpx::throwmode::plain);
}
}

void rethrows_if(
hpx::error_code& ec, exception const& e, std::string const& func)
{
Expand Down
3 changes: 3 additions & 0 deletions libs/core/format/include/hpx/modules/format.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <typename... Args>
std::string format(std::string_view format_str, Args const&... args)
{
Expand Down
11 changes: 10 additions & 1 deletion libs/core/format/src/format.cpp
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
4 changes: 2 additions & 2 deletions libs/core/runtime_configuration/src/runtime_configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,9 @@ namespace hpx::util {
"exception_verbosity = ${HPX_EXCEPTION_VERBOSITY:2}",
"trace_depth = ${HPX_TRACE_DEPTH:" HPX_PP_STRINGIZE(
HPX_PP_EXPAND(HPX_HAVE_THREAD_BACKTRACE_DEPTH)) "}",
#if !defined(HPX_WINDOWS)
"handle_signals = ${HPX_HANDLE_SIGNALS:1}",
#endif
"handle_failed_new = ${HPX_HANDLE_FAILED_NEW:1}",

// arity for collective operations implemented in a tree fashion
"[hpx.lcos.collectives]",
"arity = ${HPX_LCOS_COLLECTIVES_ARITY:32}",
Expand Down
52 changes: 35 additions & 17 deletions libs/core/runtime_local/src/runtime_local.cpp
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -45,6 +45,7 @@

#include <atomic>
#include <condition_variable>
#include <csignal>
#include <cstddef>
#include <cstdint>
#include <cstring>
Expand Down Expand Up @@ -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 (...)
{
Expand Down Expand Up @@ -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");
}

///////////////////////////////////////////////////////////////////////////
Expand All @@ -252,7 +254,7 @@ namespace hpx {
void on_abort(int) noexcept
{
exit_called = true;
std::exit(-1);
std::abort();
}
} // namespace detail

Expand All @@ -276,11 +278,22 @@ namespace hpx {
#endif

#if defined(HPX_WINDOWS)
if (hpx::util::get_entry_as<int>(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<int>(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);
Expand All @@ -303,7 +316,10 @@ namespace hpx {
}
#endif

std::set_new_handler(hpx::new_handler);
if (hpx::util::get_entry_as<int>(cfg, "hpx.handle_failed_new", 1))
{
std::set_new_handler(hpx::new_handler);
}
}

///////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -622,7 +638,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<std::int64_t>(
hpx::chrono::high_resolution_clock::now()) -
runtime_uptime();
return diff < 0LL ? 0ULL : static_cast<std::uint64_t>(diff);
}

Expand Down
Loading

0 comments on commit 171f695

Please sign in to comment.