Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 35 additions & 13 deletions src/search/planner.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<int>(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. */
Expand Down
23 changes: 21 additions & 2 deletions src/search/utils/system_unix.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);

Expand Down
Loading