You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2018/02/14 13:18:13 UTC
[isis] 01/02: ISIS-1852 allow local thread variable propagation to
any child threads
This is an automated email from the ASF dual-hosted git repository.
danhaywood pushed a commit to branch ISIS-1852_parallel_stream_patch
in repository https://gitbox.apache.org/repos/asf/isis.git
commit e29b26f535079e051b3d84f6a148dfff9c4bd7bc
Author: Andi Huber <ah...@apache.org>
AuthorDate: Mon Jan 29 15:06:27 2018 +0100
ISIS-1852 allow local thread variable propagation to any child threads
---
.../core/runtime/services/ServiceInstantiator.java | 6 +-
.../runtime/services/ServiceInstantiatorTest.java | 281 ++++++++++++---------
2 files changed, 167 insertions(+), 120 deletions(-)
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServiceInstantiator.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServiceInstantiator.java
index 2b6591b..77adb02 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServiceInstantiator.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServiceInstantiator.java
@@ -157,11 +157,15 @@ public final class ServiceInstantiator {
final T newInstance = proxySubclass.newInstance();
final ProxyObject proxyObject = (ProxyObject) newInstance;
proxyObject.setHandler(new MethodHandler() {
- private ThreadLocal<T> serviceByThread = new ThreadLocal<>();
+ // Allow serviceByThread to be propagated from the thread that starts the request
+ // to any child-threads, hence InheritableThreadLocal.
+ private InheritableThreadLocal<T> serviceByThread = new InheritableThreadLocal<>();
@Override
public Object invoke(final Object proxied, final Method proxyMethod, final Method proxiedMethod, final Object[] args) throws Throwable {
+ System.out.println("invoke: "+proxyMethod.getName()+" "+this);
+
cacheMethodsIfNecessary(cls);
if(proxyMethod.getName().equals("__isis_startRequest")) {
diff --git a/core/runtime/src/test/java/org/apache/isis/core/runtime/services/ServiceInstantiatorTest.java b/core/runtime/src/test/java/org/apache/isis/core/runtime/services/ServiceInstantiatorTest.java
index 3237b51..5b39423 100644
--- a/core/runtime/src/test/java/org/apache/isis/core/runtime/services/ServiceInstantiatorTest.java
+++ b/core/runtime/src/test/java/org/apache/isis/core/runtime/services/ServiceInstantiatorTest.java
@@ -16,134 +16,177 @@
*/
package org.apache.isis.core.runtime.services;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.LongAdder;
+
import javax.enterprise.context.RequestScoped;
+
+import org.apache.isis.core.commons.config.IsisConfigurationDefault;
+import org.apache.isis.core.metamodel.services.ServicesInjector;
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2;
import org.jmock.auto.Mock;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-import org.apache.isis.core.commons.config.IsisConfigurationDefault;
-import org.apache.isis.core.metamodel.services.ServicesInjector;
-import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2;
+public class ServiceInstantiatorTest {
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
+ @Rule
+ public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(JUnitRuleMockery2.Mode.INTERFACES_AND_CLASSES);
-public class ServiceInstantiatorTest {
+ private ServiceInstantiator serviceInstantiator;
+
+ @JUnitRuleMockery2.Ignoring
+ @Mock
+ private ServicesInjector mockServiceInjector;
+
+ @Before
+ public void setUp() throws Exception {
+
+ serviceInstantiator = new ServiceInstantiator();
+ serviceInstantiator.setConfiguration(new IsisConfigurationDefault());
+ }
+
+ @Test
+ public void singleton() {
+ SingletonCalculator calculator = serviceInstantiator.createInstance(SingletonCalculator.class);
+ assertThat(calculator.add(3,4), is(7));
+ }
+
+ @Test
+ public void requestScoped_instantiate() {
+ AccumulatingCalculator calculator = serviceInstantiator.createInstance(AccumulatingCalculator.class);
+ assertThat(calculator instanceof RequestScopedService, is(true));
+ }
+
+ @Test
+ public void requestScoped_justOneThread() {
+ AccumulatingCalculator calculator = serviceInstantiator.createInstance(AccumulatingCalculator.class);
+ try {
+ ((RequestScopedService)calculator).__isis_startRequest(mockServiceInjector);
+ assertThat(calculator.add(3), is(3));
+ assertThat(calculator.add(4), is(7));
+ assertThat(calculator.getTotal(), is(7));
+ } finally {
+ ((RequestScopedService)calculator).__isis_endRequest();
+ }
+ }
+
+ @Test
+ public void requestScoped_multipleThreads() throws InterruptedException, BrokenBarrierException {
+
+ final AccumulatingCalculator calculator = serviceInstantiator.createInstance(AccumulatingCalculator.class);
+
+ // will ask each thread's calculator to increment 10 times
+ final int[] steps = new int[]{10};
+
+ // each thread will post its totals here
+ final int[] totals = new int[]{0,0,0};
+
+ // after each step, all threads wait. The +1 is for this thread (the co-ordinator)
+ final CyclicBarrier barrier =
+ new CyclicBarrier(totals.length+1, new Runnable() {
+ public void run() {
+ // all threads waiting; decrement number of steps
+ steps[0]--;
+ }
+ });
+
+ // start off all threads
+ for(int i=0; i<totals.length; i++) {
+ final int j=i;
+ new Thread() {
+ public void run() {
+ try {
+ ((RequestScopedService)calculator).__isis_startRequest(mockServiceInjector);
+ // keep incrementing, till no more steps
+ while(steps[0]>0) {
+ try {
+ calculator.add((j+1));
+ totals[j] = calculator.getTotal();
+ barrier.await();
+ } catch (InterruptedException | BrokenBarrierException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ } finally {
+ ((RequestScopedService)calculator).__isis_endRequest();
+ }
+ };
+ }.start();
+ }
+
+ // this thread is the co-ordinator; move onto next step when all are waiting
+ while(steps[0]>0) {
+ barrier.await();
+ }
+
+ assertThat(totals[0], is(10));
+ assertThat(totals[1], is(20));
+ assertThat(totals[2], is(30));
+ }
+
+ @Test
+ public void requestScoped_childThreads() throws InterruptedException {
+
+ final AccumulatingCalculator calculator =
+ serviceInstantiator.createInstance(AccumulatingCalculator.class);
+
+ final LongAdder counter = new LongAdder();
+
+ final int n = 100;
+ final int nThreads = 8;
+ final ExecutorService execService = Executors.newFixedThreadPool(nThreads);
+
+ // initialize the request scoped calculator on current thread ('main')
+ ((RequestScopedService)calculator).__isis_startRequest(mockServiceInjector);
+
+ for(int i=1;i<=n;++i) {
+ final int j=i;
+ execService.submit(()->{
+ try {
+
+ // access the request scoped calculator on a child thread of 'main'
+ calculator.add(j);
+ counter.add(calculator.getTotal());
+
+ } catch (Exception e) {
+ System.err.println(e.getMessage());
+ }
+ });
+ }
+
+ execService.shutdown();
+
+ execService.awaitTermination(10, TimeUnit.SECONDS);
+
+ ((RequestScopedService)calculator).__isis_endRequest();
+
+ assertThat(counter.intValue(), is(n*(n+1)/2));
+ }
+
+ public static class SingletonCalculator {
+ public int add(int x, int y) {
+ return x+y;
+ }
+ }
- @Rule
- public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(JUnitRuleMockery2.Mode.INTERFACES_AND_CLASSES);
-
- private ServiceInstantiator serviceInstantiator;
-
- @JUnitRuleMockery2.Ignoring
- @Mock
- private ServicesInjector mockServiceInjector;
-
- @Before
- public void setUp() throws Exception {
-
- serviceInstantiator = new ServiceInstantiator();
- serviceInstantiator.setConfiguration(new IsisConfigurationDefault());
- }
-
- @Test
- public void singleton() {
- SingletonCalculator calculator = serviceInstantiator.createInstance(SingletonCalculator.class);
- assertThat(calculator.add(3,4), is(7));
- }
-
- @Test
- public void requestScoped_instantiate() {
- AccumulatingCalculator calculator = serviceInstantiator.createInstance(AccumulatingCalculator.class);
- assertThat(calculator instanceof RequestScopedService, is(true));
- }
-
- @Test
- public void requestScoped_justOneThread() {
- AccumulatingCalculator calculator = serviceInstantiator.createInstance(AccumulatingCalculator.class);
- try {
- ((RequestScopedService)calculator).__isis_startRequest(mockServiceInjector);
- assertThat(calculator.add(3), is(3));
- assertThat(calculator.add(4), is(7));
- assertThat(calculator.getTotal(), is(7));
- } finally {
- ((RequestScopedService)calculator).__isis_endRequest();
- }
- }
-
- @Test
- public void requestScoped_multipleThreads() throws InterruptedException, BrokenBarrierException {
-
- final AccumulatingCalculator calculator = serviceInstantiator.createInstance(AccumulatingCalculator.class);
-
- // will ask each thread's calculator to increment 10 times
- final int[] steps = new int[]{10};
-
- // each thread will post its totals here
- final int[] totals = new int[]{0,0,0};
-
- // after each step, all threads wait. The +1 is for this thread (the co-ordinator)
- final CyclicBarrier barrier =
- new CyclicBarrier(totals.length+1, new Runnable() {
- public void run() {
- // all threads waiting; decrement number of steps
- steps[0]--;
- }
- });
-
- // start off all threads
- for(int i=0; i<totals.length; i++) {
- final int j=i;
- new Thread() {
- public void run() {
- try {
- ((RequestScopedService)calculator).__isis_startRequest(mockServiceInjector);
- // keep incrementing, til no more steps
- while(steps[0]>0) {
- try {
- calculator.add((j+1));
- totals[j] = calculator.getTotal();
- barrier.await();
- } catch (InterruptedException | BrokenBarrierException e) {
- throw new RuntimeException(e);
- }
- }
- } finally {
- ((RequestScopedService)calculator).__isis_endRequest();
- }
- };
- }.start();
- }
-
- // this thread is the co-ordinator; move onto next step when all are waiting
- while(steps[0]>0) {
- barrier.await();
- }
-
- assertThat(totals[0], is(10));
- assertThat(totals[1], is(20));
- assertThat(totals[2], is(30));
- }
-
-
- public static class SingletonCalculator {
- public int add(int x, int y) {
- return x+y;
- }
- }
-
- @RequestScoped
- public static class AccumulatingCalculator {
- private int total;
- public int add(int x) {
- total += x;
- return getTotal();
- }
- public int getTotal() {
- return total;
- }
- }
+ @RequestScoped
+ public static class AccumulatingCalculator {
+ private int total;
+ public int add(int x) {
+ total += x;
+ return getTotal();
+ }
+ public int getTotal() {
+ return total;
+ }
+ }
}
--
To stop receiving notification emails like this one, please contact
danhaywood@apache.org.