Skip to content

Eval method is not thread safe and gives incorrect answers when executed in parallel #364

@mats-boisen

Description

@mats-boisen

When doing evaluations in parallel in different threads it somehow seem that the values from the different evaluations conflict with each other and are used in unintended ways. I.e. variable value from one thread is used in another thread.

Issue is introduced in this change set
Improve Performance (#349)
in version 2.19.

And it can be reproduced with this unit test which passes in version 2.18 but not in 2.19.

using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
namespace DynamicExpresso.UnitTest
{
 [TestFixture]
 public class ThreadingTest
 {
  private void DynamicExpresso_Interpreter_Eval1()
  {
   var parser = new Interpreter();
   parser.SetVariable("C0", 0.4731765);
   parser.SetVariable("x", 50);
   Assert.That(parser.Identifiers.First(i => i.Name == "C0").Expression.ToString(), Is.EqualTo("0.4731765"));
   Assert.That(parser.Identifiers.First(i => i.Name == "x").Expression.ToString(), Is.EqualTo("50"));
   var result = parser.Eval<double>("x*C0");
   Assert.That(result, Is.EqualTo(23.658825));
  }
  private void DynamicExpresso_Interpreter_Eval2()
  {
   var parser = new Interpreter();
   parser.SetVariable("C0", 0.4731765);
   parser.SetVariable("y", 1080);
   Assert.That(parser.Identifiers.First(i => i.Name == "C0").Expression.ToString(), Is.EqualTo("0.4731765"));
   Assert.That(parser.Identifiers.First(i => i.Name == "y").Expression.ToString(), Is.EqualTo("1080"));
   var result = parser.Eval<double>("y/C0");
   Assert.That(result, Is.EqualTo(2282.4464021353556));
  }
  private void DynamicExpresso_Interpreter_Eval_Sequence()
  {
   for (var i = 0; i < 10; i++)
   {
    DynamicExpresso_Interpreter_Eval1();
    DynamicExpresso_Interpreter_Eval2();
   }
  }
  [Test]
  public async Task DynamicExpresso_Interpreter_Eval_Threading()
  {
   Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
   const int noOfTasks = 5;
   var tasksToRun = new List<Task>();
   for (var i = 0; i < noOfTasks; i++)
   {
    var taskToRunLater = new Task(() => DynamicExpresso_Interpreter_Eval_Sequence());
    tasksToRun.Add(taskToRunLater);
   }
   foreach (var taskToRunLater in tasksToRun)
   {
    taskToRunLater.Start();
   }
   foreach (var taskToRunLater in tasksToRun)
   {
    await taskToRunLater;
   }
  }
 }
}
 

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions