use_dll: fixes to parallel compilation

— No longer call std::exit() from threads when compilation fails, that function
  is marked as not thread-safe under GNU/Linux; and it leads to deadlocks under
  Windows. Rather store the list of failed objects, and exit with a message and
  an error code from the main thread when that list is not empty at the end of
  preprocessing.
– Fix the condition used for waiting until all compilation threads finish;
  checking that the queue is empty is not enough, since a compilation may be
  ongoing. So also track objects whose compilation is ongoing.
master
Sébastien Villemot 2022-12-20 14:47:32 +01:00
parent f349e22f4c
commit 21fcfa7758
No known key found for this signature in database
GPG Key ID: 2CECE9350ECEBE4A
2 changed files with 25 additions and 9 deletions

View File

@ -44,7 +44,7 @@
condition_variable_any ModelTree::mex_compilation_cv; 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_ongoing, ModelTree::mex_compilation_done, ModelTree::mex_compilation_failed;
vector<jthread> ModelTree::mex_compilation_workers; vector<jthread> ModelTree::mex_compilation_workers;
void void
@ -1955,6 +1955,7 @@ ModelTree::initializeMEXCompilationWorkers(int numworkers)
output = get<0>(*it); output = get<0>(*it);
cmd = get<2>(*it); cmd = get<2>(*it);
mex_compilation_queue.erase(it); mex_compilation_queue.erase(it);
mex_compilation_ongoing.insert(output);
return true; return true;
} }
return false; return false;
@ -1964,13 +1965,13 @@ ModelTree::initializeMEXCompilationWorkers(int numworkers)
if (mex_compilation_cv.wait(lk, stoken, pick_job)) if (mex_compilation_cv.wait(lk, stoken, pick_job))
{ {
lk.unlock(); lk.unlock();
if (system(cmd.c_str())) int r { system(cmd.c_str()) };
{
cerr << "Compilation failed" << endl;
exit(EXIT_FAILURE);
}
lk.lock(); lk.lock();
mex_compilation_done.insert(output); mex_compilation_ongoing.erase(output);
if (r)
mex_compilation_failed.insert(output);
else
mex_compilation_done.insert(output);
/* The object just compiled may be a prerequisite for several /* The object just compiled may be a prerequisite for several
other objects, so notify all waiting workers. Also needed to other objects, so notify all waiting workers. Also needed to
notify the main thread when in notify the main thread when in
@ -1984,7 +1985,18 @@ void
ModelTree::waitForMEXCompilationWorkers() ModelTree::waitForMEXCompilationWorkers()
{ {
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() && mex_compilation_ongoing.empty())
|| !mex_compilation_failed.empty(); });
if (!mex_compilation_failed.empty())
{
cerr << "Compilation failed for: ";
for (const auto &p : mex_compilation_failed)
cerr << p.string() << " ";
cerr << endl;
lk.unlock(); // So that threads can process their stoken
exit(EXIT_FAILURE);
}
} }
void void

View File

@ -439,8 +439,12 @@ private:
/* 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) */
static vector<tuple<filesystem::path, set<filesystem::path>, string>> mex_compilation_queue; static vector<tuple<filesystem::path, set<filesystem::path>, string>> mex_compilation_queue;
// Object/MEX files already compiled // Object/MEX files in the process of being compiled
static set<filesystem::path> mex_compilation_ongoing;
// Object/MEX files already compiled successfully
static set<filesystem::path> mex_compilation_done; static set<filesystem::path> mex_compilation_done;
// Object/MEX files whose compilation failed
static set<filesystem::path> mex_compilation_failed;
/* Compute a pseudo-Jacobian whose all elements are either zero or one, /* Compute a pseudo-Jacobian whose all elements are either zero or one,
depending on whether the variable symbolically appears in the equation. If depending on whether the variable symbolically appears in the equation. If