use_dll: ensure proper cleanup of thread workers threads in case of early exit (e.g. upon failure)
parent
781e10c24b
commit
9f96db89ba
|
@ -535,12 +535,11 @@ main(int argc, char **argv)
|
||||||
nointeractive, config_file, check_model_changes, minimal_workspace, compute_xrefs,
|
nointeractive, config_file, check_model_changes, minimal_workspace, compute_xrefs,
|
||||||
mexext, matlabroot, dynareroot, onlymodel, gui, notime);
|
mexext, matlabroot, dynareroot, onlymodel, gui, notime);
|
||||||
|
|
||||||
/* Ensures that the preprocessor final message is printed after the end of
|
/* Ensures that workers are not destroyed before they finish compiling.
|
||||||
compilation (and is not printed in case of compilation failure); also
|
Also ensures that the preprocessor final message is printed after the end of
|
||||||
avoids potential issues with destroying the thread synchronization
|
compilation (and is not printed in case of compilation failure). */
|
||||||
mechanism too soon. */
|
|
||||||
if (mod_file->use_dll)
|
if (mod_file->use_dll)
|
||||||
ModelTree::terminateMEXCompilationWorkers();
|
ModelTree::waitForMEXCompilationWorkers();
|
||||||
|
|
||||||
cout << "Preprocessing completed." << endl;
|
cout << "Preprocessing completed." << endl;
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
|
|
|
@ -37,11 +37,15 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
vector<jthread> ModelTree::mex_compilation_workers {};
|
/* NB: The workers must be listed *after* all the other static variables
|
||||||
condition_variable ModelTree::mex_compilation_cv;
|
related to MEX compilation, so that when the preprocessor exits, the workers
|
||||||
|
are destroyed *before* those variables (since the former rely on the latter
|
||||||
|
for their functioning). */
|
||||||
|
condition_variable_any ModelTree::mex_compilation_cv;
|
||||||
mutex ModelTree::mex_compilation_mut;
|
mutex ModelTree::mex_compilation_mut;
|
||||||
vector<tuple<filesystem::path, set<filesystem::path>, string>> ModelTree::mex_compilation_queue;
|
vector<tuple<filesystem::path, set<filesystem::path>, string>> ModelTree::mex_compilation_queue;
|
||||||
set<filesystem::path> ModelTree::mex_compilation_done;
|
set<filesystem::path> ModelTree::mex_compilation_done;
|
||||||
|
vector<jthread> ModelTree::mex_compilation_workers;
|
||||||
|
|
||||||
void
|
void
|
||||||
ModelTree::copyHelper(const ModelTree &m)
|
ModelTree::copyHelper(const ModelTree &m)
|
||||||
|
@ -1892,64 +1896,52 @@ ModelTree::initializeMEXCompilationWorkers(int numworkers)
|
||||||
mex_compilation_workers.emplace_back([](stop_token stoken)
|
mex_compilation_workers.emplace_back([](stop_token stoken)
|
||||||
{
|
{
|
||||||
unique_lock<mutex> lk {mex_compilation_mut};
|
unique_lock<mutex> lk {mex_compilation_mut};
|
||||||
|
filesystem::path output;
|
||||||
|
string cmd;
|
||||||
|
|
||||||
look_for_job:
|
/* Look for an object to compile, whose prerequisites are already
|
||||||
for (auto it {mex_compilation_queue.begin()}; it != mex_compilation_queue.end(); ++it)
|
compiled. If found, remove it from the queue, save the output path and
|
||||||
{
|
the compilation command, and return true. Must be run under the lock. */
|
||||||
/* The following is a copy and not a reference, because we need it
|
auto pick_job = [&cmd, &output]
|
||||||
after erasing it, and also after releasing the lock (at which
|
{
|
||||||
point the mex_compilation_queue may be modified by others). */
|
for (auto it {mex_compilation_queue.begin()}; it != mex_compilation_queue.end(); ++it)
|
||||||
const auto [output, prerequisites, cmd] {*it};
|
if (const auto &prerequisites {get<1>(*it)}; // Will become dangling after erase
|
||||||
if (includes(mex_compilation_done.begin(), mex_compilation_done.end(),
|
includes(mex_compilation_done.begin(), mex_compilation_done.end(),
|
||||||
prerequisites.begin(), prerequisites.end()))
|
prerequisites.begin(), prerequisites.end()))
|
||||||
{
|
{
|
||||||
|
output = get<0>(*it);
|
||||||
|
cmd = get<2>(*it);
|
||||||
mex_compilation_queue.erase(it);
|
mex_compilation_queue.erase(it);
|
||||||
lk.unlock(); // After that point, the iterator may become invalid
|
return true;
|
||||||
if (system(cmd.c_str()))
|
|
||||||
{
|
|
||||||
cerr << "Compilation failed" << endl;
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
lk.lock();
|
|
||||||
mex_compilation_done.insert(output);
|
|
||||||
/* The object just compiled may be a prerequisite for several
|
|
||||||
other objects, so notify all waiting workers. Also needed to
|
|
||||||
notify the main thread when in
|
|
||||||
ModelTree::terminateMEXCompilationWorkers(). */
|
|
||||||
mex_compilation_cv.notify_all();
|
|
||||||
goto look_for_job;
|
|
||||||
}
|
}
|
||||||
}
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
if (stoken.stop_requested())
|
while (!stoken.stop_requested())
|
||||||
return;
|
if (mex_compilation_cv.wait(lk, stoken, pick_job))
|
||||||
|
{
|
||||||
mex_compilation_cv.wait(lk);
|
lk.unlock();
|
||||||
|
if (system(cmd.c_str()))
|
||||||
goto look_for_job;
|
{
|
||||||
|
cerr << "Compilation failed" << endl;
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
lk.lock();
|
||||||
|
mex_compilation_done.insert(output);
|
||||||
|
/* The object just compiled may be a prerequisite for several
|
||||||
|
other objects, so notify all waiting workers. Also needed to
|
||||||
|
notify the main thread when in
|
||||||
|
ModelTree::waitForMEXCompilationWorkers().*/
|
||||||
|
mex_compilation_cv.notify_all();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ModelTree::terminateMEXCompilationWorkers()
|
ModelTree::waitForMEXCompilationWorkers()
|
||||||
{
|
{
|
||||||
// Wait until the queue is empty
|
|
||||||
unique_lock<mutex> lk {mex_compilation_mut};
|
unique_lock<mutex> lk {mex_compilation_mut};
|
||||||
mex_compilation_cv.wait(lk, [] { return mex_compilation_queue.empty(); });
|
mex_compilation_cv.wait(lk, [] { return mex_compilation_queue.empty(); });
|
||||||
|
|
||||||
/* Request stop while still holding the lock, so we are sure that workers are
|
|
||||||
either compiling or waiting right now. Otherwise there could theoretically
|
|
||||||
be a race condition where the condition variable is notified just after
|
|
||||||
the thread has checked for its stoken, and just before it begins waiting;
|
|
||||||
this would be deadlock. */
|
|
||||||
for (auto &it : mex_compilation_workers)
|
|
||||||
it.request_stop();
|
|
||||||
|
|
||||||
lk.unlock();
|
|
||||||
|
|
||||||
mex_compilation_cv.notify_all();
|
|
||||||
for (auto &it : mex_compilation_workers)
|
|
||||||
it.join();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -345,7 +345,7 @@ private:
|
||||||
/* The following variables implement the thread synchronization mechanism for
|
/* The following variables implement the thread synchronization mechanism for
|
||||||
limiting the number of concurrent GCC processes and tracking dependencies
|
limiting the number of concurrent GCC processes and tracking dependencies
|
||||||
between object files. */
|
between object files. */
|
||||||
static condition_variable mex_compilation_cv;
|
static condition_variable_any mex_compilation_cv;
|
||||||
static mutex mex_compilation_mut;
|
static mutex mex_compilation_mut;
|
||||||
/* Object/MEX files waiting to be compiled (with their prerequisites as 2nd
|
/* Object/MEX files waiting to be compiled (with their prerequisites as 2nd
|
||||||
element and compilation command as the 3rd element) */
|
element and compilation command as the 3rd element) */
|
||||||
|
@ -557,9 +557,8 @@ public:
|
||||||
// Initialize the MEX compilation workers
|
// Initialize the MEX compilation workers
|
||||||
static void initializeMEXCompilationWorkers(int numworkers);
|
static void initializeMEXCompilationWorkers(int numworkers);
|
||||||
|
|
||||||
/* Terminates all MEX compilation workers (after they have emptied the
|
// Waits until the MEX compilation queue is empty
|
||||||
waiting queue) */
|
static void waitForMEXCompilationWorkers();
|
||||||
static void terminateMEXCompilationWorkers();
|
|
||||||
|
|
||||||
//! Returns all the equation tags associated to an equation
|
//! Returns all the equation tags associated to an equation
|
||||||
map<string, string>
|
map<string, string>
|
||||||
|
|
Loading…
Reference in New Issue