View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  package org.hipparchus.analysis.integration.gauss;
23  
24  import org.hipparchus.util.Pair;
25  import org.junit.jupiter.api.Test;
26  
27  import java.util.ArrayList;
28  import java.util.List;
29  import java.util.concurrent.ArrayBlockingQueue;
30  import java.util.concurrent.Callable;
31  import java.util.concurrent.ExecutionException;
32  import java.util.concurrent.Future;
33  import java.util.concurrent.ThreadPoolExecutor;
34  import java.util.concurrent.TimeUnit;
35  import java.util.concurrent.atomic.AtomicInteger;
36  
37  import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
38  import static org.junit.jupiter.api.Assertions.assertEquals;
39  
40  /**
41   * Test for {@link AbstractRuleFactory}.
42   *
43   */
44  class RuleFactoryTest {
45      /**
46       * Tests that a given rule rule will be computed and added once to the cache
47       * whatever the number of times this rule is called concurrently.
48       */
49      @Test
50      void testConcurrentCreation() throws InterruptedException,
51          ExecutionException {
52          // Number of times the same rule will be called.
53          final int numTasks = 20;
54  
55          final ThreadPoolExecutor exec
56              = new ThreadPoolExecutor(3, numTasks, 1, TimeUnit.SECONDS,
57                                       new ArrayBlockingQueue<Runnable>(2));
58  
59          final List<Future<Pair<double[], double[]>>> results
60              = new ArrayList<Future<Pair<double[], double[]>>>();
61          for (int i = 0; i < numTasks; i++) {
62              results.add(exec.submit(new RuleBuilder()));
63          }
64  
65          // Ensure that all computations have completed.
66          for (Future<Pair<double[], double[]>> f : results) {
67              f.get();
68          }
69  
70          // Assertion would fail if "getRuleInternal" were not "synchronized".
71          final int n = RuleBuilder.getNumberOfCalls();
72          assertEquals(1, n, "Rule computation was called " + n + " times");
73      }
74  
75      private static class RuleBuilder implements Callable<Pair<double[], double[]>> {
76          private static final DummyRuleFactory factory = new DummyRuleFactory();
77  
78          public Pair<double[], double[]> call() {
79              final int dummy = 2; // Always request the same rule.
80              return factory.getRule(dummy);
81          }
82  
83          public static int getNumberOfCalls() {
84              return factory.getNumberOfCalls();
85          }
86      }
87  
88      private static class DummyRuleFactory extends AbstractRuleFactory {
89          /** Rule computations counter. */
90          private static AtomicInteger nCalls = new AtomicInteger();
91  
92          @Override
93          protected Pair<double[], double[]> computeRule(int order) {
94              // Tracks whether this computation has been called more than once.
95              nCalls.getAndIncrement();
96  
97              assertDoesNotThrow(() -> {
98                  // Sleep to simulate computation time.
99                  Thread.sleep(20);
100             }, "Unexpected interruption");
101 
102             // Dummy rule (but contents must exist).
103             final double[] p = new double[order];
104             final double[] w = new double[order];
105             for (int i = 0; i < order; i++) {
106                 p[i] = Double.valueOf(i);
107                 w[i] = Double.valueOf(i);
108             }
109             return new Pair<>(p, w);
110         }
111 
112         public int getNumberOfCalls() {
113             return nCalls.get();
114         }
115 
116     }
117 
118 }
119