Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
49 changes: 49 additions & 0 deletions check/TestHighsParallel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <iostream>

#include "catch.hpp"
#include "interfaces/highs_c_api.h"
#include "matrix_multiplication.hpp"
#include "parallel/HighsParallel.h"

Expand Down Expand Up @@ -305,4 +306,52 @@ TEST_CASE("CancelNestedTasks", "[parallel]") {
}

HighsTaskExecutor::shutdown();
}

TEST_CASE("ParallelCApi", "[parallel]") {
// This test exposes a failure in the C api when creating and destroying Highs
// instances on separate threads.
// One thread solves a mip, while the other loses some time before destroying
// the instance. If this causes the scheduler to be terminated (i.e., if
// Highs_destroy calls resetGlobalScheduler) while the first thread is still
// using it, the first thread may crash, hang, or terminate with wrong
// results.

highs::parallel::initialize_scheduler();

std::string model = std::string(HIGHS_DIR) + "/check/instances/egout.mps";
const double expected_obj = 568.1007;
double actual_obj;
double total = 0;

{
highs::parallel::TaskGroup tg;

tg.spawn([&]() {
void* highs = Highs_create();
Highs_setBoolOptionValue(highs, "output_flag", dev_run);
Highs_setStringOptionValue(highs, "parallel", "on");
Highs_readModel(highs, model.c_str());
Highs_run(highs);
Highs_getDoubleInfoValue(highs, "objective_function_value", &actual_obj);
Highs_destroy(highs);
});

tg.spawn([&]() {
void* highs = Highs_create();
for (int i = 0; i < 1e6; ++i) {
total += (double)i * i * i;
}
Highs_destroy(highs);
});

tg.taskWait();

REQUIRE(total > 1e18);
REQUIRE(std::abs(actual_obj - expected_obj) / std::abs(expected_obj) <
1e-4);
}
// TaskGroup destructor must be called before scheduler is killed

HighsTaskExecutor::shutdown(true);
}
1 change: 1 addition & 0 deletions examples/call_highs_from_c_minimal.c
Original file line number Diff line number Diff line change
Expand Up @@ -655,5 +655,6 @@ int main() {
minimal_api();
minimal_api_qp();
full_api();
Highs_resetGlobalScheduler(1);
return 0;
}
10 changes: 0 additions & 10 deletions highs/interfaces/highs_c_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,6 @@ HighsInt Highs_lpCall(const HighsInt num_col, const HighsInt num_row,
if (copy_row_basis) row_basis_status[i] = (HighsInt)basis.row_status[i];
}
}

highs.resetGlobalScheduler(true);

return (HighsInt)status;
}

Expand Down Expand Up @@ -106,9 +103,6 @@ HighsInt Highs_mipCall(const HighsInt num_col, const HighsInt num_row,
row_value[i] = solution.row_value[i];
}
}

highs.resetGlobalScheduler(true);

return (HighsInt)status;
}

Expand Down Expand Up @@ -165,16 +159,12 @@ HighsInt Highs_qpCall(
if (copy_row_basis) row_basis_status[i] = (HighsInt)basis.row_status[i];
}
}

highs.resetGlobalScheduler(true);

return (HighsInt)status;
}

void* Highs_create(void) { return new Highs(); }

void Highs_destroy(void* highs) {
Highs::resetGlobalScheduler(true);
delete (Highs*)highs;
}

Expand Down
Loading