Skip to content

Commit 0962ec5

Browse files
committed
Remove usage of std::exit with special exception such that the environments are always torn town correctly.
1 parent 975b513 commit 0962ec5

File tree

13 files changed

+224
-173
lines changed

13 files changed

+224
-173
lines changed

include/plssvm/exceptions/exceptions.hpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,30 @@ class exception : public std::runtime_error {
5656
source_location loc_;
5757
};
5858

59+
/**
60+
* @brief Exception type thrown for early exit in the cmd parser constructor.
61+
* @details Used for a graceful tear down.
62+
*/
63+
class cmd_parser_exit : public exception {
64+
public:
65+
/**
66+
* @brief Construct a new exception forwarding the exit code and source location to `plssvm::exception`.
67+
* @param[in] exit_code the exit code
68+
* @param[in] loc the exception's call side information
69+
*/
70+
explicit cmd_parser_exit(int exit_code, source_location loc = source_location::current());
71+
72+
/**
73+
* @brief Return the previously defined exit code.
74+
* @return the exit code (`[[nodiscard]]`)
75+
*/
76+
[[nodiscard]] int exit_code() const noexcept { return exit_code_; }
77+
78+
private:
79+
/// The exit code.
80+
int exit_code_{};
81+
};
82+
5983
/**
6084
* @brief Exception type thrown if the provided parameter is invalid.
6185
*/

src/main_predict.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,10 @@ int main(int argc, char *argv[]) {
270270
PLSSVM_DETAIL_TRACKING_PERFORMANCE_TRACKER_SAVE(cmd_parser.performance_tracking_filename);
271271
#endif
272272

273+
} catch (const plssvm::cmd_parser_exit &e) {
274+
// something inside the cmd parser went wrong
275+
// -> don't call std::exit directly to gracefully tear down the environment
276+
return e.exit_code();
273277
} catch (const plssvm::exception &e) {
274278
std::cerr << fmt::format("An exception occurred on MPI rank {}!: {}", comm.rank(), e.what_with_loc()) << std::endl;
275279
return EXIT_FAILURE;

src/main_scale.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
#include <chrono> // std::chrono::{steady_clock, duration}, std::chrono_literals namespace
2727
#include <cstddef> // std::size_t
28-
#include <cstdlib> // std::exit, EXIT_SUCCESS, EXIT_FAILURE
28+
#include <cstdlib> // EXIT_SUCCESS, EXIT_FAILURE
2929
#include <exception> // std::exception
3030
#include <iostream> // std::cerr, std::endl
3131
#include <variant> // std::visit
@@ -121,6 +121,10 @@ int main(int argc, char *argv[]) {
121121

122122
PLSSVM_DETAIL_TRACKING_PERFORMANCE_TRACKER_SAVE(cmd_parser.performance_tracking_filename);
123123

124+
} catch (const plssvm::cmd_parser_exit &e) {
125+
// something inside the cmd parser went wrong
126+
// -> don't call std::exit directly to gracefully tear down the environment
127+
return e.exit_code();
124128
} catch (const plssvm::exception &e) {
125129
std::cerr << fmt::format("An exception occurred on MPI rank {}!: {}", comm.rank(), e.what_with_loc()) << std::endl;
126130
return EXIT_FAILURE;

src/main_train.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,10 @@ int main(int argc, char *argv[]) {
220220
PLSSVM_DETAIL_TRACKING_PERFORMANCE_TRACKER_SAVE(cmd_parser.performance_tracking_filename);
221221
#endif
222222

223+
} catch (const plssvm::cmd_parser_exit &e) {
224+
// something inside the cmd parser went wrong
225+
// -> don't call std::exit directly to gracefully tear down the environment
226+
return e.exit_code();
223227
} catch (const plssvm::exception &e) {
224228
std::cerr << fmt::format("An exception occurred on MPI rank {}!: {}", comm.rank(), e.what_with_loc()) << std::endl;
225229
return EXIT_FAILURE;

src/plssvm/detail/cmd/parser_predict.cpp

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "plssvm/constants.hpp" // plssvm::real_type
1515
#include "plssvm/detail/assert.hpp" // PLSSVM_ASSERT
1616
#include "plssvm/detail/logging/mpi_log_untracked.hpp" // plssvm::detail::log_untracked
17+
#include "plssvm/exceptions/exceptions.hpp" // plssvm::cmd_parser_exit
1718
#include "plssvm/mpi/communicator.hpp" // plssvm::mpi::communicator
1819
#include "plssvm/target_platforms.hpp" // plssvm::list_available_target_platforms
1920
#include "plssvm/verbosity_levels.hpp" // plssvm::verbosity, plssvm::verbosity_level
@@ -24,7 +25,7 @@
2425
#include "fmt/format.h" // fmt::format
2526
#include "fmt/ranges.h" // fmt::join
2627

27-
#include <cstdlib> // std::exit, EXIT_SUCCESS, EXIT_FAILURE, std::atexit
28+
#include <cstdlib> // EXIT_SUCCESS, EXIT_FAILURE
2829
#include <exception> // std::exception
2930
#include <filesystem> // std::filesystem::path
3031
#include <iostream> // std::cout, std::cerr, std::endl
@@ -38,13 +39,6 @@ parser_predict::parser_predict(const mpi::communicator &comm, int argc, char **a
3839
PLSSVM_ASSERT(argc >= 1, fmt::format("At least one argument is always given (the executable name), but argc is {}!", argc));
3940
PLSSVM_ASSERT(argv != nullptr, "At least one argument is always given (the executable name), but argv is a nullptr!");
4041

41-
// register a std::atexit handler since our parser may directly call std::exit
42-
std::atexit([]() {
43-
if (mpi::is_active()) {
44-
mpi::finalize();
45-
}
46-
});
47-
4842
// setup command line parser with all available options
4943
cxxopts::Options options("plssvm-predict", "LS-SVM with multiple (GPU-)backends");
5044
options
@@ -89,23 +83,23 @@ parser_predict::parser_predict(const mpi::communicator &comm, int argc, char **a
8983
std::cerr << fmt::format(fmt::fg(fmt::color::red), "ERROR: {}\n", e.what()) << std::endl;
9084
std::cout << options.help() << std::endl;
9185
}
92-
std::exit(EXIT_FAILURE);
86+
throw cmd_parser_exit{ EXIT_FAILURE };
9387
}
9488

9589
// print help message and exit
9690
if (result.count("help")) {
9791
if (comm.is_main_rank()) {
9892
std::cout << options.help() << std::endl;
9993
}
100-
std::exit(EXIT_SUCCESS);
94+
throw cmd_parser_exit{ EXIT_SUCCESS };
10195
}
10296

10397
// print version info
10498
if (result.count("version")) {
10599
if (comm.is_main_rank()) {
106100
std::cout << version::detail::get_version_info("plssvm-predict") << std::endl;
107101
}
108-
std::exit(EXIT_SUCCESS);
102+
throw cmd_parser_exit{ EXIT_SUCCESS };
109103
}
110104

111105
// check if the number of positional arguments is not too large
@@ -114,7 +108,7 @@ parser_predict::parser_predict(const mpi::communicator &comm, int argc, char **a
114108
std::cerr << fmt::format(fmt::fg(fmt::color::red), "ERROR: only up to three positional options may be given, but {} (\"{}\") additional option(s) where provided!", result.unmatched().size(), fmt::join(result.unmatched(), " ")) << std::endl;
115109
std::cout << options.help() << std::endl;
116110
}
117-
std::exit(EXIT_FAILURE);
111+
throw cmd_parser_exit{ EXIT_FAILURE };
118112
}
119113

120114
// parse backend_type and cast the value to the respective enum
@@ -189,7 +183,7 @@ parser_predict::parser_predict(const mpi::communicator &comm, int argc, char **a
189183
std::cerr << fmt::format(fmt::fg(fmt::color::red), "ERROR: missing test file!\n") << std::endl;
190184
std::cout << options.help() << std::endl;
191185
}
192-
std::exit(EXIT_FAILURE);
186+
throw cmd_parser_exit{ EXIT_FAILURE };
193187
}
194188
input_filename = result["test"].as<decltype(input_filename)>();
195189

@@ -199,7 +193,7 @@ parser_predict::parser_predict(const mpi::communicator &comm, int argc, char **a
199193
std::cerr << fmt::format(fmt::fg(fmt::color::red), "ERROR: missing model file!\n") << std::endl;
200194
std::cout << options.help() << std::endl;
201195
}
202-
std::exit(EXIT_FAILURE);
196+
throw cmd_parser_exit{ EXIT_FAILURE };
203197
}
204198
model_filename = result["model"].as<decltype(model_filename)>();
205199

@@ -229,7 +223,7 @@ parser_predict::parser_predict(const mpi::communicator &comm, int argc, char **a
229223
std::cerr << fmt::format(fmt::fg(fmt::color::red), "ERROR: the number of load balancing weights ({}) must match the number of MPI ranks ({})!\n", mpi_load_balancing_weights.size(), comm.size()) << std::endl;
230224
std::cout << options.help() << std::endl;
231225
}
232-
std::exit(EXIT_FAILURE);
226+
throw cmd_parser_exit{ EXIT_FAILURE };
233227
}
234228
}
235229
#endif

src/plssvm/detail/cmd/parser_scale.cpp

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#include "plssvm/detail/assert.hpp" // PLSSVM_ASSERT
1212
#include "plssvm/detail/logging/mpi_log_untracked.hpp" // plssvm::detail::log_untracked
13+
#include "plssvm/exceptions/exceptions.hpp" // plssvm::cmd_parser_exit
1314
#include "plssvm/mpi/communicator.hpp" // plssvm::mpi::communicator
1415
#include "plssvm/mpi/environment.hpp" // plssvm::mpi::{is_active, finalize}
1516
#include "plssvm/verbosity_levels.hpp" // plssvm::verbosity, plssvm::verbosity_level
@@ -20,7 +21,7 @@
2021
#include "fmt/format.h" // fmt::format
2122
#include "fmt/ranges.h" // fmt::join
2223

23-
#include <cstdlib> // std::exit, EXIT_SUCCESS, EXIT_FAILURE, std::atexit
24+
#include <cstdlib> // EXIT_SUCCESS, EXIT_FAILURE
2425
#include <exception> // std::exception
2526
#include <iostream> // std::cout, std::cerr, std::endl
2627
#include <type_traits> // std::is_same_v
@@ -32,13 +33,6 @@ parser_scale::parser_scale(const mpi::communicator &comm, int argc, char **argv)
3233
PLSSVM_ASSERT(argc >= 1, fmt::format("At least one argument is always given (the executable name), but argc is {}!", argc));
3334
PLSSVM_ASSERT(argv != nullptr, "At least one argument is always given (the executable name), but argv is a nullptr!");
3435

35-
// register a std::atexit handler since our parser may directly call std::exit
36-
std::atexit([]() {
37-
if (mpi::is_active()) {
38-
mpi::finalize();
39-
}
40-
});
41-
4236
// setup command line parser with all available options
4337
cxxopts::Options options("plssvm-scale", "LS-SVM with multiple (GPU-)backends");
4438
options
@@ -76,23 +70,23 @@ parser_scale::parser_scale(const mpi::communicator &comm, int argc, char **argv)
7670
std::cerr << fmt::format(fmt::fg(fmt::color::red), "ERROR: {}\n", e.what()) << std::endl;
7771
std::cout << options.help() << std::endl;
7872
}
79-
std::exit(EXIT_FAILURE);
73+
throw cmd_parser_exit{ EXIT_FAILURE };
8074
}
8175

8276
// print help message and exit
8377
if (result.count("help")) {
8478
if (comm.is_main_rank()) {
8579
std::cout << options.help() << std::endl;
8680
}
87-
std::exit(EXIT_SUCCESS);
81+
throw cmd_parser_exit{ EXIT_SUCCESS };
8882
}
8983

9084
// print version info
9185
if (result.count("version")) {
9286
if (comm.is_main_rank()) {
9387
std::cout << version::detail::get_version_info("plssvm-scale", false) << std::endl;
9488
}
95-
std::exit(EXIT_SUCCESS);
89+
throw cmd_parser_exit{ EXIT_SUCCESS };
9690
}
9791

9892
// check if the number of positional arguments is not too large
@@ -101,7 +95,7 @@ parser_scale::parser_scale(const mpi::communicator &comm, int argc, char **argv)
10195
std::cerr << fmt::format(fmt::fg(fmt::color::red), "ERROR: only up to two positional options may be given, but {} (\"{}\") additional option(s) where provided!\n", result.unmatched().size(), fmt::join(result.unmatched(), " ")) << std::endl;
10296
std::cout << options.help() << std::endl;
10397
}
104-
std::exit(EXIT_FAILURE);
98+
throw cmd_parser_exit{ EXIT_FAILURE };
10599
}
106100

107101
// parse the lowest allowed value
@@ -116,7 +110,7 @@ parser_scale::parser_scale(const mpi::communicator &comm, int argc, char **argv)
116110
std::cerr << fmt::format(fmt::fg(fmt::color::red), "ERROR: invalid scaling range [lower, upper] with [{}, {}]!\n", lower, upper) << std::endl;
117111
std::cout << options.help() << std::endl;
118112
}
119-
std::exit(EXIT_FAILURE);
113+
throw cmd_parser_exit{ EXIT_FAILURE };
120114
}
121115

122116
// parse the file format
@@ -150,7 +144,7 @@ parser_scale::parser_scale(const mpi::communicator &comm, int argc, char **argv)
150144
std::cerr << fmt::format(fmt::fg(fmt::color::red), "ERROR: missing input file!\n") << std::endl;
151145
std::cout << options.help() << std::endl;
152146
}
153-
std::exit(EXIT_FAILURE);
147+
throw cmd_parser_exit{ EXIT_FAILURE };
154148
}
155149
input_filename = result["input"].as<decltype(input_filename)>();
156150

@@ -165,7 +159,7 @@ parser_scale::parser_scale(const mpi::communicator &comm, int argc, char **argv)
165159
std::cerr << fmt::format(fmt::fg(fmt::color::red), "ERROR: cannot use -s (--save_filename) and -r (--restore_filename) simultaneously!\n") << std::endl;
166160
std::cout << options.help() << std::endl;
167161
}
168-
std::exit(EXIT_FAILURE);
162+
throw cmd_parser_exit{ EXIT_FAILURE };
169163
}
170164

171165
// parse the file name to save the calculated weights to

src/plssvm/detail/cmd/parser_train.cpp

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "plssvm/detail/assert.hpp" // PLSSVM_ASSERT
1818
#include "plssvm/detail/logging/mpi_log_untracked.hpp" // plssvm::detail::log_untracked
1919
#include "plssvm/detail/utility.hpp" // plssvm::detail::to_underlying
20+
#include "plssvm/exceptions/exceptions.hpp" // plssvm::cmd_parser_exit
2021
#include "plssvm/gamma.hpp" // plssvm::get_gamma_string
2122
#include "plssvm/kernel_function_types.hpp" // plssvm::kernel_type_to_math_string
2223
#include "plssvm/mpi/communicator.hpp" // plssvm::mpi::communicator
@@ -31,7 +32,7 @@
3132
#include "fmt/format.h" // fmt::format
3233
#include "fmt/ranges.h" // fmt::join
3334

34-
#include <cstdlib> // std::exit, EXIT_SUCCESS, EXIT_FAILURE, std::atexit
35+
#include <cstdlib> // EXIT_SUCCESS, EXIT_FAILURE
3536
#include <exception> // std::exception
3637
#include <filesystem> // std::filesystem::path
3738
#include <iostream> // std::cout, std::cerr, std::endl
@@ -47,13 +48,6 @@ parser_train::parser_train(const mpi::communicator &comm, int argc, char **argv)
4748
PLSSVM_ASSERT(argc >= 1, fmt::format("At least one argument is always given (the executable name), but argc is {}!", argc));
4849
PLSSVM_ASSERT(argv != nullptr, "At least one argument is always given (the executable name), but argv is a nullptr!");
4950

50-
// register a std::atexit handler since our parser may directly call std::exit
51-
std::atexit([]() {
52-
if (mpi::is_active()) {
53-
mpi::finalize();
54-
}
55-
});
56-
5751
// create the help message for the kernel function type
5852
const auto kernel_type_to_help_entry = [](const kernel_function_type kernel) {
5953
return fmt::format("\t {} -- {}: {}\n", detail::to_underlying(kernel), kernel, kernel_function_type_to_math_string(kernel));
@@ -117,23 +111,23 @@ parser_train::parser_train(const mpi::communicator &comm, int argc, char **argv)
117111
std::cerr << fmt::format(fmt::fg(fmt::color::red), "ERROR: {}\n", e.what()) << std::endl;
118112
std::cout << options.help() << std::endl;
119113
}
120-
std::exit(EXIT_FAILURE);
114+
throw cmd_parser_exit{ EXIT_FAILURE };
121115
}
122116

123117
// print help message and exit
124118
if (result.count("help")) {
125119
if (comm.is_main_rank()) {
126120
std::cout << options.help() << std::endl;
127121
}
128-
std::exit(EXIT_SUCCESS);
122+
throw cmd_parser_exit{ EXIT_SUCCESS };
129123
}
130124

131125
// print version info
132126
if (result.count("version")) {
133127
if (comm.is_main_rank()) {
134128
std::cout << version::detail::get_version_info("plssvm-train") << std::endl;
135129
}
136-
std::exit(EXIT_SUCCESS);
130+
throw cmd_parser_exit{ EXIT_SUCCESS };
137131
}
138132

139133
// check if the number of positional arguments is not too large
@@ -142,7 +136,7 @@ parser_train::parser_train(const mpi::communicator &comm, int argc, char **argv)
142136
std::cerr << fmt::format(fmt::fg(fmt::color::red), "ERROR: only up to two positional options may be given, but {} (\"{}\") additional option(s) where provided!\n", result.unmatched().size(), fmt::join(result.unmatched(), " ")) << std::endl;
143137
std::cout << options.help() << std::endl;
144138
}
145-
std::exit(EXIT_FAILURE);
139+
throw cmd_parser_exit{ EXIT_FAILURE };
146140
}
147141

148142
// parse svm_type and cast the value to the respective enum
@@ -169,7 +163,7 @@ parser_train::parser_train(const mpi::communicator &comm, int argc, char **argv)
169163
std::cerr << fmt::format(fmt::fg(fmt::color::red), "ERROR: gamma must be greater than 0.0, but is {}!\n", std::get<real_type>(gamma_input)) << std::endl;
170164
std::cout << options.help() << std::endl;
171165
}
172-
std::exit(EXIT_FAILURE);
166+
throw cmd_parser_exit{ EXIT_FAILURE };
173167
}
174168
// provided gamma was legal -> override default value
175169
csvm_params.gamma = gamma_input;
@@ -199,7 +193,7 @@ parser_train::parser_train(const mpi::communicator &comm, int argc, char **argv)
199193
std::cerr << fmt::format(fmt::fg(fmt::color::red), "ERROR: max_iter must be greater than 0, but is {}!\n", max_iter_input) << std::endl;
200194
std::cout << options.help() << std::endl;
201195
}
202-
std::exit(EXIT_FAILURE);
196+
throw cmd_parser_exit{ EXIT_FAILURE };
203197
}
204198
// provided max_iter was legal -> override default value
205199
max_iter = static_cast<decltype(max_iter)>(max_iter_input);
@@ -309,7 +303,7 @@ parser_train::parser_train(const mpi::communicator &comm, int argc, char **argv)
309303
std::cerr << fmt::format(fmt::fg(fmt::color::red), "ERROR: missing input file!\n") << std::endl;
310304
std::cout << options.help() << std::endl;
311305
}
312-
std::exit(EXIT_FAILURE);
306+
throw cmd_parser_exit{ EXIT_FAILURE };
313307
}
314308
input_filename = result["input"].as<decltype(input_filename)>();
315309

@@ -339,7 +333,7 @@ parser_train::parser_train(const mpi::communicator &comm, int argc, char **argv)
339333
std::cerr << fmt::format(fmt::fg(fmt::color::red), "ERROR: the number of load balancing weights ({}) must match the number of MPI ranks ({})!\n", mpi_load_balancing_weights.size(), comm.size()) << std::endl;
340334
std::cout << options.help() << std::endl;
341335
}
342-
std::exit(EXIT_FAILURE);
336+
throw cmd_parser_exit{ EXIT_FAILURE };
343337
}
344338
}
345339
#endif

src/plssvm/exceptions/exceptions.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ std::string exception::what_with_loc() const {
3939
loc_.line());
4040
}
4141

42+
cmd_parser_exit::cmd_parser_exit(const int exit_code, source_location loc) :
43+
exception{ fmt::format("exit code: {}", exit_code), "cmd_parser_exit", loc },
44+
exit_code_{ exit_code } { }
45+
4246
invalid_parameter_exception::invalid_parameter_exception(const std::string &msg, source_location loc) :
4347
exception{ msg, "invalid_parameter_exception", loc } { }
4448

0 commit comments

Comments
 (0)