Miscellaneous¶
Error handling¶
Like in any other asynchronous invocation scheme it is important to be able to handle error conditions occurring while the asynchronous (and possibly remote) operation is executed. In HPX all error handling is based on standard C++ exception handling. Any exception thrown during the execution of an asynchronous operation will be transferred back to the original invocation locality, where it is rethrown during synchronization with the calling thread.
The source code for this example can be found here:
error_handling.cpp
.
Working with exceptions¶
For the following description we assume that the function raise_exception()
is executed by invoking the plain action raise_exception_type
.
//[error_handling_raise_exception
void raise_exception()
{
HPX_THROW_EXCEPTION(hpx::no_success, "raise_exception", "simulated error");
The exception is thrown using the macro HPX_THROW_EXCEPTION
. The type
of the thrown exception is hpx::exception
. 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.
Any exception thrown during the execution of an action is transferred back to
the (asynchronous) invocation site. It will be rethrown in this context when the
calling thread tries to wait for the result of the action by invoking either
future<>::get()
or the synchronous action invocation wrapper as shown here:
// Error reporting using exceptions
//[exception_diagnostic_information
hpx::cout << "Error reporting using exceptions\n";
try {
// invoke raise_exception() which throws an exception
raise_exception_action do_it;
do_it(hpx::find_here());
}
catch (hpx::exception const& e) {
// Print just the essential error information.
hpx::cout << "caught exception: " << e.what() << "\n\n";
// Print all of the available diagnostic information as stored with
// the exception.
hpx::cout << "diagnostic information:"
<< hpx::diagnostic_information(e) << "\n";
Note
The exception is transferred back to the invocation site even if it is executed on a different locality.
Additionally, this example demonstrates how an exception thrown by an (possibly
remote) action can be handled. It shows the use of
hpx::diagnostic_information
which retrieves all available diagnostic
information from the exception as a formatted string. This includes, for
instance, the name of the source file and line number, the sequence number of
the OS-thread and the HPX-thread id, the locality id and the stack
backtrace of the point where the original exception was thrown.
Under certain circumstances it is desirable to output only some of the diagnostics, or to output those using different formatting. For this case, HPX exposes a set of lower level functions as demonstrated in the following code snippet:
// Detailed error reporting using exceptions
//[exception_diagnostic_elements
hpx::cout << "Detailed error reporting using exceptions\n";
try {
// Invoke raise_exception() which throws an exception.
raise_exception_action do_it;
do_it(hpx::find_here());
}
catch (hpx::exception const& e) {
// Print the elements of the diagnostic information separately.
hpx::cout << "{what}: " << hpx::get_error_what(e) << "\n";
hpx::cout << "{locality-id}: " << hpx::get_error_locality_id(e) << "\n";
hpx::cout << "{hostname}: " << hpx::get_error_host_name(e) << "\n";
hpx::cout << "{pid}: " << hpx::get_error_process_id(e) << "\n";
hpx::cout << "{function}: " << hpx::get_error_function_name(e) << "\n";
hpx::cout << "{file}: " << hpx::get_error_file_name(e) << "\n";
hpx::cout << "{line}: " << hpx::get_error_line_number(e) << "\n";
hpx::cout << "{os-thread}: " << hpx::get_error_os_thread(e) << "\n";
hpx::cout << "{thread-id}: " << std::hex << hpx::get_error_thread_id(e)
<< "\n";
hpx::cout << "{thread-description}: "
<< hpx::get_error_thread_description(e) << "\n";
hpx::cout << "{state}: " << std::hex << hpx::get_error_state(e)
<< "\n";
hpx::cout << "{stack-trace}: " << hpx::get_error_backtrace(e) << "\n";
hpx::cout << "{env}: " << hpx::get_error_env(e) << "\n";
Working with error codes¶
Most of the API functions exposed by HPX can be invoked in two different
modes. By default those will throw an exception on error as described above.
However, sometimes it is desirable not to throw an exception in case of an error
condition. In this case an object instance of the hpx::error_code
type can be passed as the last argument to the API function. In case of an error
the error condition will be returned in that hpx::error_code
instance. The following example demonstrates extracting the full diagnostic
information without exception handling:
{
//[error_handling_diagnostic_information
hpx::cout << "Error reporting using error code\n";
// Create a new error_code instance.
hpx::error_code ec;
// If an instance of an error_code is passed as the last argument while
// invoking the action, the function will not throw in case of an error
// but store the error information in this error_code instance instead.
raise_exception_action do_it;
do_it(hpx::find_here(), ec);
if (ec) {
// Print just the essential error information.
hpx::cout << "returned error: " << ec.get_message() << "\n";
// Print all of the available diagnostic information as stored with
// the exception.
hpx::cout << "diagnostic information:"
<< hpx::diagnostic_information(ec) << "\n";
}
Note
The error information is transferred back to the invocation site even if it is executed on a different locality.
This example show how an error can be handled without having to resolve to
exceptions and that the returned hpx::error_code
instance can be
used in a very similar way as the hpx::exception
type above. Simply
pass it to the hpx::diagnostic_information
which retrieves all
available diagnostic information from the error code instance as a formatted
string.
As for handling exceptions, when working with error codes, under certain circumstances it is desirable to output only some of the diagnostics, or to output those using different formatting. For this case, HPX exposes a set of lower level functions usable with error codes as demonstrated in the following code snippet:
{
//[error_handling_diagnostic_elements
hpx::cout << "Detailed error reporting using error code\n";
// Create a new error_code instance.
hpx::error_code ec;
// If an instance of an error_code is passed as the last argument while
// invoking the action, the function will not throw in case of an error
// but store the error information in this error_code instance instead.
raise_exception_action do_it;
do_it(hpx::find_here(), ec);
if (ec) {
// Print the elements of the diagnostic information separately.
hpx::cout << "{what}: " << hpx::get_error_what(ec) << "\n";
hpx::cout << "{locality-id}: " << hpx::get_error_locality_id(ec) << "\n";
hpx::cout << "{hostname}: " << hpx::get_error_host_name(ec) << "\n";
hpx::cout << "{pid}: " << hpx::get_error_process_id(ec) << "\n";
hpx::cout << "{function}: " << hpx::get_error_function_name(ec)
<< "\n";
hpx::cout << "{file}: " << hpx::get_error_file_name(ec) << "\n";
hpx::cout << "{line}: " << hpx::get_error_line_number(ec) << "\n";
hpx::cout << "{os-thread}: " << hpx::get_error_os_thread(ec) << "\n";
hpx::cout << "{thread-id}: " << std::hex
<< hpx::get_error_thread_id(ec) << "\n";
hpx::cout << "{thread-description}: "
<< hpx::get_error_thread_description(ec) << "\n\n";
hpx::cout << "{state}: " << std::hex << hpx::get_error_state(ec)
<< "\n";
hpx::cout << "{stack-trace}: " << hpx::get_error_backtrace(ec) << "\n";
hpx::cout << "{env}: " << hpx::get_error_env(ec) << "\n";
}
For more information please refer to the documentation of
hpx::get_error_what
, hpx::get_error_locality_id
,
hpx::get_error_host_name
, hpx::get_error_process_id
,
hpx::get_error_function_name
, hpx::get_error_file_name
,
hpx::get_error_line_number
, hpx::get_error_os_thread
,
hpx::get_error_thread_id
,
hpx::get_error_thread_description
,
hpx::get_error_backtrace
, hpx::get_error_env
, and
hpx::get_error_state
.
Lightweight error codes¶
Sometimes it is not desirable to collect all the ambient information about the error at the point where it happened as this might impose too much overhead for simple scenarios. In this case, HPX provides a lightweight error code facility which will hold the error code only. The following snippet demonstrates its use:
{
//[lightweight_error_handling_diagnostic_information
hpx::cout << "Error reporting using an lightweight error code\n";
// Create a new error_code instance.
hpx::error_code ec(hpx::lightweight);
// If an instance of an error_code is passed as the last argument while
// invoking the action, the function will not throw in case of an error
// but store the error information in this error_code instance instead.
raise_exception_action do_it;
do_it(hpx::find_here(), ec);
if (ec) {
// Print just the essential error information.
hpx::cout << "returned error: " << ec.get_message() << "\n";
// Print all of the available diagnostic information as stored with
// the exception.
hpx::cout << "error code:" << ec.value() << "\n";
}
All functions which retrieve other diagnostic elements from the
hpx::error_code
will fail if called with a lightweight error_code
instance.
Utilities in HPX¶
In order to ease the burden of programming in HPX we have provided several utilities to users. The following section documents those facilies.
The HPX I/O-streams component¶
The HPX I/O-streams subsystem extends the standard C++ output streams
std::cout
and std::cerr
to work in the distributed setting of an HPX
application. All of the output streamed to hpx::cout
will be dispatched to
std::cout
on the console locality. Likewise, all output generated
from hpx::cerr
will be dispatched to std::cerr
on the console
locality.
Note
All existing standard manipulators can be used in conjunction with
hpx::cout
and hpx::cerr
Historically, HPX also defines
hpx::endl
and hpx::flush
but those are just aliases for the
corresponding standard manipulators.
In order to use either hpx::cout
or hpx::cerr
application codes need to
#include <hpx/include/iostreams.hpp>
. For an example, please see the
simplest possible ‘Hello world’ program as included as an example with HPX:
// Copyright (c) 2007-2012 Hartmut Kaiser
//
// 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)
///////////////////////////////////////////////////////////////////////////////
// The purpose of this example is to execute a HPX-thread printing
// "Hello World!" once. That's all.
//[hello_world_1_getting_started
// Including 'hpx/hpx_main.hpp' instead of the usual 'hpx/hpx_init.hpp' enables
// to use the plain C-main below as the direct main HPX entry point.
#include <hpx/hpx_main.hpp>
#include <hpx/include/iostreams.hpp>
int main()
{
// Say hello to the world!
hpx::cout << "Hello World!\n" << hpx::flush;
return 0;
}
//]
Additionally those applications need to link with the iostreams component. When
using cmake this can be achieved by using the COMPONENT_DEPENDENCIES
parameter, for instance:
include(HPX_AddExecutable)
add_hpx_executable(
hello_world
SOURCES hello_world.cpp
COMPONENT_DEPENDENCIES iostreams
)
Note
The hpx::cout
and hpx::cerr
streams buffer all output locally until a
std::endl
or std::flush
is encountered. That means that no output
will appear on the console as long as either of those is explicitly used.