diff --git a/src/search/planner.cc b/src/search/planner.cc index 2f5addf422..7637994129 100644 --- a/src/search/planner.cc +++ b/src/search/planner.cc @@ -44,19 +44,41 @@ int main(int argc, const char **argv) { parse_cmd_line(argc, argv, unit_cost); utils::Timer search_timer; - search_algorithm->search(); - search_timer.stop(); - utils::g_timer.stop(); - - search_algorithm->save_plan_if_necessary(); - search_algorithm->print_statistics(); - utils::g_log << "Search time: " << search_timer << endl; - utils::g_log << "Total time: " << utils::g_timer << endl; - - ExitCode exitcode = search_algorithm->found_solution() - ? ExitCode::SUCCESS - : ExitCode::SEARCH_UNSOLVED_INCOMPLETE; - exit_with(exitcode); + + try { + search_algorithm->search(); + } catch (const utils::ExitException &e) { + + /* To ensure that all destructors are called before the program exits, + we raise an exception in utils::exit_with() and let main() return. */ + + // this branch happens only when ExitException is thrown inside search(). + + search_timer.stop(); + utils::g_timer.stop(); + + // NOTE: The following code may cons because it uses ostreams and strings. + // We currently reserve the space for it with memory padding inside register_event_handlers (1MB). + // TODO: make them reentrant. + search_algorithm->print_statistics(); + utils::g_log << "Search time: " << search_timer << endl; + utils::g_log << "Total time: " << utils::g_timer << endl; + + return static_cast(e.get_exitcode()); + } + + search_timer.stop(); + utils::g_timer.stop(); + + search_algorithm->save_plan_if_necessary(); + search_algorithm->print_statistics(); + utils::g_log << "Search time: " << search_timer << endl; + utils::g_log << "Total time: " << utils::g_timer << endl; + + ExitCode exitcode = search_algorithm->found_solution() + ? ExitCode::SUCCESS + : ExitCode::SEARCH_UNSOLVED_INCOMPLETE; + exit_with(exitcode); } catch (const utils::ExitException &e) { /* To ensure that all destructors are called before the program exits, we raise an exception in utils::exit_with() and let main() return. */ diff --git a/src/search/utils/system_unix.cc b/src/search/utils/system_unix.cc index 3d8739c578..ba9b2b84d1 100644 --- a/src/search/utils/system_unix.cc +++ b/src/search/utils/system_unix.cc @@ -42,6 +42,10 @@ using namespace std; namespace utils { + +static void* emergency_reserve = nullptr; +static const std::size_t reserve_size = 1024 * 1024; // 1MB + static void write_reentrant(int filedescr, const char *message, int len) { while (len > 0) { int written; @@ -164,8 +168,21 @@ static void out_of_memory_handler() { memory is not sufficient, we can consider using sigaltstack to reserve memory for the stack of the signal handler and raising a signal here. */ - write_reentrant_str(STDOUT_FILENO, "Failed to allocate memory.\n"); - exit_with_reentrant(ExitCode::SEARCH_OUT_OF_MEMORY); + // Above is an old comment. The old behavior immediately went to the else branch. + // Instead of exiting immediately, we free the padding memory, + // throw utils::ExitException, catch it in main, and exit gracefully. + // Throwing and catching the exception unwinds the stack, which ensures running the destructors. + // Masataro Asai 2026/03/05 + if (emergency_reserve) { + std::free(emergency_reserve); + emergency_reserve = nullptr; + write_reentrant_str(STDOUT_FILENO, "Failed to allocate memory.\n"); + write_reentrant_str(STDOUT_FILENO, "Trying to exit gracefully.\n"); + exit_with(ExitCode::SEARCH_OUT_OF_MEMORY); + } else { + write_reentrant_str(STDOUT_FILENO, "Failed to allocate memory (second time). Exiting.\n"); + exit_with_reentrant(ExitCode::SEARCH_OUT_OF_MEMORY); + } } static void signal_handler(int signal_number) { @@ -223,6 +240,8 @@ int get_peak_memory_in_kb() { } void register_event_handlers() { + emergency_reserve = std::malloc(reserve_size); + // Terminate when running out of memory. set_new_handler(out_of_memory_handler);