From 3efde4f898168affd38ed845d692a6b14b323346 Mon Sep 17 00:00:00 2001 From: Christopher Jones <chrisdjones15@gmail.com> Date: Fri, 4 Dec 2020 13:45:14 -0600 Subject: [PATCH] Work around ::string copy not being thread safe - Be sure to only make a copy when the dictionary lock is held. - Avoid making a string if not needed --- DDParsers/src/Evaluator/Evaluator.cpp | 29 +++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/DDParsers/src/Evaluator/Evaluator.cpp b/DDParsers/src/Evaluator/Evaluator.cpp index df8e3c55e..47a5894d3 100644 --- a/DDParsers/src/Evaluator/Evaluator.cpp +++ b/DDParsers/src/Evaluator/Evaluator.cpp @@ -165,7 +165,8 @@ static int variable(const string & name, double & result, dic_type::const_iterator iter = dictionary.find(name); if (iter == dictionary.end()) return EVAL::ERROR_UNKNOWN_VARIABLE; - Item item = iter->second; + //NOTE: copying ::string not thread safe so must use ref + Item const& item = iter->second; switch (item.what) { case Item::VARIABLE: result = item.variable; @@ -205,7 +206,8 @@ static int function(const string & name, stack<double> & par, dic_type::const_iterator iter = dictionary.find(sss[npar]+name); if (iter == dictionary.end()) return EVAL::ERROR_UNKNOWN_FUNCTION; - Item item = iter->second; + //NOTE: copying ::string not thread safe so must use ref + Item const& item = iter->second; double pp[MAX_N_PAR]; for(int i=0; i<npar; i++) { pp[i] = par.top(); par.pop(); } @@ -743,12 +745,15 @@ void Evaluator::Object::EvalStatus::print_error(std::ostream& os) const { int Evaluator::Object::setEnviron(const char* name, const char* value) { string prefix = "${"; string item_name = prefix + string(name) + string("}"); + + //Need to take lock before creating Item since since Item to be destroyed + // before the lock in order avoid ::string ref count thread problem + Struct::WriteLock guard(imp); Item item; item.what = Item::STRING; item.expression = value; item.function = 0; item.variable = 0; - Struct::WriteLock guard(imp); dic_type::iterator iter = imp->theDictionary.find(item_name); if (iter != imp->theDictionary.end()) { iter->second = item; @@ -765,13 +770,13 @@ int Evaluator::Object::setEnviron(const char* name, const char* value) { //--------------------------------------------------------------------------- std::pair<const char*,int> Evaluator::Object::getEnviron(const char* name) const { - string item_name = name; Struct::ReadLock guard(imp); - dic_type::iterator iter = imp->theDictionary.find(item_name); - if (iter != imp->theDictionary.end()) { + Struct const* cImp = imp; + dic_type::const_iterator iter = cImp->theDictionary.find(name); + if (iter != cImp->theDictionary.end()) { return std::make_pair(iter->second.expression.c_str(), EVAL::OK); } - if ( ::strlen(item_name.c_str()) > 3 ) { + if ( ::strlen(name) > 3 ) { // Need to remove braces from ${xxxx} for call to getenv() string env_name(name+2,::strlen(name)-3); const char* env_str = ::getenv(env_name.c_str()); @@ -788,7 +793,15 @@ int Evaluator::Object::setVariable(const char * name, double value) { } int Evaluator::Object::setVariable(const char * name, const char * expression) { - return setItem("", name, Item(expression), imp); + Item item(expression); + auto returnValue = setItem("", name, item, imp); + { + //We need to decrement the ref count on the item.expression while holding + // the lock since the ::string was copied into the dictionary + Struct::WriteLock guard(imp); + item.expression = ""; + } + return returnValue; } void Evaluator::Object::setVariableNoLock(const char * name, double value) { -- GitLab