diff --git a/src/DynareMain.cc b/src/DynareMain.cc
index c1cf3eae9785378fe9672e4c2ee6e542d7eca847..4acd1bd2359929e991029a2ff383aff1402641ce 100644
--- a/src/DynareMain.cc
+++ b/src/DynareMain.cc
@@ -535,12 +535,11 @@ main(int argc, char **argv)
                            nointeractive, config_file, check_model_changes, minimal_workspace, compute_xrefs,
                            mexext, matlabroot, dynareroot, onlymodel, gui, notime);
 
-  /* Ensures that the preprocessor final message is printed after the end of
-     compilation (and is not printed in case of compilation failure); also
-     avoids potential issues with destroying the thread synchronization
-     mechanism too soon. */
+  /* Ensures that workers are not destroyed before they finish compiling.
+     Also ensures that the preprocessor final message is printed after the end of
+     compilation (and is not printed in case of compilation failure). */
   if (mod_file->use_dll)
-    ModelTree::terminateMEXCompilationWorkers();
+    ModelTree::waitForMEXCompilationWorkers();
 
   cout << "Preprocessing completed." << endl;
   return EXIT_SUCCESS;
diff --git a/src/ModelTree.cc b/src/ModelTree.cc
index e9cd191fe12f6b6a52f4cbd280a52b83d63ac345..acef5a5ac19fe935864c23fa0633f28815125e5c 100644
--- a/src/ModelTree.cc
+++ b/src/ModelTree.cc
@@ -37,11 +37,15 @@
 #include <utility>
 #include <algorithm>
 
-vector<jthread> ModelTree::mex_compilation_workers {};
-condition_variable ModelTree::mex_compilation_cv;
+/* NB: The workers must be listed *after* all the other static variables
+   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;
 vector<tuple<filesystem::path, set<filesystem::path>, string>> ModelTree::mex_compilation_queue;
 set<filesystem::path> ModelTree::mex_compilation_done;
+vector<jthread> ModelTree::mex_compilation_workers;
 
 void
 ModelTree::copyHelper(const ModelTree &m)
@@ -1892,64 +1896,52 @@ ModelTree::initializeMEXCompilationWorkers(int numworkers)
     mex_compilation_workers.emplace_back([](stop_token stoken)
     {
       unique_lock<mutex> lk {mex_compilation_mut};
+      filesystem::path output;
+      string cmd;
 
-    look_for_job:
-      for (auto it {mex_compilation_queue.begin()}; it != mex_compilation_queue.end(); ++it)
-        {
-          /* The following is a copy and not a reference, because we need it
-             after erasing it, and also after releasing the lock (at which
-             point the mex_compilation_queue may be modified by others). */
-          const auto [output, prerequisites, cmd] {*it};
-          if (includes(mex_compilation_done.begin(), mex_compilation_done.end(),
+      /* Look for an object to compile, whose prerequisites are already
+         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. */
+      auto pick_job = [&cmd, &output]
+      {
+        for (auto it {mex_compilation_queue.begin()}; it != mex_compilation_queue.end(); ++it)
+          if (const auto &prerequisites {get<1>(*it)}; // Will become dangling after erase
+              includes(mex_compilation_done.begin(), mex_compilation_done.end(),
                        prerequisites.begin(), prerequisites.end()))
             {
+              output = get<0>(*it);
+              cmd = get<2>(*it);
               mex_compilation_queue.erase(it);
-              lk.unlock(); // After that point, the iterator may become invalid
-              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 true;
             }
-        }
-
-      if (stoken.stop_requested())
-        return;
+        return false;
+      };
 
-      mex_compilation_cv.wait(lk);
-
-      goto look_for_job;
+      while (!stoken.stop_requested())
+        if (mex_compilation_cv.wait(lk, stoken, pick_job))
+          {
+            lk.unlock();
+            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::waitForMEXCompilationWorkers().*/
+            mex_compilation_cv.notify_all();
+          }
     });
 }
 
 void
-ModelTree::terminateMEXCompilationWorkers()
+ModelTree::waitForMEXCompilationWorkers()
 {
-  // Wait until the queue is empty
   unique_lock<mutex> lk {mex_compilation_mut};
   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
diff --git a/src/ModelTree.hh b/src/ModelTree.hh
index 1f3bfe737a420e679de1e684d86e0e1f7b045e0c..fcc59547e4358869c4f1f374e4f7c1256514ce67 100644
--- a/src/ModelTree.hh
+++ b/src/ModelTree.hh
@@ -345,7 +345,7 @@ private:
   /* The following variables implement the thread synchronization mechanism for
      limiting the number of concurrent GCC processes and tracking dependencies
      between object files. */
-  static condition_variable mex_compilation_cv;
+  static condition_variable_any mex_compilation_cv;
   static mutex mex_compilation_mut;
   /* Object/MEX files waiting to be compiled (with their prerequisites as 2nd
      element and compilation command as the 3rd element) */
@@ -557,9 +557,8 @@ public:
   // Initialize the MEX compilation workers
   static void initializeMEXCompilationWorkers(int numworkers);
 
-  /* Terminates all MEX compilation workers (after they have emptied the
-     waiting queue) */
-  static void terminateMEXCompilationWorkers();
+  // Waits until the MEX compilation queue is empty
+  static void waitForMEXCompilationWorkers();
 
   //! Returns all the equation tags associated to an equation
   map<string, string>