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
  • Loading branch information
hkaiser committed Sep 12, 2024
1 parent f4ff6e8 commit 640a624
Show file tree
Hide file tree
Showing 12 changed files with 171 additions and 45 deletions.
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
47 changes: 31 additions & 16 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 Down Expand Up @@ -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<std::int64_t>(
hpx::chrono::high_resolution_clock::now()) -
runtime_uptime();
return diff < 0LL ? 0ULL : static_cast<std::uint64_t>(diff);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -585,9 +585,7 @@ namespace hpx::threads::policies {
debug::threadinfo<thread_id_type*>(&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_;
Expand Down
8 changes: 2 additions & 6 deletions libs/core/schedulers/include/hpx/schedulers/thread_queue.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -724,9 +722,7 @@ namespace hpx::threads::policies {
if (HPX_UNLIKELY(!p.second))
{
lk.unlock();
HPX_THROWS_IF(ec, hpx::error::out_of_memory,
"thread_queue::create_thread",
"Couldn't add new thread to the map of threads");
HPX_THROWS_BAD_ALLOC_IF(ec, "thread_queue::create_thread");
return;
}
++thread_map_count_;
Expand Down
3 changes: 1 addition & 2 deletions libs/core/threading_base/src/thread_helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::size_t>(remaining_stack) >= space_needed;
Expand Down
3 changes: 1 addition & 2 deletions libs/full/components_base/src/server/one_size_heap_list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading

0 comments on commit 640a624

Please sign in to comment.