From 2d621b758345525c3e4a55aa951b48d82908675d Mon Sep 17 00:00:00 2001 From: Brian Vosburgh Date: Tue, 9 Feb 2016 09:57:14 -0500 Subject: test and fix(!) CachingConcurrentStack --- .../stack/CachingConcurrentStackTests.java | 162 ++++++++++++++++++++- 1 file changed, 160 insertions(+), 2 deletions(-) (limited to 'common/tests') diff --git a/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/stack/CachingConcurrentStackTests.java b/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/stack/CachingConcurrentStackTests.java index eb05335c2c..3e68f24b94 100644 --- a/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/stack/CachingConcurrentStackTests.java +++ b/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/stack/CachingConcurrentStackTests.java @@ -9,7 +9,13 @@ ******************************************************************************/ package org.eclipse.jpt.common.utility.tests.internal.stack; +import java.util.Arrays; +import java.util.List; +import java.util.Vector; +import org.eclipse.jpt.common.utility.internal.ArrayTools; import org.eclipse.jpt.common.utility.internal.ObjectTools; +import org.eclipse.jpt.common.utility.internal.reference.SynchronizedBoolean; +import org.eclipse.jpt.common.utility.internal.stack.CachingConcurrentStack; import org.eclipse.jpt.common.utility.internal.stack.StackTools; import org.eclipse.jpt.common.utility.stack.Stack; @@ -26,6 +32,156 @@ public class CachingConcurrentStackTests return StackTools.cachingConcurrentStack(); } + public void testConcurrentAccess() throws InterruptedException { + CachingConcurrentStack stack = StackTools.cachingConcurrentStack(); + + int threadCount = 10; + int elementsPerThread = 100000; + SynchronizedBoolean startFlag = new SynchronizedBoolean(false); + + PushRunnable[] pushRunnables = new PushRunnable[threadCount]; + Thread[] pushThreads = new Thread[threadCount]; + for (int i = 0; i < threadCount; i++) { + PushRunnable pushRunnable = new PushRunnable(stack, (i * elementsPerThread), elementsPerThread, startFlag); + pushRunnables[i] = pushRunnable; + Thread pushThread = new Thread(pushRunnable); + pushThreads[i] = pushThread; + pushThread.start(); + } + + PopRunnable[] popRunnables = new PopRunnable[threadCount]; + Thread[] popThreads = new Thread[threadCount]; + for (int i = 0; i < threadCount; i++) { + PopRunnable popRunnable = new PopRunnable(stack, elementsPerThread, startFlag); + popRunnables[i] = popRunnable; + Thread popThread = new Thread(popRunnable); + popThreads[i] = popThread; + popThread.start(); + } + + startFlag.setTrue(); + for (int i = 0; i < threadCount; i++) { + pushThreads[i].join(); + assertTrue(pushRunnables[i].exceptions.isEmpty()); + } + for (int i = 0; i < threadCount; i++) { + popThreads[i].join(); + assertTrue(popRunnables[i].exceptions.isEmpty()); + } + + // if we get here, we have, at the least, popd as many elements as we pushd... + // ...now verify that all the popd elements are unique + // (i.e. none were lost or duplicated) + int totalCount = threadCount * elementsPerThread; + int uberMax = totalCount + 1; + int uberMaxThreadIndex = threadCount + 1; + int[] indexes = ArrayTools.fill(new int[threadCount], 0); + for (int i = 0; i < totalCount; i++) { + int min = uberMax; + int minThreadIndex = uberMaxThreadIndex; + for (int j = 0; j < threadCount; j++) { + int currentIndex = indexes[j]; + if (currentIndex < elementsPerThread) { + int current = popRunnables[j].elements[currentIndex].intValue(); + if (current < min) { + min = current; + minThreadIndex = j; + } + } + } + assertEquals(i, min); + indexes[minThreadIndex]++; + } + } + + public static class PushRunnable + implements Runnable + { + private final Stack stack; + private final int start; + private final int stop; + private final SynchronizedBoolean startFlag; + final List exceptions = new Vector<>(); + + public PushRunnable(Stack stack, int start, int count, SynchronizedBoolean startFlag) { + super(); + this.stack = stack; + this.start = start; + this.stop = start + count; + this.startFlag = startFlag; + } + + public void run() { + try { + this.run_(); + } catch (InterruptedException ex) { + this.exceptions.add(ex); + } + } + + private void run_() throws InterruptedException { + this.startFlag.waitUntilTrue(); + for (int i = this.start; i < this.stop; i++) { + this.stack.push(Integer.valueOf(i)); + if ((i % 20) == 0) { + Thread.sleep(0); + } + } + } + } + + public static class PopRunnable + implements Runnable + { + private final Stack stack; + private final int count; + final Integer[] elements; + private int elementsCount; + private final SynchronizedBoolean startFlag; + final List exceptions = new Vector<>(); + + public PopRunnable(Stack stack, int count, SynchronizedBoolean startFlag) { + super(); + this.stack = stack; + this.count = count; + this.elements = new Integer[count]; + this.elementsCount = 0; + this.startFlag = startFlag; + } + + public void run() { + try { + this.run_(); + } catch (InterruptedException ex) { + this.exceptions.add(ex); + } + } + + private void run_() throws InterruptedException { + this.startFlag.waitUntilTrue(); + int i = 0; + while (true) { + Integer element = this.stack.peek(); // fiddle with peek also + element = this.stack.pop(); + if (element != null) { + this.elements[this.elementsCount++] = element; + if (this.elementsCount == this.count) { + break; + } + } + if ((i % 20) == 0) { + Thread.sleep(0); + } + if (i == Integer.MAX_VALUE) { + i = 0; + } else { + i++; + } + } + Arrays.sort(this.elements); + } + } + public void testCache() { Stack stack = this.buildStack(); String first = "first"; @@ -43,7 +199,8 @@ public class CachingConcurrentStackTests stack.push(fifth); this.verifyNodeCache(0, stack); Object headRef = ObjectTools.get(stack, "cacheHeadRef"); - assertNull(ObjectTools.get(headRef, "value")); + Object pair = ObjectTools.get(headRef, "pair"); + assertNull(ObjectTools.get(pair, "reference")); stack.pop(); this.verifyNodeCache(1, stack); @@ -70,7 +227,8 @@ public class CachingConcurrentStackTests public void verifyNodeCache(int size, Object stack) { int nodeCount = 0; Object headRef = ObjectTools.get(stack, "cacheHeadRef"); - for (Object node = ObjectTools.get(headRef, "value"); node != null; node = ObjectTools.get(node, "next")) { + Object pair = ObjectTools.get(headRef, "pair"); + for (Object node = ObjectTools.get(pair, "reference"); node != null; node = ObjectTools.get(node, "next")) { nodeCount++; } assertEquals(size, nodeCount); -- cgit v1.2.3