From 491fb1cf0ce8c9333594e6c364044f20fff819de Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Villemot?= <sebastien@dynare.org>
Date: Tue, 29 Apr 2025 14:51:13 +0200
Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20perfect=5Fforesight=5Fproblem=20?=
 =?UTF-8?q?MEX:=20error=20mechanism=20now=20thread-safe?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../perfect_foresight_problem/DynamicModelCaller.cc        | 7 ++++++-
 .../perfect_foresight_problem/DynamicModelCaller.hh        | 4 +++-
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/mex/sources/perfect_foresight_problem/DynamicModelCaller.cc b/mex/sources/perfect_foresight_problem/DynamicModelCaller.cc
index 0879b6241f..4ed7b9be9e 100644
--- a/mex/sources/perfect_foresight_problem/DynamicModelCaller.cc
+++ b/mex/sources/perfect_foresight_problem/DynamicModelCaller.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2019-2024 Dynare Team
+ * Copyright © 2019-2025 Dynare Team
  *
  * This file is part of Dynare.
  *
@@ -26,6 +26,7 @@
 using namespace std::literals::string_literals;
 
 std::string DynamicModelCaller::error_msg;
+std::mutex DynamicModelCaller::error_mtx;
 
 #if !defined(_WIN32) && !defined(__CYGWIN32__)
 void* DynamicModelDllCaller::resid_mex {nullptr};
@@ -210,12 +211,14 @@ DynamicModelMatlabCaller::eval(double* resid)
                                               funcname.c_str())};
     if (exception)
       {
+        std::lock_guard lk {error_mtx};
         error_msg = "An error occurred when calling " + funcname;
         return; // Avoid manipulating null pointers in plhs, see #1832
       }
 
     if (!mxIsDouble(plhs[0]) || mxIsSparse(plhs[0]))
       {
+        std::lock_guard lk {error_mtx};
         error_msg = "Residuals should be a dense array of double floats";
         return;
       }
@@ -249,6 +252,7 @@ DynamicModelMatlabCaller::eval(double* resid)
                                                 funcname.c_str())};
       if (exception)
         {
+          std::lock_guard lk {error_mtx};
           error_msg = "An error occurred when calling " + funcname;
           return; // Avoid manipulating null pointers in plhs, see #1832
         }
@@ -261,6 +265,7 @@ DynamicModelMatlabCaller::eval(double* resid)
 
       if (!mxIsDouble(plhs[0]) || !mxIsSparse(plhs[0]))
         {
+          std::lock_guard lk {error_mtx};
           error_msg = "Jacobian should be a sparse array of double floats";
           return;
         }
diff --git a/mex/sources/perfect_foresight_problem/DynamicModelCaller.hh b/mex/sources/perfect_foresight_problem/DynamicModelCaller.hh
index b651ec59a2..09612b4d69 100644
--- a/mex/sources/perfect_foresight_problem/DynamicModelCaller.hh
+++ b/mex/sources/perfect_foresight_problem/DynamicModelCaller.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2019-2024 Dynare Team
+ * Copyright © 2019-2025 Dynare Team
  *
  * This file is part of Dynare.
  *
@@ -23,6 +23,7 @@
 #include <algorithm>
 #include <limits>
 #include <memory>
+#include <mutex>
 #include <string>
 #include <type_traits>
 #include <vector>
@@ -47,6 +48,7 @@ public:
 
   // Used to store error messages (as exceptions cannot cross the OpenMP boundary)
   static std::string error_msg;
+  static std::mutex error_mtx; // Guard for access in OpenMP context
 
   DynamicModelCaller(bool linear_arg, bool compute_jacobian_arg) :
       linear {linear_arg}, compute_jacobian {compute_jacobian_arg}
-- 
GitLab