This is an automated email from the ASF dual-hosted git repository.

aherbert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-math.git


The following commit(s) were added to refs/heads/master by this push:
     new 85f7ee1d2 MATH-1693: Synchronize BaseRuleFactory.getRule (#343)
85f7ee1d2 is described below

commit 85f7ee1d2991ba93a71d5799d3f845db225a7c36
Author: insaf021 <[email protected]>
AuthorDate: Thu Oct 29 22:57:39 2026 +0530

    MATH-1693: Synchronize BaseRuleFactory.getRule (#343)
---
 .../integration/gauss/BaseRuleFactory.java         |  2 +-
 .../integration/gauss/BaseRuleFactoryTest.java     | 72 ++++++++++++++++++++++
 2 files changed, 73 insertions(+), 1 deletion(-)

diff --git 
a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/analysis/integration/gauss/BaseRuleFactory.java
 
b/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/analysis/integration/gauss/BaseRuleFactory.java
index 13d04d6ee..c82fe74f2 100644
--- 
a/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/analysis/integration/gauss/BaseRuleFactory.java
+++ 
b/commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/analysis/integration/gauss/BaseRuleFactory.java
@@ -52,7 +52,7 @@ public abstract class BaseRuleFactory<T extends Number> {
      * @throws DimensionMismatchException if the elements of the rule pair do 
not
      * have the same length.
      */
-    public Pair<double[], double[]> getRule(int numberOfPoints) {
+    public synchronized Pair<double[], double[]> getRule(int numberOfPoints) {
 
         if (numberOfPoints <= 0) {
             throw new 
NotStrictlyPositiveException(LocalizedFormats.NUMBER_OF_POINTS,
diff --git 
a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/analysis/integration/gauss/BaseRuleFactoryTest.java
 
b/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/analysis/integration/gauss/BaseRuleFactoryTest.java
index 89ede23ea..8b3ab3e3a 100644
--- 
a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/analysis/integration/gauss/BaseRuleFactoryTest.java
+++ 
b/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/analysis/integration/gauss/BaseRuleFactoryTest.java
@@ -16,8 +16,12 @@
  */
 package org.apache.commons.math4.legacy.analysis.integration.gauss;
 
+import java.time.Duration;
 import java.util.List;
 import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.TimeUnit;
@@ -29,6 +33,7 @@ import java.util.concurrent.atomic.AtomicInteger;
 import org.apache.commons.math4.legacy.core.Pair;
 import org.junit.Test;
 import org.junit.Assert;
+import org.junit.jupiter.api.Assertions;
 
 /**
  * Test for {@link BaseRuleFactory}.
@@ -65,6 +70,73 @@ public class BaseRuleFactoryTest {
         final int n = RuleBuilder.getNumberOfCalls();
         Assert.assertEquals("Rule computation was called " + n + " times", 1, 
n);
     }
+
+    /**
+     * Tests that concurrent requests for different rules on a shared factory
+     * do not corrupt the cache of double-precision rules (MATH-1693).
+     * Without synchronization of "getRule", the cache can be left in a state
+     * where a lookup never terminates.
+     */
+    @Test
+    public void testConcurrentCreationOfDifferentRules() {
+        final int numThreads = 8;
+        final int maxPoints = 1000;
+        final int rounds = 20;
+
+        Assertions.assertTimeoutPreemptively(Duration.ofSeconds(30), () -> {
+            final ExecutorService exec = 
Executors.newFixedThreadPool(numThreads, r -> {
+                final Thread t = new Thread(r);
+                t.setDaemon(true);
+                return t;
+            });
+            try {
+                for (int r = 0; r < rounds; r++) {
+                    final BaseRuleFactory<Double> factory = new 
CheapRuleFactory();
+                    // Compute the rules upfront so that the threads mostly 
race
+                    // on the double-precision cache.
+                    for (int n = 1; n <= maxPoints; n++) {
+                        factory.getRuleInternal(n);
+                    }
+                    final CountDownLatch start = new CountDownLatch(1);
+                    final List<Future<?>> results = new ArrayList<>();
+                    for (int t = 0; t < numThreads; t++) {
+                        final int offset = t;
+                        results.add(exec.submit(() -> {
+                            start.await();
+                            for (int i = 0; i < maxPoints; i++) {
+                                final int n = 1 + (i * numThreads + offset) % 
maxPoints;
+                                Assert.assertEquals(n, 
factory.getRule(n).getFirst().length);
+                            }
+                            return null;
+                        }));
+                    }
+                    start.countDown();
+                    for (Future<?> f : results) {
+                        f.get();
+                    }
+                    for (int n = 1; n <= maxPoints; n++) {
+                        Assert.assertEquals(n, 
factory.getRule(n).getFirst().length);
+                    }
+                }
+            } finally {
+                exec.shutdownNow();
+            }
+        });
+    }
+
+    /** Factory with a fast rule computation. */
+    private static final class CheapRuleFactory extends 
BaseRuleFactory<Double> {
+        @Override
+        protected Pair<Double[], Double[]> computeRule(int order) {
+            final Double[] p = new Double[order];
+            final Double[] w = new Double[order];
+            for (int i = 0; i < order; i++) {
+                p[i] = Double.valueOf(i);
+                w[i] = Double.valueOf(i);
+            }
+            return new Pair<>(p, w);
+        }
+    }
 }
 
 class RuleBuilder implements Callable<Pair<double[], double[]>> {

Reply via email to