Giter Site home page Giter Site logo

Comments (4)

xuanswe avatar xuanswe commented on August 17, 2024 2

Isn't that too much for users?
Should it be just automatically thread-safe without doing anything from usage side?
If users want to switch from Thread to whatever we don't know, ex. Kotlin coroutines, they will need to find a way to make it work.
If the lib support immutability approach, we don't need to care how users use the lib api. It will automatically be safe with any parallel programming frameworks (Java Thread, coroutines or whatever else we don't care).

from evalex.

uklimaschewski avatar uklimaschewski commented on August 17, 2024

In fact, an expression is thread-safe, as soon as the expression was parsed.
But the standard MapBasedDataAccessor which holds the variable values is not thread-safe.

There are some possibilities to achieve thread safety here. I did a small test with a custom data accessor that is using ThreadLocal to store the data along with the thread:

package com.ezylang.evalex;

import com.ezylang.evalex.config.ExpressionConfiguration;
import com.ezylang.evalex.data.DataAccessorIfc;
import com.ezylang.evalex.data.EvaluationValue;
import com.ezylang.evalex.data.MapBasedDataAccessor;
import com.ezylang.evalex.parser.ParseException;
import org.junit.jupiter.api.Test;

import java.math.BigDecimal;
import java.security.SecureRandom;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import static org.assertj.core.api.Assertions.assertThat;

public class ThreadLocalTest {

  class ThreadLocalDataAccess implements DataAccessorIfc {
    ThreadLocal<MapBasedDataAccessor> threadLocal =
        ThreadLocal.withInitial(MapBasedDataAccessor::new);

    @Override
    public EvaluationValue getData(String variable) {
      return threadLocal.get().getData(variable);
    }

    @Override
    public void setData(String variable, EvaluationValue value) {
      threadLocal.get().setData(variable, value);
    }
  }

  @Test
  void testThreadLocal() throws ParseException, InterruptedException {

    ExpressionConfiguration configuration =
        ExpressionConfiguration.builder().dataAccessorSupplier(ThreadLocalDataAccess::new).build();

    Expression expression = new Expression("a+b", configuration);
    // validate makes sure the expression is parsed before entering the multi-threaded code
    expression.validate();

    SecureRandom random = new SecureRandom();
    // start 100 threads
    ExecutorService es = Executors.newCachedThreadPool();
    for (int t = 0; t < 100; t++) {
      es.execute(
          () -> {
            try {
              System.out.println("Starting with " + Thread.currentThread().getName());
              for (int i = 0; i < 10000; i++) {
                BigDecimal a = new BigDecimal(random.nextInt());
                BigDecimal b = new BigDecimal(random.nextInt());
                EvaluationValue result = expression.with("a", a).and("b", b).evaluate();

                BigDecimal sum = a.add(b);
                assertThat(result.getStringValue()).isEqualTo(sum.toPlainString());
              }
              System.out.println("Done with " + Thread.currentThread().getName());
            } catch (EvaluationException e) {
              throw new RuntimeException(e);
            } catch (ParseException e) {
              throw new RuntimeException(e);
            }
          });
    }
    es.awaitTermination(10, TimeUnit.SECONDS);
  }
}

To test, I started 100 threads and each one is doing 10,000 calculations with random values.
If you do not use the ThreadLocalDataAccess in configuration, there are a lot of errors, because all threads try to read and write to the same map.
When using ThreadLocalDataAccess, then there are no errors because each thread has now its own map of data.

from evalex.

uklimaschewski avatar uklimaschewski commented on August 17, 2024

EvalEx is a library for use by programmers. So, the user is somebody with programming skills. And as you point out, the environment where and how the library is used may differ. And the library user should be able to adopt to different scenarios.

The above thread safe solution might get a part of a future release, so that it can be configured without the need of an own implementation.

Immutability can only be an optional configuration, the current default flexibility allows for e.g. operators and functions with side effects. This was intentional.

But I will think of a more general immutable evaluation variant, shouldn't be too hard.

from evalex.

xuanswe avatar xuanswe commented on August 17, 2024

EvalEx is a library for use by programmers. So, the user is somebody with programming skills. And as you point out, the environment where and how the library is used may differ. And the library user should be able to adopt to different scenarios.

I don't totally agree with this. IMO, user has skill, doesn't mean s/he want to manage extra work for each environment. User would be happier if the internal of lib would produce the same thing regardless of where it is running.

Immutability can only be an optional configuration, the current default flexibility allows for e.g. operators and functions with side effects. This was intentional.

You don't need to have an "optional configuration". As I mentioned in previous comment, it's possible to create a snapshot from existing expression. The "snapshot" could reuse expensive computation result of the Expression instance as the starting point plus the provided variables to create the immutable state. The evaluation will be done with the immutable state of the snapshot. So, developers could simply pass around the snapshot to anywhere and it will just work without worrying about thread safe.
This is the powerful of immutability.

from evalex.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.