From 2defc6390aa093b17727c4980d18e9c806b40660 Mon Sep 17 00:00:00 2001
From: Markus FRANK <Markus.Frank@cern.ch>
Date: Tue, 24 Nov 2020 14:39:42 +0100
Subject: [PATCH] Towards a thread safe expression evaluation....

---
 DDParsers/include/Evaluator/Evaluator.h       | 71 ++++++++++++++++++-
 .../include/Evaluator/detail/Evaluator.h      | 26 +++----
 DDParsers/src/Evaluator/Evaluator.cpp         | 48 +++++++++++++
 .../src/Evaluator/ExpressionEvaluator.cpp     | 47 ++++++++----
 4 files changed, 166 insertions(+), 26 deletions(-)

diff --git a/DDParsers/include/Evaluator/Evaluator.h b/DDParsers/include/Evaluator/Evaluator.h
index e509708b6..668d3af4f 100644
--- a/DDParsers/include/Evaluator/Evaluator.h
+++ b/DDParsers/include/Evaluator/Evaluator.h
@@ -98,6 +98,66 @@ namespace dd4hep  {
        */
       std::pair<int,double> evaluate(const std::string& expression, std::ostream& os)  const;
 
+      /**
+       * Adds to the dictionary a function without parameters.
+       * If such a function already exist in the dictionary,
+       * then status will be set to WARNING_EXISTING_FUNCTION.
+       *
+       * @param name function name.
+       * @param fun pointer to the real function in the user code.
+       */
+      void setFunction(const std::string& name, double (*fun)())  const;
+
+      /**
+       * Adds to the dictionary a function with one parameter.
+       * If such a function already exist in the dictionary,
+       * then status will be set to WARNING_EXISTING_FUNCTION.
+       *
+       * @param name function name.
+       * @param fun pointer to the real function in the user code.
+       */
+      void setFunction(const std::string& name, double (*fun)(double))  const;
+
+      /**
+       * Adds to the dictionary a function with two parameters.
+       * If such a function already exist in the dictionary,
+       * then status will be set to WARNING_EXISTING_FUNCTION.
+       *
+       * @param name function name.
+       * @param fun pointer to the real function in the user code.
+       */
+      void setFunction(const std::string& name, double (*fun)(double, double))  const;
+
+      /**
+       * Adds to the dictionary a function with three parameters.
+       * If such a function already exist in the dictionary,
+       * then status will be set to WARNING_EXISTING_FUNCTION.
+       *
+       * @param name function name.
+       * @param fun pointer to the real function in the user code.
+       */
+      void setFunction(const std::string& name, double (*fun)(double, double, double))  const;
+
+      /**
+       * Adds to the dictionary a function with four parameters.
+       * If such a function already exist in the dictionary,
+       * then status will be set to WARNING_EXISTING_FUNCTION.
+       *
+       * @param name function name.
+       * @param fun pointer to the real function in the user code.
+       */
+      void setFunction(const std::string& name, double (*fun)(double, double, double, double))  const;
+
+      /**
+       * Adds to the dictionary a function with five parameters.
+       * If such a function already exist in the dictionary,
+       * then status will be set to WARNING_EXISTING_FUNCTION.
+       *
+       * @param name function name.
+       * @param fun pointer to the real function in the user code.
+       */
+      void setFunction(const std::string& name, double (*fun)(double, double, double, double, double))  const;
+
       /**
        * Adds to the dictionary a string constant
        *
@@ -152,8 +212,17 @@ namespace dd4hep  {
        */
       bool findVariable(const std::string& name)  const;
 
+      /**
+       * Finds the function in the dictionary.
+       *
+       * @param  name name of the function to be unset.
+       * @param  npar number of parameters of the function.
+       * @return true if such a function exists, false otherwise.
+       */
+      bool findFunction(const std::string& name, int npar)   const;
+
       class Object;
-      Object* object = 0;          // private data
+      Object* object = 0;                                  // internal data
 
     private:
       Evaluator(const Evaluator &) = delete;               // copy constructor is not allowed
diff --git a/DDParsers/include/Evaluator/detail/Evaluator.h b/DDParsers/include/Evaluator/detail/Evaluator.h
index 56387b849..7e20a03dd 100644
--- a/DDParsers/include/Evaluator/detail/Evaluator.h
+++ b/DDParsers/include/Evaluator/detail/Evaluator.h
@@ -74,7 +74,7 @@ namespace dd4hep  {
        * @see error_position
        * @see print_error
        */
-      double evaluate(const char * expression);
+      double evaluate(const char* expression);
 
       /**
        * Returns status of the last operation with the evaluator.
@@ -119,7 +119,7 @@ namespace dd4hep  {
        * @param name name of the variable.
        * @param value value assigned to the variable.
        */
-      void setVariable(const char * name, double value);
+      void setVariable(const char* name, double value);
 
       /**
        * Adds to the dictionary a variable with an arithmetic expression
@@ -130,7 +130,7 @@ namespace dd4hep  {
        * @param name name of the variable.
        * @param expression arithmetic expression.
        */
-      void setVariable(const char * name, const char * expression);
+      void setVariable(const char* name, const char* expression);
 
       /**
        * Adds to the dictionary a function without parameters.
@@ -140,7 +140,7 @@ namespace dd4hep  {
        * @param name function name.
        * @param fun pointer to the real function in the user code.
        */
-      void setFunction(const char * name, double (*fun)());
+      void setFunction(const char* name, double (*fun)());
 
       /**
        * Adds to the dictionary a function with one parameter.
@@ -150,7 +150,7 @@ namespace dd4hep  {
        * @param name function name.
        * @param fun pointer to the real function in the user code.
        */
-      void setFunction(const char * name, double (*fun)(double));
+      void setFunction(const char* name, double (*fun)(double));
 
       /**
        * Adds to the dictionary a function with two parameters.
@@ -160,7 +160,7 @@ namespace dd4hep  {
        * @param name function name.
        * @param fun pointer to the real function in the user code.
        */
-      void setFunction(const char * name, double (*fun)(double, double));
+      void setFunction(const char* name, double (*fun)(double, double));
 
       /**
        * Adds to the dictionary a function with three parameters.
@@ -170,7 +170,7 @@ namespace dd4hep  {
        * @param name function name.
        * @param fun pointer to the real function in the user code.
        */
-      void setFunction(const char * name, double (*fun)(double, double, double));
+      void setFunction(const char* name, double (*fun)(double, double, double));
 
       /**
        * Adds to the dictionary a function with four parameters.
@@ -180,7 +180,7 @@ namespace dd4hep  {
        * @param name function name.
        * @param fun pointer to the real function in the user code.
        */
-      void setFunction(const char * name, double (*fun)(double, double, double, double));
+      void setFunction(const char* name, double (*fun)(double, double, double, double));
 
       /**
        * Adds to the dictionary a function with five parameters.
@@ -190,7 +190,7 @@ namespace dd4hep  {
        * @param name function name.
        * @param fun pointer to the real function in the user code.
        */
-      void setFunction(const char * name, double (*fun)(double, double, double, double, double));
+      void setFunction(const char* name, double (*fun)(double, double, double, double, double));
 
       /**
        * Finds the variable in the dictionary.
@@ -198,7 +198,7 @@ namespace dd4hep  {
        * @param  name name of the variable.
        * @return true if such a variable exists, false otherwise.
        */
-      bool findVariable(const char * name) const;
+      bool findVariable(const char* name) const;
 
       /**
        * Finds the function in the dictionary.
@@ -207,14 +207,14 @@ namespace dd4hep  {
        * @param  npar number of parameters of the function.
        * @return true if such a function exists, false otherwise.
        */
-      bool findFunction(const char * name, int npar) const;
+      bool findFunction(const char* name, int npar) const;
 
       /**
        * Removes the variable from the dictionary.
        *
        * @param name name of the variable.
        */
-      void removeVariable(const char * name);
+      void removeVariable(const char* name);
 
       /**
        * Removes the function from the dictionary.
@@ -222,7 +222,7 @@ namespace dd4hep  {
        * @param name name of the function to be unset.
        * @param npar number of parameters of the function.
        */
-      void removeFunction(const char * name, int npar);
+      void removeFunction(const char* name, int npar);
 
       /**
        * Clear all settings.
diff --git a/DDParsers/src/Evaluator/Evaluator.cpp b/DDParsers/src/Evaluator/Evaluator.cpp
index 48ca55229..611edc4d0 100644
--- a/DDParsers/src/Evaluator/Evaluator.cpp
+++ b/DDParsers/src/Evaluator/Evaluator.cpp
@@ -933,5 +933,53 @@ bool Evaluator::findVariable(const std::string& name)  const    {
   return ret;
 }
 
+//---------------------------------------------------------------------------
+void Evaluator::setFunction(const std::string& name, double (*fun)())  const    {
+  object->lock();
+  object->setFunction(name.c_str(), fun);
+  object->unlock();
+}
+
+//---------------------------------------------------------------------------
+void Evaluator::setFunction(const std::string& name, double (*fun)(double))  const    {
+  object->lock();
+  object->setFunction(name.c_str(), fun);
+  object->unlock();
+}
+
+//---------------------------------------------------------------------------
+void Evaluator::setFunction(const std::string& name, double (*fun)(double, double))  const    {
+  object->lock();
+  object->setFunction(name.c_str(), fun);
+  object->unlock();
+}
+
+//---------------------------------------------------------------------------
+void Evaluator::setFunction(const std::string& name, double (*fun)(double, double, double))  const    {
+  object->lock();
+  object->setFunction(name.c_str(), fun);
+  object->unlock();
+}
 
+//---------------------------------------------------------------------------
+void Evaluator::setFunction(const std::string& name, double (*fun)(double, double, double, double))  const    {
+  object->lock();
+  object->setFunction(name.c_str(), fun);
+  object->unlock();
+}
 
+//---------------------------------------------------------------------------
+void Evaluator::setFunction(const std::string& name, double (*fun)(double, double, double, double, double))  const    {
+  object->lock();
+  object->setFunction(name.c_str(), fun);
+  object->unlock();
+}
+
+//---------------------------------------------------------------------------
+bool Evaluator::findFunction(const std::string& name, int npar) const    {
+  bool ret;
+  object->lock();
+  ret = object->findFunction(name.c_str(), npar);
+  object->unlock();
+  return ret;
+}
diff --git a/DDParsers/src/Evaluator/ExpressionEvaluator.cpp b/DDParsers/src/Evaluator/ExpressionEvaluator.cpp
index 098c3adb2..74d0da5fe 100644
--- a/DDParsers/src/Evaluator/ExpressionEvaluator.cpp
+++ b/DDParsers/src/Evaluator/ExpressionEvaluator.cpp
@@ -10,14 +10,25 @@
 // Author     : M.Frank
 //
 //==========================================================================
+
+/// Framework include files
 #include "Parsers/config.h"
 #include "Evaluator/Evaluator.h"
 #include "Evaluator/detail/Evaluator.h"
 #include "Evaluator/DD4hepUnits.h"
 
+/// C/C++ include files
+#include <mutex>
+
 namespace units = dd4hep;
 
 namespace {
+
+  void _eval_lock(bool lock_or_unlock)    {
+    static std::mutex construction_lock;
+    if ( lock_or_unlock ) construction_lock.lock();
+    else construction_lock.unlock();
+  }
   
   void _init(dd4hep::tools::Evaluator& e) {
     // Initialize numerical expressions parser with the standard math functions
@@ -72,10 +83,14 @@ namespace dd4hep {
   const tools::Evaluator& evaluator() {
     static const tools::Evaluator* e = 0;
     if ( !e )   {
-      static tools::Evaluator ev;
-      _init(ev);
-      _tgeoUnits(ev);
-      e = &ev;
+      _eval_lock(true);
+      if ( !e )   {
+	static tools::Evaluator ev;
+	_init(ev);
+	_tgeoUnits(ev);
+	e = &ev;
+      }
+      _eval_lock(false);
     }
     return *e;
   }
@@ -84,10 +99,14 @@ namespace dd4hep {
   const tools::Evaluator& g4Evaluator()   {
     static const tools::Evaluator* e = 0;
     if ( !e )   {
-      static tools::Evaluator ev;
-      _init(ev);
-      _g4Units(ev);
-      e = &ev;
+      _eval_lock(true);
+      if ( !e )   {
+	static tools::Evaluator ev;
+	_init(ev);
+	_g4Units(ev);
+	e = &ev;
+      }
+      _eval_lock(false);
     }
     return *e;
   }
@@ -96,10 +115,14 @@ namespace dd4hep {
   const tools::Evaluator& cgsEvaluator()   {
     static const tools::Evaluator* e = 0;
     if ( !e )   {
-      static tools::Evaluator ev;
-      _init(ev);
-      _cgsUnits(ev);
-      e = &ev;
+      _eval_lock(true);
+      if ( !e )   {
+	static tools::Evaluator ev;
+	_init(ev);
+	_cgsUnits(ev);
+	e = &ev;
+      }
+      _eval_lock(false);
     }
     return *e;
   }
-- 
GitLab