From be0a5da9d57c80fd304d54e8f88c388363269b85 Mon Sep 17 00:00:00 2001
From: Christophe Geuzaine <cgeuzaine@uliege.be>
Date: Mon, 23 May 2022 19:50:11 +0200
Subject: [PATCH] OpenMP forbids leaving block via exception: catch exceptions
 inside the block, and throw the last error outside the block (cf. #1941)

---
 src/geo/GModelIO_MSH4.cpp   | 13 +++++++++++--
 src/mesh/Generator.cpp      | 21 ++++++++++++++++++---
 src/mesh/meshGRegionHxt.cpp | 21 +++++++++++++++------
 3 files changed, 44 insertions(+), 11 deletions(-)

diff --git a/src/geo/GModelIO_MSH4.cpp b/src/geo/GModelIO_MSH4.cpp
index 0fd59119e2..54222d9a17 100644
--- a/src/geo/GModelIO_MSH4.cpp
+++ b/src/geo/GModelIO_MSH4.cpp
@@ -15,6 +15,7 @@
 #include <string>
 #include <cstdlib>
 #include <limits>
+#include <stdexcept>
 
 #include "GmshDefines.h"
 #include "OS.h"
@@ -2847,6 +2848,7 @@ int GModel::_writePartitionedMSH4(const std::string &baseName, double version,
 {
   int nthreads = CTX::instance()->numThreads;
   if(!nthreads) nthreads = Msg::GetMaxThreads();
+  bool exceptions = false;
 #pragma omp parallel for num_threads(nthreads)
   for(std::size_t part = 1; part <= getNumPartitions(); part++) {
     std::ostringstream sstream;
@@ -2860,10 +2862,17 @@ int GModel::_writePartitionedMSH4(const std::string &baseName, double version,
     else {
       Msg::Info("Writing partition %d in file '%s'", part, sstream.str().c_str());
     }
-    _writeMSH4(sstream.str(), version, binary, saveAll, saveParametric,
-               scalingFactor, false, part);
+    try { // OpenMP forbids leaving block via exception
+      _writeMSH4(sstream.str(), version, binary, saveAll, saveParametric,
+                 scalingFactor, false, part);
+    }
+    catch(...) {
+      exceptions = true;
+    }
   }
 
+  if(exceptions) throw std::runtime_error(Msg::GetLastError());
+
   return 1;
 }
 
diff --git a/src/mesh/Generator.cpp b/src/mesh/Generator.cpp
index 4e542c2878..5f87235687 100644
--- a/src/mesh/Generator.cpp
+++ b/src/mesh/Generator.cpp
@@ -5,6 +5,8 @@
 
 #include <stdlib.h>
 #include <stack>
+#include <stdexcept>
+
 #include "GmshConfig.h"
 #include "GmshMessage.h"
 #include "Numeric.h"
@@ -378,12 +380,18 @@ static void Mesh1D(GModel *m)
     }
 
     int nPending = 0;
+    bool exceptions = false;
 #pragma omp parallel for schedule(dynamic) num_threads(nthreads)
     for(size_t K = 0; K < temp.size(); K++) {
       int localPending = 0;
       GEdge *ed = temp[K];
       if(ed->meshStatistics.status == GEdge::PENDING) {
-        ed->mesh(true);
+        try{ // OpenMP forbids leaving block via exception
+          ed->mesh(true);
+        }
+        catch(...){
+          exceptions = true;
+        }
 #pragma omp atomic capture
         {
           ++nPending;
@@ -392,7 +400,7 @@ static void Mesh1D(GModel *m)
       }
       if(!nIter) Msg::ProgressMeter(localPending, false, "Meshing 1D...");
     }
-
+    if(exceptions) throw std::runtime_error(Msg::GetLastError());
     if(!nPending) break;
     if(nIter++ > CTX::instance()->mesh.maxRetries) break;
   }
@@ -525,6 +533,7 @@ static void Mesh2D(GModel *m)
       }
 
       int nPending = 0;
+      bool exceptions = false;
       std::vector<GFace *> temp;
       temp.insert(temp.begin(), f.begin(), f.end());
 #pragma omp parallel for schedule(dynamic) num_threads(nthreads)
@@ -532,7 +541,12 @@ static void Mesh2D(GModel *m)
         int localPending = 0;
         if(temp[K]->meshStatistics.status == GFace::PENDING) {
           backgroundMesh::current()->unset();
-          temp[K]->mesh(true);
+          try{ // OpenMP forbids leaving block via exception
+            temp[K]->mesh(true);
+          }
+          catch(...) {
+            exceptions = true;
+          }
 #pragma omp atomic capture
           {
             ++nPending;
@@ -541,6 +555,7 @@ static void Mesh2D(GModel *m)
         }
         if(!nIter) Msg::ProgressMeter(localPending, false, "Meshing 2D...");
       }
+      if(exceptions) throw std::runtime_error(Msg::GetLastError());
       if(!nPending) break;
       // iter == 2 is for meshing re-parametrized surfaces; after that, we
       // serialize (self-intersections of 1D meshes are not thread safe)!
diff --git a/src/mesh/meshGRegionHxt.cpp b/src/mesh/meshGRegionHxt.cpp
index a399ee5b8d..f45064bf0c 100644
--- a/src/mesh/meshGRegionHxt.cpp
+++ b/src/mesh/meshGRegionHxt.cpp
@@ -5,6 +5,7 @@
 
 #include <map>
 #include <set>
+#include <stdexcept>
 
 #include "GmshConfig.h"
 #include "meshGRegionHxt.h"
@@ -55,6 +56,7 @@ static HXTStatus nodalSizesCallBack(double *pts, uint32_t *volume,
   HXT_INFO("Computing %smesh sizes...", useInterpolatedSize ? "interpolated " : "");
 
   int nthreads = getNumThreads();
+  bool exceptions = false;
 #pragma omp parallel for schedule(dynamic) num_threads(nthreads)
   for(size_t i = 0; i < numPts; i++) {
     if(volume[i] < 0 || volume[i] >= allGR->size()) {
@@ -62,14 +64,21 @@ static HXTStatus nodalSizesCallBack(double *pts, uint32_t *volume,
       continue;
     }
     GRegion *gr = (*allGR)[volume[i]];
-    double lc = BGM_MeshSizeWithoutScaling(gr, 0, 0, pts[4 * i + 0],
-                                           pts[4 * i + 1], pts[4 * i + 2]);
-    if(useInterpolatedSize && pts[4 * i + 3] > 0.0)
-      pts[4 * i + 3] = std::min(pts[4 * i + 3], std::min(lcGlob, lc));
-    else
-      pts[4 * i + 3] = std::min(lcGlob, lc);
+    try{ // OpenMP forbids leaving block via exception
+      double lc = BGM_MeshSizeWithoutScaling(gr, 0, 0, pts[4 * i + 0],
+                                             pts[4 * i + 1], pts[4 * i + 2]);
+      if(useInterpolatedSize && pts[4 * i + 3] > 0.0)
+        pts[4 * i + 3] = std::min(pts[4 * i + 3], std::min(lcGlob, lc));
+      else
+        pts[4 * i + 3] = std::min(lcGlob, lc);
+    }
+    catch(...) {
+      exceptions = true;
+    }
   }
 
+  if(exceptions) throw std::runtime_error(Msg::GetLastError());
+
   HXT_INFO("Done computing %smesh sizes", useInterpolatedSize ? "interpolated " : "");
 
   return HXT_STATUS_OK;
-- 
GitLab