Starting the HPX runtime
Contents
Starting the HPX runtime#
In order to write an application that uses services from the HPX runtime system, you need to initialize the HPX library by inserting certain calls into the code of your application. Depending on your use case, this can be done in 3 different ways:
Minimally invasive: Re-use the
main()
function as the main HPX entry point.Balanced use case: Supply your own main HPX entry point while blocking the main thread.
Most flexibility: Supply your own main HPX entry point while avoiding blocking the main thread.
Suspend and resume: As above but suspend and resume the HPX runtime to allow for other runtimes to be used.
Re-use the main()
function as the main HPX entry point#
This method is the least intrusive to your code. However, it provides you with the smallest flexibility in terms of initializing the HPX runtime system. The following code snippet shows what a minimal HPX application using this technique looks like:
#include <hpx/hpx_main.hpp>
int main(int argc, char* argv[])
{
return 0;
}
The only change to your code you have to make is to include the file
hpx/hpx_main.hpp
. In this case the function main()
will be invoked as
the first HPX thread of the application. The runtime system will be
initialized behind the scenes before the function main()
is executed and
will automatically stop after main()
has returned. For this method to work
you must link your application to the CMake target HPX::wrap_main
. This is
done automatically if you are using the provided macros
(Using macros to create new targets) to set up your application, but must be done
explicitly if you are using targets directly (Using CMake targets).
All HPX API functions can be used from within the main()
function now.
Note
The function main()
does not need to expect receiving argc
and
argv
as shown above, but could expose the signature int main()
. This
is consistent with the usually allowed prototypes for the function main()
in C++ applications.
All command line arguments specific to HPX will still be processed by the
HPX runtime system as usual. However, those command line options will be
removed from the list of values passed to argc
/argv
of the function
main()
. The list of values passed to main()
will hold only the
commandline options that are not recognized by the HPX runtime system (see
the section HPX Command Line Options for more details on what options are recognized
by HPX).
Note
In this mode all one-letter shortcuts that are normally
available on the HPX command line are disabled (such as -t
or -l
see
HPX Command Line Options). This is done to minimize any possible interaction
between the command line options recognized by the HPX runtime system and
any command line options defined by the application.
The value returned from the function main()
as shown above will be returned
to the operating system as usual.
Important
To achieve this seamless integration, the header file hpx/hpx_main.hpp
defines a macro:
#define main hpx_startup::user_main
which could result in unexpected behavior.
Important
To achieve this seamless integration, we use different implementations for
different operating systems. In case of Linux or macOS, the code present in
hpx_wrap.cpp
is put into action. We hook into the system function in case
of Linux and provide alternate entry point in case of macOS. For other
operating systems we rely on a macro:
#define main hpx_startup::user_main
provided in the header file hpx/hpx_main.hpp
. This implementation can
result in unexpected behavior.
Caution
We make use of an override variable include_libhpx_wrap
in the header
file hpx/hpx_main.hpp
to swiftly choose the function call stack at
runtime. Therefore, the header file should only be included in the main
executable. Including it in the components will result in multiple definition
of the variable.
Supply your own main HPX entry point while blocking the main thread#
With this method you need to provide an explicit main-thread function named
hpx_main
at global scope. This function will be invoked as the main entry
point of your HPX application on the console locality only (this
function will be invoked as the first HPX thread of your application). All
HPX API functions can be used from within this function.
The thread executing the function hpx::init
will block waiting for
the runtime system to exit. The value returned from hpx_main
will be
returned from hpx::init
after the runtime system has stopped.
The function hpx::finalize
has to be called on one of the HPX
localities in order to signal that all work has been scheduled and the runtime
system should be stopped after the scheduled work has been executed.
This method of invoking HPX has the advantage of the user being able to decide
which version of hpx::init
to call. This allows to pass
additional configuration parameters while initializing the HPX runtime system.
#include <hpx/hpx_init.hpp>
int hpx_main(int argc, char* argv[])
{
// Any HPX application logic goes here...
return hpx::finalize();
}
int main(int argc, char* argv[])
{
// Initialize HPX, run hpx_main as the first HPX thread, and
// wait for hpx::finalize being called.
return hpx::init(argc, argv);
}
Note
The function hpx_main
does not need to expect receiving argc
/argv
as shown above, but could expose one of the following signatures:
int hpx_main();
int hpx_main(int argc, char* argv[]);
int hpx_main(hpx::program_options::variables_map& vm);
This is consistent with (and extends) the usually allowed prototypes for the
function main()
in C++ applications.
The header file to include for this method of using HPX is
hpx/hpx_init.hpp
.
There are many additional overloads of hpx::init
available, such as
the ability to provide your own entry-point function instead of hpx_main
.
Please refer to the function documentation for more details (see: hpx/hpx_init.hpp
).
Supply your own main HPX entry point while avoiding blocking the main thread#
With this method you need to provide an explicit main thread function named
hpx_main
at global scope. This function will be invoked as the main entry
point of your HPX application on the console locality only (this
function will be invoked as the first HPX thread of your application). All
HPX API functions can be used from within this function.
The thread executing the function hpx::start
will not block
waiting for the runtime system to exit, but will return immediately.
The function hpx::finalize
has to be called on one of the HPX
localities in order to signal that all work has been scheduled and the runtime
system should be stopped after the scheduled work has been executed.
This method of invoking HPX is useful for applications where the main thread
is used for special operations, such a GUIs. The function hpx::stop
can be used to wait for the HPX runtime system to exit and should at least be
used as the last function called in main()
. The value returned from
hpx_main
will be returned from hpx::stop
after the runtime
system has stopped.
#include <hpx/hpx_start.hpp>
int hpx_main(int argc, char* argv[])
{
// Any HPX application logic goes here...
return hpx::finalize();
}
int main(int argc, char* argv[])
{
// Initialize HPX, run hpx_main.
hpx::start(argc, argv);
// ...Execute other code here...
// Wait for hpx::finalize being called.
return hpx::stop();
}
Note
The function hpx_main
does not need to expect receiving argc
/argv
as shown above, but could expose one of the following signatures:
int hpx_main();
int hpx_main(int argc, char* argv[]);
int hpx_main(hpx::program_options::variables_map& vm);
This is consistent with (and extends) the usually allowed prototypes for the
function main()
in C++ applications.
The header file to include for this method of using HPX is
hpx/hpx_start.hpp
.
There are many additional overloads of hpx::start
available, such as
the option for users to provide their own entry point function instead of hpx_main
.
Please refer to the function documentation for more details (see:
hpx/hpx_start.hpp
).
Supply your own explicit startup function as the main HPX entry point#
There is also a way to specify any function (besides hpx_main
) to be used as
the main entry point for your HPX application:
#include <hpx/hpx_init.hpp>
int application_entry_point(int argc, char* argv[])
{
// Any HPX application logic goes here...
return hpx::finalize();
}
int main(int argc, char* argv[])
{
// Initialize HPX, run application_entry_point as the first HPX thread,
// and wait for hpx::finalize being called.
return hpx::init(&application_entry_point, argc, argv);
}
Note
The function supplied to hpx::init
must have one of the following
prototypes:
int application_entry_point(int argc, char* argv[]); int application_entry_point(hpx::program_options::variables_map& vm);
Note
If nullptr
is used as the function argument, HPX will not run any
startup function on this locality.
Suspending and resuming the HPX runtime#
In some applications it is required to combine HPX with other runtimes. To
support this use case, HPX provides two functions: hpx::suspend
and
hpx::resume
. hpx::suspend
is a blocking call which will
wait for all scheduled tasks to finish executing and then put the thread pool OS
threads to sleep. hpx::resume
simply wakes up the sleeping threads
so that they are ready to accept new work. hpx::suspend
and
hpx::resume
can be found in the header hpx/hpx_suspend.hpp
.
#include <hpx/hpx_start.hpp>
#include <hpx/hpx_suspend.hpp>
int main(int argc, char* argv[])
{
// Initialize HPX, don't run hpx_main
hpx::start(nullptr, argc, argv);
// Schedule a function on the HPX runtime
hpx::post(&my_function, ...);
// Wait for all tasks to finish, and suspend the HPX runtime
hpx::suspend();
// Execute non-HPX code here
// Resume the HPX runtime
hpx::resume();
// Schedule more work on the HPX runtime
// hpx::finalize has to be called from the HPX runtime before hpx::stop
hpx::post([]() { hpx::finalize(); });
return hpx::stop();
}
Note
hpx::suspend
does not wait for hpx::finalize
to be
called. Only call hpx::finalize
when you wish to fully stop the
HPX runtime.
Warning
hpx::suspend
only waits for local tasks, i.e. tasks on thecurrent locality, to finish executing. When using
hpx::suspend
in a multi-locality scenario the user is responsible for ensuring that any work required from other localities has also finished.
HPX also supports suspending individual thread pools and threads. For details
on how to do that, see the documentation for hpx::threads::thread_pool_base
.
Automatically suspending worker threads#
The previous method guarantees that the worker threads are suspended when you ask for it and that they stay suspended. An alternative way to achieve the same effect is to tweak how quickly HPX suspends its worker threads when they run out of work. The following configuration values make sure that HPX idles very quickly:
hpx.max_idle_backoff_time = 1000
hpx.max_idle_loop_count = 0
They can be set on the command line using
--hpx:ini=hpx.max_idle_backoff_time=1000
and
--hpx:ini=hpx.max_idle_loop_count=0
. See Launching and configuring HPX applications
for more details on how to set configuration parameters.
After setting idling parameters the previous example could now be written like this instead:
#include <hpx/hpx_start.hpp>
int main(int argc, char* argv[])
{
// Initialize HPX, don't run hpx_main
hpx::start(nullptr, argc, argv);
// Schedule some functions on the HPX runtime
// NOTE: run_as_hpx_thread blocks until completion.
hpx::run_as_hpx_thread(&my_function, ...);
hpx::run_as_hpx_thread(&my_other_function, ...);
// hpx::finalize has to be called from the HPX runtime before hpx::stop
hpx::post([]() { hpx::finalize(); });
return hpx::stop();
}
In this example each call to hpx::run_as_hpx_thread
acts as a
“parallel region”.
Working of hpx_main.hpp
#
In order to initialize HPX from main()
, we make use of linker tricks.
It is implemented differently for different operating systems. The method of implementation is as follows:
Linux: Using linker
--wrap
option.Mac OSX: Using the linker
-e
option.Windows: Using
#define main hpx_startup::user_main
Linux implementation#
We make use of the Linux linker ld
‘s --wrap
option to wrap the
main()
function. This way any calls to main()
are redirected to our own
implementation of main. It is here that we check for the existence of
hpx_main.hpp
by making use of a shadow variable include_libhpx_wrap
. The
value of this variable determines the function stack at runtime.
The implementation can be found in libhpx_wrap.a
.
Important
It is necessary that hpx_main.hpp
be not included more than once.
Multiple inclusions can result in multiple definition of
include_libhpx_wrap
.
Mac OSX implementation#
Here we make use of yet another linker option -e
to change the entry point
to our custom entry function initialize_main
. We initialize the HPX
runtime system from this function and call main from the initialized system. We
determine the function stack at runtime by making use of the shadow variable
include_libhpx_wrap
.
The implementation can be found in libhpx_wrap.a
.
Important
It is necessary that hpx_main.hpp
be not included more than once.
Multiple inclusions can result in multiple definition of
include_libhpx_wrap
.
Windows implementation#
We make use of a macro #define main hpx_startup::user_main
to take care of
the initializations.
This implementation could result in unexpected behaviors.