Skip to content
Snippets Groups Projects
Commit 71dbacd5 authored by Hadrien G's avatar Hadrien G Committed by Hadrien Benjamin Grasland
Browse files

Okay clang-format, that really looks awful now, but if it's the only way to pass CI...

parent aba656b1
No related branches found
No related tags found
No related merge requests found
......@@ -49,42 +49,42 @@ namespace Details {
return (outcome == LoopOutcome<T>(LoopFlow::Break));
}
// This macro wraps a user-provided for loop iteration code into a functor
// which returns a LoopOutcome. It handles all loop iteration outcomes which
// can only be handled by a macro, that is, everything but exceptions.
//
// The RETURN_TYPE parameter indicates the return type of the function inside
// which the underlying for loop is located, and INDEX is the name of the loop
// variable expected by the user's iteration code.
//
// Ideally, RETURN_TYPE should be inferred instead of caller-provider, but
// that does not seem possible in C++14. I think C++17's flavor of auto is
// powerful enough for such inference, so maybe something for the future...
//
#define _ACTFW_WRAPPED_LOOP_ITERATION(RETURN_TYPE, INDEX, ...) \
/* To produce a functor in a random context, we use a catch-all lambda */ \
[&](size_t INDEX) -> FW::Details::LoopOutcome<RETURN_TYPE> { \
/* We set up a fake for loop environment to catch all user actions */ \
for (int _dummy_##INDEX = 0; _dummy_##INDEX < 2; ++_dummy_##INDEX) { \
if (_dummy_##INDEX == 0) { \
/* The user's loop iteration code is pasted here. It may return */ \
/* or throw an exception, that is handled in higher-level code */ \
__VA_ARGS__ \
// This macro wraps a user-provided for loop iteration code into a functor
// which returns a LoopOutcome. It handles all loop iteration outcomes which
// can only be handled by a macro, that is, everything but exceptions.
//
// The RETURN_TYPE parameter indicates the return type of the function inside
// which the underlying for loop is located, and INDEX is the name of the loop
// variable expected by the user's iteration code.
//
// Ideally, RETURN_TYPE should be inferred instead of caller-provider, but
// that does not seem possible in C++14. I think C++17's flavor of auto is
// powerful enough for such inference, so maybe something for the future...
//
#define _ACTFW_WRAPPED_LOOP_ITERATION(RETURN_TYPE, INDEX, ...) \
/* To produce a functor in a random context, we use a catch-all lambda */ \
[&](size_t INDEX) -> FW::Details::LoopOutcome<RETURN_TYPE> { \
/* We set up a fake for loop environment to catch all user actions */ \
for (int _dummy_##INDEX = 0; _dummy_##INDEX < 2; ++_dummy_##INDEX) { \
if (_dummy_##INDEX == 0) { \
/* The user's loop iteration code is pasted here. It may return */ \
/* or throw an exception, that is handled in higher-level code */ \
__VA_ARGS__ \
\
/* If control reaches this point, the loop iteration code */ \
/* finished normally without continuing, breaking, or returning */ \
return FW::Details::LoopFlow::NormalIteration; \
} else { \
/* If control reaches this point, the loop iteration was skipped */ \
/* using the "continue" control flow keyword */ \
return FW::Details::LoopFlow::Continue; \
} \
/* If control reaches this point, the loop iteration code */ \
/* finished normally without continuing, breaking, or returning */ \
return FW::Details::LoopFlow::NormalIteration; \
} else { \
/* If control reaches this point, the loop iteration was skipped */ \
/* using the "continue" control flow keyword */ \
return FW::Details::LoopFlow::Continue; \
} \
} \
\
/* It control reaches this point, the loop was aborted using the */ \
/* "break" control flow keyword */ \
return FW::Details::LoopFlow::Break; \
}
/* It control reaches this point, the loop was aborted using the */ \
/* "break" control flow keyword */ \
return FW::Details::LoopFlow::Break; \
}
// Thanks to the loop iteration wrapper above, most of the ACTSFW parallel
// for loop can now be written as real C++ code, instead of a macro.
......@@ -113,11 +113,11 @@ namespace Details {
bool exit_loop_early = false;
LoopOutcome<T> exit_reason = LoopFlow::NormalIteration;
// Our parallel for loop is implemented using OpenMP
#pragma omp parallel for
// Our parallel for loop is implemented using OpenMP
#pragma omp parallel for
for (size_t index = start; index < end; ++index) {
// Skip remaining loop iterations if asked to exit the loop early
#pragma omp flush(exit_loop_early)
// Skip remaining loop iterations if asked to exit the loop early
#pragma omp flush(exit_loop_early)
if (exit_loop_early) continue;
// Run this loop iteration and record the outcome, exceptions included
......@@ -130,65 +130,64 @@ namespace Details {
// Abort the loop if the iteration's outcome states that we should do so
if (loop_outcome_is_fatal(outcome)) {
#pragma omp critical
#pragma omp critical
{
exit_reason = std::move(outcome);
exit_loop_early = true;
#pragma omp flush(exit_loop_early)
#pragma omp flush(exit_loop_early)
}
}
}
// Analyze the loop termination cause and react accordingly
switch (exit_reason.which()) {
// The loop exited normally or via break, no need to do anything
case 0:
return boost::optional<T>();
// The loop was exited due to an early return, propagate it up the stack
case 1:
return boost::get<T>(std::move(exit_reason));
// The loop was exited because an exception was thrown. Rethrow it.
case 2:
auto exception = boost::get<std::exception_ptr>(std::move(exit_reason));
std::rethrow_exception(std::move(exception));
// The loop exited normally or via break, no need to do anything
case 0:
return boost::optional<T>();
// The loop was exited due to an early return, propagate it up the stack
case 1:
return boost::get<T>(std::move(exit_reason));
// The loop was exited because an exception was thrown. Rethrow it.
case 2:
auto exception = boost::get<std::exception_ptr>(std::move(exit_reason));
std::rethrow_exception(std::move(exception));
}
}
/// The following macro is the out-of-order multithreaded equivalent of the
/// following serial loop (macro arguments capitalized for emphasis):
///
/// for(size_t INDEX = START; index < END; ++index) {
/// ...
/// }
///
/// Unlike raw OpenMP 3.1, we also support breaks, early returns, and
/// exception propagation, to allow for more idiomatic C++ code. On the other
/// hand, due to the way the C preprocessor handles macro arguments, the loop
/// iteration code should not contain any preprocessor directive.
///
/// Due to limitations of C++14's type inference, this macro may currently
/// only be called in a function which returns FW::ProcessCode. This
/// restriction may be lifted in the future if C++ type inference improves.
///
#define ACTFW_PARALLEL_FOR(INDEX, START, END, ...) \
/* This dummy do-while asserts that the macro's output is a statement */ \
do { \
/* Execute the parallel for loop */ \
auto optional_early_return \
= FW::Details::parallel_for_impl<FW::ProcessCode>( \
(START), \
(END), \
_ACTFW_WRAPPED_LOOP_ITERATION( \
FW::ProcessCode, INDEX, __VA_ARGS__)); \
/// The following macro is the out-of-order multithreaded equivalent of the
/// following serial loop (macro arguments capitalized for emphasis):
///
/// for(size_t INDEX = START; index < END; ++index) {
/// ...
/// }
///
/// Unlike raw OpenMP 3.1, we also support breaks, early returns, and
/// exception propagation, to allow for more idiomatic C++ code. On the other
/// hand, due to the way the C preprocessor handles macro arguments, the loop
/// iteration code should not contain any preprocessor directive.
///
/// Due to limitations of C++14's type inference, this macro may currently
/// only be called in a function which returns FW::ProcessCode. This
/// restriction may be lifted in the future if C++ type inference improves.
///
#define ACTFW_PARALLEL_FOR(INDEX, START, END, ...) \
/* This dummy do-while asserts that the macro's output is a statement */ \
do { \
/* Execute the parallel for loop */ \
auto optional_early_return \
= FW::Details::parallel_for_impl<FW::ProcessCode>( \
(START), \
(END), \
_ACTFW_WRAPPED_LOOP_ITERATION( \
FW::ProcessCode, INDEX, __VA_ARGS__)); \
\
/* Return early from the host function if asked to do so */ \
if (optional_early_return) { \
return *optional_early_return; \
} \
} while (false);
/* Return early from the host function if asked to do so */ \
if (optional_early_return) { \
return *optional_early_return; \
} \
} while (false);
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment