Skip to content

Commit 9591f8a

Browse files
authored
Merge pull request #2558 from ERGO-Code/fix-2534
Added `Highs::closeLogFile()`, and calling this before returning from the command line executable
2 parents b2ceebf + 2e5d7eb commit 9591f8a

File tree

4 files changed

+59
-28
lines changed

4 files changed

+59
-28
lines changed

app/RunHighs.cpp

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,23 @@
88
/**@file ../app/RunHighs.cpp
99
* @brief HiGHS main
1010
*/
11+
#include <cstdio> // For fclose
12+
1113
#include "Highs.h"
1214
#include "HighsRuntimeOptions.h"
1315

16+
int runHighsReturn(Highs& highs, const int status) {
17+
// Close any log file explicitly
18+
highs.closeLogFile();
19+
// Check that the log file has been closed
20+
assert(highs.getOptions().log_options.log_stream == nullptr);
21+
return status;
22+
}
23+
24+
int runHighsReturn(Highs& highs, const HighsStatus status) {
25+
return runHighsReturn(highs, int(status));
26+
}
27+
1428
int main(int argc, char** argv) {
1529
// Create the Highs instance.
1630
Highs highs;
@@ -40,31 +54,31 @@ int main(int argc, char** argv) {
4054
app.parse(argc, argv);
4155
} catch (const CLI::CallForHelp& e) {
4256
std::cout << app.help() << std::endl;
43-
return 0;
57+
return runHighsReturn(highs, 0);
4458
} catch (const CLI::CallForAllHelp& e) {
4559
std::cout << app.help();
46-
return 0;
60+
return runHighsReturn(highs, 0);
4761
} catch (const CLI::RequiredError& e) {
4862
std::cout << "Please specify filename in .mps|.lp|.ems format."
4963
<< std::endl;
50-
return (int)HighsStatus::kError;
64+
return runHighsReturn(highs, HighsStatus::kError);
5165
} catch (const CLI::ExtrasError& e) {
5266
std::cout << e.what() << std::endl;
5367
std::cout << "Multiple files not supported." << std::endl;
54-
return (int)HighsStatus::kError;
68+
return runHighsReturn(highs, HighsStatus::kError);
5569
} catch (const CLI::ArgumentMismatch& e) {
5670
std::cout << e.what() << std::endl;
5771
std::cout << "Too many arguments provided. Please provide only one."
5872
<< std::endl;
59-
return (int)HighsStatus::kError;
73+
return runHighsReturn(highs, HighsStatus::kError);
6074
} catch (const CLI::ParseError& e) {
6175
std::cout << e.what() << std::endl;
6276
// app.exit() should be called from main.
63-
return app.exit(e);
77+
return runHighsReturn(highs, app.exit(e));
6478
}
6579

6680
if (!loadOptions(app, log_options, cmd_options, loaded_options))
67-
return (int)HighsStatus::kError;
81+
return runHighsReturn(highs, HighsStatus::kError);
6882

6983
// Open the app log file - unless output_flag is false, to avoid
7084
// creating an empty file. It does nothing if its name is "".
@@ -86,13 +100,13 @@ int main(int argc, char** argv) {
86100
HighsStatus read_status = highs.readModel(cmd_options.model_file);
87101
if (read_status == HighsStatus::kError) {
88102
highsLogUser(log_options, HighsLogType::kInfo, "Error loading file\n");
89-
return (int)read_status;
103+
return runHighsReturn(highs, read_status);
90104
}
91105

92106
if (options.write_presolved_model_file != "") {
93107
// Run presolve and write the presolved model to a file
94108
HighsStatus status = highs.presolve();
95-
if (status == HighsStatus::kError) return int(status);
109+
if (status == HighsStatus::kError) return runHighsReturn(highs, status);
96110
HighsPresolveStatus model_presolve_status = highs.getModelPresolveStatus();
97111
const bool ok_to_write =
98112
model_presolve_status == HighsPresolveStatus::kNotReduced ||
@@ -102,18 +116,18 @@ int main(int argc, char** argv) {
102116
if (!ok_to_write) {
103117
highsLogUser(log_options, HighsLogType::kInfo,
104118
"No presolved model to write to file\n");
105-
return int(status);
119+
return runHighsReturn(highs, status);
106120
}
107121
status = highs.writePresolvedModel(options.write_presolved_model_file);
108-
return int(status);
122+
return runHighsReturn(highs, status);
109123
}
110124
// Solve the model
111125
HighsStatus run_status = highs.run();
112-
if (run_status == HighsStatus::kError) return int(run_status);
126+
if (run_status == HighsStatus::kError) runHighsReturn(highs, run_status);
113127

114128
// Shut down task executor for explicit release of memory.
115129
// Valgrind still reachable otherwise.
116130
highs.resetGlobalScheduler(true);
117131

118-
return (int)run_status;
132+
return runHighsReturn(highs, run_status);
119133
}

highs/Highs.h

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,7 @@ const char* highsGithash();
4242
class Highs {
4343
public:
4444
Highs();
45-
virtual ~Highs() {
46-
FILE* log_stream = options_.log_options.log_stream;
47-
if (log_stream != nullptr) {
48-
assert(log_stream != stdout);
49-
fclose(log_stream);
50-
}
51-
}
45+
virtual ~Highs() { this->closeLogFile(); }
5246

5347
/**
5448
* @brief Return the version as a string
@@ -1216,6 +1210,11 @@ class Highs {
12161210
*/
12171211
HighsStatus openLogFile(const std::string& log_file = "");
12181212

1213+
/**
1214+
* @brief Close any open log file
1215+
*/
1216+
HighsStatus closeLogFile();
1217+
12191218
/**
12201219
* @brief Interpret common qualifiers to string values
12211220
*/

highs/lp_data/Highs.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4762,6 +4762,21 @@ HighsStatus Highs::openLogFile(const std::string& log_file) {
47624762
return HighsStatus::kOk;
47634763
}
47644764

4765+
HighsStatus Highs::closeLogFile() {
4766+
// Work with a copy of the pointer for brevity
4767+
FILE* log_stream = this->options_.log_options.log_stream;
4768+
if (log_stream != nullptr) {
4769+
assert(log_stream != stdout);
4770+
fflush(log_stream);
4771+
fclose(log_stream);
4772+
// Set the true log_stream to nullptr to give a test whether it
4773+
// has been closed (and avoid trying to close it again which
4774+
// causes an error)
4775+
this->options_.log_options.log_stream = nullptr;
4776+
}
4777+
return HighsStatus::kOk;
4778+
}
4779+
47654780
void Highs::resetGlobalScheduler(bool blocking) {
47664781
HighsTaskExecutor::shutdown(blocking);
47674782
}

highs/lp_data/HighsOptions.cpp

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,22 @@ void highsOpenLogFile(HighsLogOptions& log_options,
2525
OptionStatus status =
2626
getOptionIndex(log_options, "log_file", option_records, index);
2727
assert(status == OptionStatus::kOk);
28-
if (log_options.log_stream != NULL) {
28+
if (log_options.log_stream != nullptr) {
2929
// Current log file stream is not null, so flush and close it
3030
fflush(log_options.log_stream);
3131
fclose(log_options.log_stream);
32+
// Set the stream to null to give a test whether it has been
33+
// closed (and avoid trying to close it again which causes an
34+
// error)
35+
log_options.log_stream = nullptr;
3236
}
33-
if (log_file.compare("")) {
34-
// New log file name is not empty, so open it, appending if
35-
// possible
37+
assert(!log_options.log_stream);
38+
// If new log file name is not empty, open it, appending if possible
39+
//
40+
// If fopen fails then it returns nullptr, so log_file is open for
41+
// writing or nullptr
42+
if (log_file.compare(""))
3643
log_options.log_stream = fopen(log_file.c_str(), "a");
37-
} else {
38-
// New log file name is empty, so set the stream to null
39-
log_options.log_stream = NULL;
40-
}
4144
OptionRecordString& option = *(OptionRecordString*)option_records[index];
4245
option.assignvalue(log_file);
4346
}

0 commit comments

Comments
 (0)