aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPawel Piech2012-04-19 18:13:28 (EDT)
committerEugene Tarassov2012-05-11 14:56:41 (EDT)
commita085832e6a99ba77526599026a19b4b934be9ae1 (patch)
tree31265f23b1aad00d89e610f2dd23d805b6f14597
parent39ed8a7b883ce156bb676869c5542446e4f364ee (diff)
downloadorg.eclipse.tcf-a085832e6a99ba77526599026a19b4b934be9ae1.zip
org.eclipse.tcf-a085832e6a99ba77526599026a19b4b934be9ae1.tar.gz
org.eclipse.tcf-a085832e6a99ba77526599026a19b4b934be9ae1.tar.bz2
Bug 379189 - [tests] Add test to measure stepping performance
Added RangeCache.
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/RangeCacheTests.java477
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/SampleTest.java60
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TransactionTests.java161
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/UtilsSuite.java36
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/AbstractCacheManager.java6
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/BreakpointsCM.java8
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/ResetMap.java52
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/RunControlCM.java19
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/StackTraceCM.java167
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/SymbolsCM.java3
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/AbstractCache.java54
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Callback.java34
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/CallbackCache.java2
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/RangeCache.java329
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TokenCache.java3
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Transaction.java109
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TransactionCache.java23
17 files changed, 1328 insertions, 215 deletions
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/RangeCacheTests.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/RangeCacheTests.java
new file mode 100644
index 0000000..ed23864
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/RangeCacheTests.java
@@ -0,0 +1,477 @@
+/*******************************************************************************
+ * Copyright (c) 2006 Wind River Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.debug.test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.eclipse.tcf.debug.test.util.Callback;
+import org.eclipse.tcf.debug.test.util.DataCallback;
+import org.eclipse.tcf.debug.test.util.ICache;
+import org.eclipse.tcf.debug.test.util.Query;
+import org.eclipse.tcf.debug.test.util.RangeCache;
+import org.eclipse.tcf.protocol.Protocol;
+
+/**
+ * Tests that exercise the DataCache object.
+ */
+public class RangeCacheTests extends TestCase {
+
+ class TestRangeCache extends RangeCache<Integer> {
+
+ @Override
+ protected void retrieve(long offset, int count, DataCallback<List<Integer>> rm) {
+ fRetrieveInfos.add(new RetrieveInfo(offset, count, rm));
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ }
+
+ }
+
+ class TestQuery extends Query<List<Integer>> {
+ long fOffset;
+ int fCount;
+ TestQuery(long offset, int count) {
+ fOffset = offset;
+ fCount = count;
+ }
+
+ @Override
+ protected void execute(final DataCallback<List<Integer>> rm) {
+ fRangeCache = fTestCache.getRange(fOffset, fCount);
+ if (fRangeCache.isValid()) {
+ rm.setData(fRangeCache.getData());
+ rm.setError(fRangeCache.getError());
+ rm.done();
+ } else {
+ fRangeCache.update(new Callback(rm) {
+ @Override
+ protected void handleSuccess() {
+ rm.setData(fRangeCache.getData());
+ rm.done();
+ }
+ });
+ }
+ }
+ }
+
+ class RetrieveInfo implements Comparable<RetrieveInfo> {
+ long fOffset;
+ int fCount;
+ DataCallback<List<Integer>> fRm;
+ RetrieveInfo(long offset, int count, DataCallback<List<Integer>> rm) {
+ fOffset = offset;
+ fCount = count;
+ fRm = rm;
+ }
+
+ public int compareTo(RetrieveInfo o) {
+ if (fOffset > o.fOffset) {
+ return 1;
+ } else if (fOffset == o.fOffset) {
+ return 0;
+ } else /*if (fOffset < o.fOffset)*/ {
+ return -1;
+ }
+ }
+ }
+
+ TestRangeCache fTestCache;
+ SortedSet<RetrieveInfo> fRetrieveInfos;
+ ICache<List<Integer>> fRangeCache;
+
+ private List<Integer> makeList(long offset, int count) {
+ List<Integer> list = new ArrayList<Integer>(count);
+ for (int i = 0; i < count; i++) {
+ list.add((int)(i + offset));
+ }
+ return list;
+ }
+
+ /**
+ * There's no rule on how quickly the cache has to start data retrieval
+ * after it has been requested. It could do it immediately, or it could
+ * wait a dispatch cycle, etc..
+ */
+ private void waitForRetrieveRm(int size) {
+ synchronized(this) {
+ while (fRetrieveInfos.size() < size) {
+ try {
+ wait(100);
+ } catch (InterruptedException e) {
+ return;
+ }
+ }
+ }
+ }
+
+ protected void setUp() throws Exception {
+ fTestCache = new TestRangeCache();
+ fRetrieveInfos = new TreeSet<RetrieveInfo>();
+ fRangeCache = null;
+ }
+
+ protected void tearDown() throws ExecutionException, InterruptedException {
+ fTestCache = null;
+ }
+
+ private void assertCacheValidWithData(ICache<List<Integer>> cache, long offset, int count) {
+ Assert.assertTrue(cache.isValid());
+ Assert.assertEquals(makeList(offset, count), cache.getData());
+ Assert.assertTrue(cache.getError() == null);
+ }
+
+ private void assertCacheValidWithError(ICache<List<Integer>> cache) {
+ Assert.assertTrue(cache.isValid());
+ Assert.assertFalse(cache.getError() == null);
+ }
+
+ private void assertCacheWaiting(ICache<List<Integer>> cache) {
+ Assert.assertFalse(cache.isValid());
+ try {
+ cache.getData();
+ Assert.fail("Expected an IllegalStateException");
+ } catch (IllegalStateException e) {}
+ try {
+ cache.getError();
+ Assert.fail("Expected an IllegalStateException");
+ } catch (IllegalStateException e) {}
+ }
+
+ private void completeInfo(final RetrieveInfo info, final long offset, final int count) {
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ info.fRm.setData(makeList(offset, count));
+ info.fRm.done();
+ }
+ });
+ }
+
+ private void getRange(long queryOffset, int queryCount, long[] retrieveOffsets, int retrieveCounts[]) throws InterruptedException, ExecutionException {
+ assert retrieveOffsets.length == retrieveCounts.length;
+ int retrieveCount = retrieveOffsets.length;
+
+ // Request data from cache
+ TestQuery q = new TestQuery(queryOffset, queryCount);
+
+ fRangeCache = null;
+ fRetrieveInfos.clear();
+
+ q.invoke();
+
+ // Wait until the cache requests the data.
+ waitForRetrieveRm(retrieveOffsets.length);
+
+ if (retrieveCount != 0) {
+ assertCacheWaiting(fRangeCache);
+
+ // Set the data
+ Assert.assertEquals(retrieveCount, fRetrieveInfos.size());
+ int i = 0;
+ for (RetrieveInfo info : fRetrieveInfos) {
+ completeInfo(info, retrieveOffsets[i], retrieveCounts[i]);
+ i++;
+ }
+ }
+
+ // Wait for data.
+ Assert.assertEquals(makeList(queryOffset, queryCount), q.get());
+
+ // Check state while waiting for data
+ assertCacheValidWithData(fRangeCache, queryOffset, queryCount);
+ }
+
+ public void testGetOneRangeTest() throws InterruptedException, ExecutionException {
+ getRange(0, 100, new long[] { 0 }, new int[] { 100 });
+ }
+
+ public void testGetMultipleRangesTest() throws InterruptedException, ExecutionException {
+ // Retrieve a range in-between two cached ranges
+ getRange(0, 100, new long[] { 0 }, new int[] { 100 });
+ getRange(200, 100, new long[] { 200 }, new int[] { 100 });
+ getRange(0, 300, new long[] { 100 }, new int[] { 100 });
+
+ // Retrieve a range overlapping two cached ranges
+ getRange(1000, 100, new long[] { 1000 }, new int[] { 100 });
+ getRange(1200, 100, new long[] { 1200 }, new int[] { 100 });
+ getRange(900, 500, new long[] { 900, 1100, 1300 }, new int[] { 100, 100, 100 });
+
+ // Retrieve a range that's a subset of a cached range.
+ getRange(2000, 100, new long[] { 2000 }, new int[] { 100 });
+ getRange(2000, 50, new long[] {}, new int[] {});
+ getRange(2025, 50, new long[] {}, new int[] {});
+ getRange(2050, 50, new long[] {}, new int[] {});
+ }
+
+ public void testGetIncompleteRangeTest() throws InterruptedException, ExecutionException {
+ long queryOffset = 0;
+ int queryCount = 100;
+ long[] retrieveOffsets = new long[] { 0 };
+ int retrieveCounts[] = new int[] { 50 };
+
+ assert retrieveOffsets.length == retrieveCounts.length;
+ int retrieveCount = retrieveOffsets.length;
+
+ // Request data from cache
+ TestQuery q = new TestQuery(queryOffset, queryCount);
+
+ fRangeCache = null;
+ fRetrieveInfos.clear();
+
+ q.invoke();
+
+ // Wait until the cache requests the data.
+ waitForRetrieveRm(retrieveOffsets.length);
+
+ if (retrieveCount != 0) {
+ assertCacheWaiting(fRangeCache);
+
+ // Set the data
+ Assert.assertEquals(retrieveCount, fRetrieveInfos.size());
+ int i = 0;
+ for (RetrieveInfo info : fRetrieveInfos) {
+ completeInfo(info, retrieveOffsets[i], retrieveCounts[i]);
+ i++;
+ }
+ }
+
+ // Wait for data.
+ List<Integer> data = q.get();
+ Assert.assertEquals(100, data.size());
+ for (int i = 50; i < 100; i++) {
+ Assert.assertEquals(null, data.get(i));
+ }
+
+ // Check state while waiting for data
+ assertCacheValidWithData(fRangeCache, 0, 50);
+ }
+
+
+ private void cancelRange(long queryOffset, int queryCount, long[] retrieveOffsets, int retrieveCounts[]) throws Exception {
+ int retrieveCount = retrieveOffsets.length;
+
+ // Request data from cache
+ TestQuery q = new TestQuery(queryOffset, queryCount);
+
+ fRangeCache = null;
+ fRetrieveInfos.clear();
+
+ q.invoke();
+
+ // Wait until the cache requests the data.
+ waitForRetrieveRm(retrieveCount);
+
+ assertCacheWaiting(fRangeCache);
+
+ // Set the data without using an executor.
+ Assert.assertEquals(retrieveCount, fRetrieveInfos.size());
+ int i = 0;
+ for (RetrieveInfo info : fRetrieveInfos) {
+ Assert.assertEquals(retrieveOffsets[i], info.fOffset);
+ Assert.assertEquals(retrieveCounts[i], info.fCount);
+ Assert.assertFalse(info.fRm.isCanceled());
+ i++;
+ }
+
+ q.cancel(true);
+ try {
+ q.get();
+ Assert.fail("Expected a cancellation exception");
+ } catch (CancellationException e) {} // Expected exception;
+
+ for (RetrieveInfo info : fRetrieveInfos) {
+ Assert.assertTrue(info.fRm.isCanceled());
+ }
+ }
+
+ public void testCancelOneRangeTest() throws Exception {
+ cancelRange(0, 100, new long[] { 0 }, new int[] { 100 });
+ }
+
+ public void testCancelMultipleRangesTest() throws Exception {
+ // Cancel a couple of ranges.
+ cancelRange(0, 100, new long[] { 0 }, new int[] { 100 });
+ cancelRange(200, 100, new long[] { 200 }, new int[] { 100 });
+
+ // Cancel a range overlapping two previously canceled ranges.
+ cancelRange(0, 300, new long[] { 0 }, new int[] { 300 });
+ }
+
+ public void testGetAndCancelMultipleRangesTest() throws Exception {
+ // Cancel a range, then retrieve the same range
+ cancelRange(0, 100, new long[] { 0 }, new int[] { 100 });
+ getRange(0, 100, new long[] { 0 }, new int[] { 100 });
+
+ // Cancel a range overlapping a cached range.
+ cancelRange(0, 200, new long[] { 100 }, new int[] { 100 });
+ }
+
+ public void testResetOneRangeTest() throws InterruptedException, ExecutionException {
+ getRange(0, 100, new long[] { 0 }, new int[] { 100 });
+
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ fTestCache.reset();
+ };
+ });
+
+ getRange(0, 100, new long[] { 0 }, new int[] { 100 });
+ }
+
+ public void testResetMultipleRangesTest() throws InterruptedException, ExecutionException {
+ // Retrieve a range in-between two cached ranges
+ getRange(0, 100, new long[] { 0 }, new int[] { 100 });
+ getRange(200, 100, new long[] { 200 }, new int[] { 100 });
+ getRange(0, 300, new long[] { 100 }, new int[] { 100 });
+
+ // Retrieve a range overlapping two cached ranges
+ getRange(1000, 100, new long[] { 1000 }, new int[] { 100 });
+ getRange(1200, 100, new long[] { 1200 }, new int[] { 100 });
+ getRange(900, 500, new long[] { 900, 1100, 1300 }, new int[] { 100, 100, 100 });
+
+ // Retrieve a range that's a subset of a cached range.
+ getRange(2000, 100, new long[] { 2000 }, new int[] { 100 });
+ getRange(2000, 50, new long[] {}, new int[] {});
+ getRange(2025, 50, new long[] {}, new int[] {});
+ getRange(2050, 50, new long[] {}, new int[] {});
+
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ fTestCache.reset();
+ };
+ });
+
+ // Retrieve a range in-between two cached ranges
+ getRange(0, 100, new long[] { 0 }, new int[] { 100 });
+ getRange(200, 100, new long[] { 200 }, new int[] { 100 });
+ getRange(0, 300, new long[] { 100 }, new int[] { 100 });
+
+ // Retrieve a range overlapping two cached ranges
+ getRange(1000, 100, new long[] { 1000 }, new int[] { 100 });
+ getRange(1200, 100, new long[] { 1200 }, new int[] { 100 });
+ getRange(900, 500, new long[] { 900, 1100, 1300 }, new int[] { 100, 100, 100 });
+
+ // Retrieve a range that's a subset of a cached range.
+ getRange(2000, 100, new long[] { 2000 }, new int[] { 100 });
+ getRange(2000, 50, new long[] {}, new int[] {});
+ getRange(2025, 50, new long[] {}, new int[] {});
+ getRange(2050, 50, new long[] {}, new int[] {});
+ }
+
+ public void testResetWhileInvalidTest() throws InterruptedException, ExecutionException {
+ // Request data from cache
+ TestQuery q = new TestQuery(10, 100);
+
+ fRangeCache = null;
+ fRetrieveInfos.clear();
+
+ q.invoke();
+
+ // Wait until the cache requests the data.
+ waitForRetrieveRm(1);
+
+ assertCacheWaiting(fRangeCache);
+
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ fTestCache.reset();
+ };
+ });
+
+ // Set the data without using an executor.
+ Assert.assertEquals(1, fRetrieveInfos.size());
+ completeInfo(fRetrieveInfos.first(), 10, 100);
+
+ // Wait for data.
+ Assert.assertEquals(makeList(10, 100), q.get());
+
+ // Check state while waiting for data
+ assertCacheValidWithData(fRangeCache, 10, 100);
+ }
+
+ public void testSetRangeErrorTest() throws InterruptedException, ExecutionException {
+
+ // Retrieve a range of data.
+ getRange(0, 100, new long[] { 0 }, new int[] { 100 });
+
+ // Force an error status into the range cache.
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ fTestCache.set(0, 100, null, new Throwable( "Cache invalid" ));
+ };
+ });
+
+ // Retrieve a range cache and check that it immediately contains the error status in it.
+ fRangeCache = null;
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ fRangeCache = fTestCache.getRange(0, 100);
+ }
+ });
+
+ assertCacheValidWithError(fRangeCache);
+
+ // Request an update from the range and check for exception.
+ TestQuery q = new TestQuery(10, 90);
+
+ fRangeCache = null;
+ fRetrieveInfos.clear();
+
+ q.invoke();
+
+ try {
+ q.get();
+ Assert.fail("Expected an ExecutionException");
+ } catch (ExecutionException e) {}
+ }
+
+ public void testGetOneRangeUsingDifferentRangeInstanceTest() throws InterruptedException, ExecutionException {
+ // Request data from cache
+ TestQuery q = new TestQuery(0, 100);
+
+ fRangeCache = null;
+ fRetrieveInfos.clear();
+
+ q.invoke();
+
+ // Wait until the cache requests the data.
+ waitForRetrieveRm(1);
+
+ assertCacheWaiting(fRangeCache);
+
+ // Set the data without using an executor.
+ Assert.assertEquals(1, fRetrieveInfos.size());
+ RetrieveInfo info = fRetrieveInfos.iterator().next();
+ completeInfo(info, 0, 100);
+
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ fRangeCache = fTestCache.getRange(0, 100);
+ }
+ });
+
+ // Check state while waiting for data
+ assertCacheValidWithData(fRangeCache, 0, 100);
+ }
+
+
+
+}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/SampleTest.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/SampleTest.java
index 4c49aa4..7ab4976 100644
--- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/SampleTest.java
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/SampleTest.java
@@ -10,10 +10,13 @@ import java.util.regex.Pattern;
import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualItem;
import org.eclipse.tcf.debug.test.services.IWaitForEventCache;
import org.eclipse.tcf.debug.test.services.RunControlCM;
+import org.eclipse.tcf.debug.test.util.ICache;
+import org.eclipse.tcf.debug.test.util.RangeCache;
import org.eclipse.tcf.debug.test.util.Transaction;
import org.eclipse.tcf.services.ILineNumbers.CodeArea;
import org.eclipse.tcf.services.IRunControl;
import org.eclipse.tcf.services.IRunControl.RunControlContext;
+import org.eclipse.tcf.services.IStackTrace.StackTraceContext;
import org.eclipse.tcf.services.ISymbols;
import org.eclipse.tcf.services.ISymbols.Symbol;
import org.junit.Assert;
@@ -269,6 +272,59 @@ public class SampleTest extends AbstractTcfUITest {
}
}.get();
}
+
+ public void testStackTraceCMResetOnContextStateChange() throws Exception {
+ initProcessModel("tcf_test_func2");
+
+ // Retrieve the current PC and top frame for use later
+ final String pc = new Transaction<String>() {
+ @Override
+ protected String process() throws InvalidCacheException, ExecutionException {
+ return validate(fRunControlCM.getState(fThreadId)).pc;
+ }
+ }.get();
+
+ // Retrieve data from caches (make them valid).
+ new Transaction<Object>() {
+ @Override
+ protected String process() throws InvalidCacheException, ExecutionException {
+ String[] frameIds = validate( fStackTraceCM.getChildren(fThreadId) );
+ validate (fStackTraceCM.getContexts(frameIds));
+ RangeCache<StackTraceContext> framesRange = fStackTraceCM.getContextRange(fThreadId);
+ validate( framesRange.getRange(0, frameIds.length) );
+ return null;
+ }
+ }.get();
+
+
+ // Execute a step.
+ resumeAndWaitForSuspend(fThreadCtx, IRunControl.RM_STEP_OUT);
+
+ // End test, check that all caches were reset and now return an error.
+ new Transaction<Object>() {
+ @Override
+ protected Object process() throws InvalidCacheException, ExecutionException {
+ ICache<String[]> frameIdsCache = fStackTraceCM.getChildren(fThreadId);
+ Assert.assertFalse("Expected cache to be reset", frameIdsCache.isValid());
+ return null;
+ }
+ }.get();
+
+ new Transaction<Object>() {
+ @Override
+ protected Object process() throws InvalidCacheException, ExecutionException {
+ String[] frameIds = validate( fStackTraceCM.getChildren(fThreadId) );
+ ICache<StackTraceContext[]> cache = fStackTraceCM.getContexts(frameIds);
+ Assert.assertFalse("Expected cache to be reset", cache.isValid());
+
+ RangeCache<StackTraceContext> framesRange = fStackTraceCM.getContextRange(fThreadId);
+ ICache<List<StackTraceContext>> framesRangeCache = framesRange.getRange(0, frameIds.length);
+ Assert.assertFalse("Expected cache to be reset", framesRangeCache.isValid());
+
+ return null;
+ }
+ }.get();
+ }
public void testRunControlCMChildrenInvalidation() throws Exception {
initProcessModel("tcf_test_func0");
@@ -323,12 +379,14 @@ public class SampleTest extends AbstractTcfUITest {
new Transaction<String>() {
@Override
protected String process() throws InvalidCacheException, ExecutionException {
- validate( fDiagnosticsCM.cancelTest(fTestId, this) );
// Create wait caches
+ fRunControlCM.waitForContextRemoved(fProcessId, this);
IWaitForEventCache<?>[] waitCaches = new IWaitForEventCache<?>[threads.length];
for (int i = 0; i < threads.length; i++) {
waitCaches[i] = fRunControlCM.waitForContextRemoved(threads[i], this);
}
+ validate( fDiagnosticsCM.cancelTest(fTestId, this) );
+ validate(waitCaches);
validate(fRunControlCM.waitForContextRemoved(fProcessId, this));
try {
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TransactionTests.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TransactionTests.java
index f1d0d19..101f9a4 100644
--- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TransactionTests.java
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TransactionTests.java
@@ -10,7 +10,9 @@
*******************************************************************************/
package org.eclipse.tcf.debug.test;
-import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
import java.util.concurrent.ExecutionException;
import junit.framework.Assert;
@@ -29,7 +31,7 @@ public class TransactionTests extends TestCase {
final static private int NUM_CACHES = 5;
TestCache[] fTestCaches = new TestCache[NUM_CACHES];
- DataCallback<?>[] fRetrieveRms = new DataCallback<?>[NUM_CACHES];
+ Map<DataCallback<?>, Boolean> fRetrieveRms;
class TestCache extends CallbackCache<Integer> {
@@ -42,7 +44,7 @@ public class TransactionTests extends TestCase {
@Override
protected void retrieve(DataCallback<Integer> rm) {
synchronized(TransactionTests.this) {
- fRetrieveRms[fIndex] = rm;
+ fRetrieveRms.put(rm, true);
TransactionTests.this.notifyAll();
}
}
@@ -70,47 +72,95 @@ public class TransactionTests extends TestCase {
return sum;
}
}
+
+ class TestSingleTransactionUnchecked extends Transaction<Integer> {
+
+ @Override
+ protected boolean processUnchecked() {
+ if (!validateUnchecked(fTestCaches[0])) {
+ return false;
+ }
+ assert fTestCaches[0].getError() == null;
+ setData(fTestCaches[0].getData());
+ return true;
+ }
+ }
+
+ class TestSumTransactionUnchecked extends Transaction<Integer> {
+ @Override
+ protected boolean processUnchecked() {
+ if (!validateUnchecked(fTestCaches)) {
+ return false;
+ }
+
+ int sum = 0;
+ for (CallbackCache<Integer> cache : fTestCaches) {
+ assert cache.getError() == null;
+ sum += cache.getData();
+ }
+ setData(sum);
+ return true;
+ }
+ }
+
+ class TestSumTransactionIterative extends Transaction<Integer> {
+ @Override
+ protected boolean processUnchecked() {
+ int sum = 0;
+ for (CallbackCache<Integer> cache : fTestCaches) {
+ if (!validateUnchecked(cache)) {
+ return false;
+ }
+ assert cache.getError() == null;
+ sum += cache.getData();
+ }
+ setData(sum);
+ return true;
+ }
+ }
/**
* There's no rule on how quickly the cache has to start data retrieval
* after it has been requested. It could do it immediately, or it could
* wait a dispatch cycle, etc..
*/
- private void waitForRetrieveRm(boolean all) {
+ private int waitForRetrieveRm() {
synchronized(this) {
- if (all) {
- while (Arrays.asList(fRetrieveRms).contains(null)) {
- try {
- wait();
- } catch (InterruptedException e) {
- return;
- }
- }
- } else {
- while (fRetrieveRms[0] == null) {
- try {
- wait();
- } catch (InterruptedException e) {
- return;
- }
+ while (!checkRetrieveRms()) {
+ try {
+ wait(100);
+ } catch (InterruptedException e) {
+ return NUM_CACHES;
}
}
+ return fRetrieveRms.size();
}
}
+ private boolean checkRetrieveRms() {
+ if (fRetrieveRms.size() == NUM_CACHES) return true;
+
+ boolean retVal = false;
+ for (DataCallback<?> rm : fRetrieveRms.keySet()) {
+ if (fRetrieveRms.get(rm)) {
+ retVal = true;
+ }
+ }
+ return retVal;
+ }
+
public void setUp() throws ExecutionException, InterruptedException {
+ fRetrieveRms = new HashMap<DataCallback<?>, Boolean>();
for (int i = 0; i < fTestCaches.length; i++) {
fTestCaches[i] = new TestCache(i);
}
}
public void tearDown() throws ExecutionException, InterruptedException {
- fRetrieveRms = new DataCallback<?>[NUM_CACHES];
fTestCaches = new TestCache[NUM_CACHES];
}
- public void testSingleTransaction() throws InterruptedException, ExecutionException {
- final TestSingleTransaction testTransaction = new TestSingleTransaction();
+ private void doTestSingleTransaction(final Transaction<Integer> testTransaction) throws InterruptedException, ExecutionException {
// Request data from cache
Query<Integer> q = new Query<Integer>() {
@Override
@@ -121,22 +171,27 @@ public class TransactionTests extends TestCase {
q.invoke();
// Wait until the cache starts data retrieval.
- waitForRetrieveRm(false);
+ waitForRetrieveRm();
// Set the data to caches.
Protocol.invokeAndWait(new Runnable() {
public void run() {
- ((DataCallback<Integer>)fRetrieveRms[0]).setData(1);
- fRetrieveRms[0].done();
+ @SuppressWarnings("unchecked")
+ DataCallback<Integer> cb = ((DataCallback<Integer>)fRetrieveRms.keySet().iterator().next());
+ cb.setData(1);
+ cb.done();
}
});
Assert.assertEquals(1, (int)q.get());
}
-
- public void testSumTransaction() throws InterruptedException, ExecutionException {
- final TestSumTransaction testTransaction = new TestSumTransaction();
+ public void testSingleTransaction() throws InterruptedException, ExecutionException {
+ doTestSingleTransaction(new TestSingleTransaction());
+ }
+
+ private void doTestSumTransaction(final Transaction<Integer> testTransaction) throws InterruptedException, ExecutionException {
+
// Request data from cache
Query<Integer> q = new Query<Integer>() {
@Override
@@ -146,22 +201,50 @@ public class TransactionTests extends TestCase {
};
q.invoke();
- // Wait until the cache starts data retrieval.
- waitForRetrieveRm(true);
-
- // Set the data to caches.
- Protocol.invokeAndWait(new Runnable() {
- public void run() {
- for (DataCallback<?> rm : fRetrieveRms) {
- ((DataCallback<Integer>)rm).setData(1);
- rm.done();
+ int numRms = 0;
+ while (numRms != NUM_CACHES) {
+ // Wait until the cache starts data retrieval.
+ numRms = waitForRetrieveRm();
+
+ // Set the data to caches.
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ for (Iterator<DataCallback<?>> itr = fRetrieveRms.keySet().iterator(); itr.hasNext();) {
+ @SuppressWarnings("unchecked")
+ DataCallback<Integer> rm =((DataCallback<Integer>)itr.next());
+ if (fRetrieveRms.get(rm)) {
+ rm.setData(1);
+ rm.done();
+ fRetrieveRms.put(rm, false);
+ itr = fRetrieveRms.keySet().iterator();
+ }
+ }
}
- }
- });
+ });
+ }
q.invoke();
Assert.assertEquals(NUM_CACHES, (int)q.get());
}
+ public void testSumTransaction() throws InterruptedException, ExecutionException {
+ doTestSumTransaction(new TestSumTransaction());
+ }
+
+ public void testSingleTransactionUnchecked() throws InterruptedException, ExecutionException {
+ doTestSingleTransaction(new TestSingleTransactionUnchecked());
+ }
+
+ public void testSumTransactionUnchecked() throws InterruptedException, ExecutionException {
+ doTestSumTransaction(new TestSumTransactionUnchecked());
+ }
+
+
+ public void testSumTransactionIterative() throws InterruptedException, ExecutionException {
+
+ doTestSumTransaction(new TestSumTransactionIterative());
+
+ }
+
}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/UtilsSuite.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/UtilsSuite.java
new file mode 100644
index 0000000..99a51ad
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/UtilsSuite.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Wind River Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.debug.test;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ *
+ */
+public class UtilsSuite extends TestSuite {
+
+ public UtilsSuite() throws Exception {
+ addTest(new TestSuite(CacheTests.class));
+ addTest(new TestSuite(TransactionTests.class));
+ addTest(new TestSuite(QueryTests.class));
+ addTest(new TestSuite(RangeCacheTests.class));
+ }
+
+ /**
+ * Returns the suite. This is required to
+ * use the JUnit Launcher.
+ */
+ public static Test suite() throws Exception {
+ return new UtilsSuite();
+ }
+
+}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/AbstractCacheManager.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/AbstractCacheManager.java
index 9094834..b3d15cd 100644
--- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/AbstractCacheManager.java
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/AbstractCacheManager.java
@@ -3,11 +3,9 @@ package org.eclipse.tcf.debug.test.services;
import java.util.LinkedHashMap;
import java.util.Map;
-import org.eclipse.tcf.debug.test.util.ICache;
-
public class AbstractCacheManager {
- protected Map<Key<?>, ICache<?>> fMap = new LinkedHashMap<Key<?>, ICache<?>>();
+ protected Map<Key<?>, Object> fMap = new LinkedHashMap<Key<?>, Object>();
public AbstractCacheManager() {
super();
@@ -27,7 +25,7 @@ public class AbstractCacheManager {
V cache = (V)fMap.get(key);
if (cache != null) return cache;
cache = key.createCache();
- fMap.put(key, (ICache<?>)cache);
+ fMap.put(key, cache);
return cache;
}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/BreakpointsCM.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/BreakpointsCM.java
index b4f1798..1a72866 100644
--- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/BreakpointsCM.java
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/BreakpointsCM.java
@@ -244,7 +244,7 @@ public class BreakpointsCM extends AbstractCacheManager implements IBreakpoints.
}
// TODO: avoid iterating over all entries, use separate list for events.
- for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) {
+ for (Map.Entry<Key<?>, Object> entry: fMap.entrySet()) {
if (entry.getKey() instanceof IdEventKey) {
IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey();
if ( StatusChangedCache.class.equals( eventKey.getCacheClass() ) &&
@@ -286,7 +286,7 @@ public class BreakpointsCM extends AbstractCacheManager implements IBreakpoints.
setBreakpointsProperties(bps);
// TODO: avoid iterating over all entries, use separate list for events.
- for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) {
+ for (Map.Entry<Key<?>, Object> entry: fMap.entrySet()) {
if (entry.getKey() instanceof EventKey) {
EventKey<?> eventKey = (EventKey<?>)entry.getKey();
if ( ContextAddedCache.class.equals( eventKey.getCacheClass() ) ) {
@@ -311,7 +311,7 @@ public class BreakpointsCM extends AbstractCacheManager implements IBreakpoints.
setBreakpointsProperties(bps);
// TODO: avoid iterating over all entries, use separate list for events.
- for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) {
+ for (Map.Entry<Key<?>, Object> entry: fMap.entrySet()) {
if (entry.getKey() instanceof EventKey) {
EventKey<?> eventKey = (EventKey<?>)entry.getKey();
if ( ContextChangedCache.class.equals( eventKey.getCacheClass() ) ) {
@@ -344,7 +344,7 @@ public class BreakpointsCM extends AbstractCacheManager implements IBreakpoints.
}
// TODO: avoid iterating over all entries, use separate list for events.
- for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) {
+ for (Map.Entry<Key<?>, Object> entry: fMap.entrySet()) {
if (entry.getKey() instanceof EventKey) {
EventKey<?> eventKey = (EventKey<?>)entry.getKey();
if ( ContextRemovedCache.class.equals( eventKey.getCacheClass() ) ) {
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/ResetMap.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/ResetMap.java
index 56bfc28..ac9ff88 100644
--- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/ResetMap.java
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/ResetMap.java
@@ -27,14 +27,18 @@ import org.eclipse.tcf.protocol.Protocol;
*/
public class ResetMap {
+ public interface IResettable {
+ public void reset();
+ }
+
public static final String ANY_ID = "";
- private Map<String, List<AbstractCache<?>>> fValid = new TreeMap<String, List<AbstractCache<?>>>();
+ private Map<String, List<IResettable>> fValid = new TreeMap<String, List<IResettable>>();
private Map<String, Set<String>> fChildren = new TreeMap<String, Set<String>>();
private Map<String, String> fParents = new TreeMap<String, String>();
- private Map<AbstractCache<?>, Set<String>> fPending = new LinkedHashMap<AbstractCache<?>, Set<String>>();
+ private Map<IResettable, Set<String>> fPending = new LinkedHashMap<IResettable, Set<String>>();
- public synchronized Set<String> removePending(AbstractCache<?> cache) {
+ public synchronized Set<String> removePending(IResettable cache) {
Set<String> pendingIds = fPending.remove(cache);
if (pendingIds == null) {
pendingIds = Collections.emptySet();
@@ -42,23 +46,23 @@ public class ResetMap {
return pendingIds;
}
- public synchronized void addValid(String id, AbstractCache<?> cache) {
+ public synchronized void addValid(String id, IResettable cache) {
assert !fPending.containsKey(cache);
- List<AbstractCache<?>> list = fValid.get(id);
+ List<IResettable> list = fValid.get(id);
if (list == null) {
- list = new ArrayList<AbstractCache<?>>();
+ list = new ArrayList<IResettable>();
fValid.put(id, list);
}
list.add(cache);
}
- public synchronized void addValid(String id, String[] childrenIds, AbstractCache<?> cache) {
+ public synchronized void addValid(String id, String[] childrenIds, IResettable cache) {
assert !fPending.containsKey(cache);
- List<AbstractCache<?>> list = fValid.get(id);
+ List<IResettable> list = fValid.get(id);
if (list == null) {
- list = new ArrayList<AbstractCache<?>>();
+ list = new ArrayList<IResettable>();
fValid.put(id, list);
}
list.add(cache);
@@ -67,13 +71,13 @@ public class ResetMap {
}
}
- public synchronized void addValid(List<String> ids, AbstractCache<?> cache) {
+ public synchronized void addValid(List<String> ids, IResettable cache) {
assert !fPending.containsKey(cache);
String id = ids.get(0);
- List<AbstractCache<?>> list = fValid.get(id);
+ List<IResettable> list = fValid.get(id);
if (list == null) {
- list = new ArrayList<AbstractCache<?>>();
+ list = new ArrayList<IResettable>();
fValid.put(id, list);
}
list.add(cache);
@@ -88,8 +92,8 @@ public class ResetMap {
}
}
- public synchronized List<AbstractCache<?>> getCaches(String id) {
- List<AbstractCache<?>> list = fValid.get(id);
+ public synchronized List<IResettable> getCaches(String id) {
+ List<IResettable> list = fValid.get(id);
if (list == null) {
list = Collections.emptyList();
}
@@ -105,9 +109,9 @@ public class ResetMap {
// Do not call reset while holding lock to reset map. Instead collect
// caches to reset and reset them outside the lock.
- List<AbstractCache<?>> anyList = Collections.emptyList();
- List<AbstractCache<?>> idList = Collections.emptyList();
- List<AbstractCache<?>> parentList = Collections.emptyList();
+ List<IResettable> anyList = Collections.emptyList();
+ List<IResettable> idList = Collections.emptyList();
+ List<IResettable> parentList = Collections.emptyList();
synchronized (this) {
for (Set<String> pendingIds : fPending.values()) {
pendingIds.add(id);
@@ -115,7 +119,7 @@ public class ResetMap {
anyList = fValid.remove(ANY_ID);
if (resetChildren && fChildren.containsKey(id)) {
- idList = new ArrayList<AbstractCache<?>>();
+ idList = new ArrayList<IResettable>();
collectChildren(id, idList);
} else {
idList = fValid.remove(id);
@@ -133,7 +137,7 @@ public class ResetMap {
resetList(parentList);
}
- private void collectChildren(String id, List<AbstractCache<?>> caches) {
+ private void collectChildren(String id, List<IResettable> caches) {
caches.addAll( fValid.remove(id) );
Set<String> children = fChildren.remove(id);
if (children != null) {
@@ -143,17 +147,15 @@ public class ResetMap {
}
}
- private void resetList(List<AbstractCache<?>> list) {
+ private void resetList(List<IResettable> list) {
if (list != null) {
- for (AbstractCache<?> cache : list) {
- if (cache.isValid()) {
- cache.reset();
- }
+ for (IResettable cache : list) {
+ cache.reset();
}
}
}
- public synchronized void addPending(AbstractCache<?> cache) {
+ public synchronized void addPending(IResettable cache) {
fPending.put(cache, new TreeSet<String>());
}
}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/RunControlCM.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/RunControlCM.java
index ca4b819..8031027 100644
--- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/RunControlCM.java
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/RunControlCM.java
@@ -17,6 +17,7 @@ import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ExecutionException;
+import org.eclipse.tcf.debug.test.services.ResetMap.IResettable;
import org.eclipse.tcf.debug.test.util.CallbackCache;
import org.eclipse.tcf.debug.test.util.DataCallback;
import org.eclipse.tcf.debug.test.util.ICache;
@@ -154,7 +155,7 @@ public class RunControlCM extends AbstractCacheManager implements RunControlList
}
- private class ContextStateCache extends CallbackCache<ContextState> {
+ private class ContextStateCache extends CallbackCache<ContextState> implements IResettable {
private class InnerContextStateCache extends TokenCache<ContextState> implements IRunControl.DoneGetState {
private final RunControlContext fContext;
@@ -327,7 +328,7 @@ public class RunControlCM extends AbstractCacheManager implements RunControlList
listener.containerResumed(context_ids);
}
- for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) {
+ for (Map.Entry<Key<?>, Object> entry: fMap.entrySet()) {
if (entry.getKey() instanceof IdEventKey) {
IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey();
if ( WaitForContainerResumedCache.class.equals(eventKey.getCacheClass()) &&
@@ -361,7 +362,7 @@ public class RunControlCM extends AbstractCacheManager implements RunControlList
listener.containerSuspended(context, pc, reason, params, suspended_ids);
}
- for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) {
+ for (Map.Entry<Key<?>, Object> entry: fMap.entrySet()) {
if (entry.getKey() instanceof IdEventKey) {
IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey();
if ( WaitForContainerSuspendedCache.class.equals( eventKey.getCacheClass() ) &&
@@ -413,7 +414,7 @@ public class RunControlCM extends AbstractCacheManager implements RunControlList
}
// TODO: avoid iterating over all entries, use separate list for events.
- for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) {
+ for (Map.Entry<Key<?>, Object> entry: fMap.entrySet()) {
if (entry.getKey() instanceof IdEventKey) {
IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey();
if ( WaitForContextSuspendedCache.class.equals( eventKey.getCacheClass() ) &&
@@ -456,7 +457,7 @@ public class RunControlCM extends AbstractCacheManager implements RunControlList
}
// TODO: avoid iterating over all entries, use separate list for events.
- for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) {
+ for (Map.Entry<Key<?>, Object> entry: fMap.entrySet()) {
if (entry.getKey() instanceof IdEventKey) {
IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey();
if ( WaitForContextResumedCache.class.equals( eventKey.getCacheClass() ) &&
@@ -483,7 +484,7 @@ public class RunControlCM extends AbstractCacheManager implements RunControlList
fStateResetMap.reset(id);
// TODO: avoid iterating over all entries, use separate list for events.
- for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) {
+ for (Map.Entry<Key<?>, Object> entry: fMap.entrySet()) {
if (entry.getKey() instanceof IdEventKey) {
IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey();
if ( WaitForContextExceptionCache.class.equals( eventKey.getCacheClass() ) &&
@@ -547,7 +548,7 @@ public class RunControlCM extends AbstractCacheManager implements RunControlList
fChildrenResetMap.reset(parent, false, false);
}
- for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) {
+ for (Map.Entry<Key<?>, Object> entry: fMap.entrySet()) {
if (entry.getKey() instanceof ContextEventKey) {
ContextEventKey<?> eventKey = (ContextEventKey<?>)entry.getKey();
if ( WaitForContextAddedCache.class.equals( eventKey.getCacheClass()) &&
@@ -580,7 +581,7 @@ public class RunControlCM extends AbstractCacheManager implements RunControlList
fChildrenResetMap.reset(context.getID(), true, false);
}
- for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) {
+ for (Map.Entry<Key<?>, Object> entry: fMap.entrySet()) {
if (entry.getKey() instanceof ContextEventKey) {
ContextEventKey<?> eventKey = (ContextEventKey<?>)entry.getKey();
if ( WaitForContextChangedCache.class.equals( eventKey.getCacheClass()) &&
@@ -614,7 +615,7 @@ public class RunControlCM extends AbstractCacheManager implements RunControlList
fStateResetMap.reset(context_id);
}
- for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) {
+ for (Map.Entry<Key<?>, Object> entry: fMap.entrySet()) {
if (entry.getKey() instanceof IdEventKey) {
IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey();
if ( WaitForContextRemovedCache.class.equals( eventKey.getCacheClass()) &&
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/StackTraceCM.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/StackTraceCM.java
index 5f8b582..e913a26 100644
--- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/StackTraceCM.java
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/StackTraceCM.java
@@ -10,10 +10,18 @@
*******************************************************************************/
package org.eclipse.tcf.debug.test.services;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import org.eclipse.tcf.debug.test.services.ResetMap.IResettable;
+import org.eclipse.tcf.debug.test.util.DataCallback;
import org.eclipse.tcf.debug.test.util.ICache;
+import org.eclipse.tcf.debug.test.util.RangeCache;
import org.eclipse.tcf.debug.test.util.TokenCache;
+import org.eclipse.tcf.debug.test.util.Transaction;
import org.eclipse.tcf.protocol.IToken;
import org.eclipse.tcf.services.IRunControl;
import org.eclipse.tcf.services.IRunControl.RunControlContext;
@@ -26,6 +34,7 @@ import org.eclipse.tcf.services.IStackTrace.StackTraceContext;
public class StackTraceCM extends AbstractCacheManager implements IRunControl.RunControlListener {
private IStackTrace fService;
private IRunControl fRunControl;
+ private final ResetMap fRunControlStateResetMap = new ResetMap();
public StackTraceCM(IStackTrace service, IRunControl runControl) {
fService = service;
@@ -39,70 +48,90 @@ public class StackTraceCM extends AbstractCacheManager implements IRunControl.Ru
super.dispose();
}
- private class ChildrenCache extends TokenCache<String[]> implements IStackTrace.DoneGetChildren {
- private final String fId;
- public ChildrenCache(String id) {
- fId = id;
- }
-
- @Override
- protected IToken retrieveToken() {
- return fService.getChildren(fId, this);
- }
- public void doneGetChildren(IToken token, Exception error, String[] context_ids) {
- set(token, context_ids, error);
- }
- public void resetChildren() {
- if (isValid()) reset();
- }
- };
+ public ICache<String[]> getChildren(final String id) {
+ class MyCache extends TokenCache<String[]> implements IStackTrace.DoneGetChildren {
+ @Override
+ protected IToken retrieveToken() {
+ fRunControlStateResetMap.addPending(this);
+ return fService.getChildren(id, this);
+ }
+
+ public void doneGetChildren(IToken token, Exception error, String[] context_ids) {
+ fRunControlStateResetMap.removePending(this);
+ fRunControlStateResetMap.addValid(id, this);
+ set(token, context_ids, error);
+ }
+ };
- private class ChildrenCacheKey extends IdKey<ChildrenCache> {
- public ChildrenCacheKey(String id) {
- super(ChildrenCache.class, id);
- }
- @Override ChildrenCache createCache() { return new ChildrenCache(fId); }
- }
-
- public ICache<String[]> getChildren(String id) {
- return mapCache(new ChildrenCacheKey(id));
+ return mapCache(new IdKey<MyCache>(MyCache.class, id) {
+ @Override MyCache createCache() { return new MyCache(); }
+ });
}
- class ContextCache extends TokenCache<StackTraceContext> implements IStackTrace.DoneGetContext {
- private final String fId;
+ public RangeCache<StackTraceContext> getContextRange(final String parentId) {
- public ContextCache(String id) {
- fId = id;
- }
- @Override
- protected IToken retrieveToken() {
- return fService.getContext(new String[] { fId }, this);
- }
- public void doneGetContext(IToken token, Exception error, StackTraceContext[] contexts) {
- StackTraceContext context = contexts != null && contexts.length > 0 ? contexts[0] : null;
- set(token, context, error);
- }
- public void resetContext() {
- if (isValid()) reset();
- }
- }
-
- private class ContextCacheKey extends IdKey<ContextCache> {
- public ContextCacheKey(String id) {
- super(ContextCache.class, id);
- }
- @Override ContextCache createCache() { return new ContextCache(fId); }
+ class MyCache extends RangeCache<StackTraceContext> implements IResettable {
+ boolean fIsValid = false;
+ boolean fIsPending = false;
+ @Override
+ protected void retrieve(final long offset, final int count, DataCallback<List<StackTraceContext>> rm) {
+ if (!fIsPending) fRunControlStateResetMap.addPending(MyCache.this);
+ new Transaction<List<StackTraceContext>>() {
+ @Override
+ protected List<StackTraceContext> process() throws InvalidCacheException, ExecutionException {
+ String[] ids = validate(getChildren(parentId));
+ int adjustedCount = Math.min(count, ids.length + (int)offset);
+ String[] subIds = new String[adjustedCount];
+ System.arraycopy(ids, (int)offset, subIds, 0, adjustedCount);
+ StackTraceContext[] contexts = validate(getContexts(subIds));
+ if (!fIsValid) {
+ fRunControlStateResetMap.removePending(MyCache.this);
+ fRunControlStateResetMap.addValid(parentId, MyCache.this);
+ }
+ return Arrays.asList(contexts);
+
+ }
+ }.request(rm);
+ }
+
+ public void reset() {
+ fIsValid = false;
+ fIsPending = false;
+ @SuppressWarnings("unchecked")
+ List<StackTraceContext> emptyData = (List<StackTraceContext>)Collections.EMPTY_LIST;
+ set(0, 0, emptyData, new Throwable("Cache invalid") );
+ }
+
+ };
+
+ return mapCache(new IdKey<MyCache>(MyCache.class, parentId) {
+ @Override MyCache createCache() { return new MyCache(); }
+ });
+
}
- public ICache<StackTraceContext>[] getContext(final String[] ids) {
- @SuppressWarnings("unchecked")
- ICache<StackTraceContext>[] caches = (ICache<StackTraceContext>[])new ICache[ids.length];
- for (int i = 0; i < ids.length; i++) {
- caches[i] = mapCache(new ContextCacheKey(ids[i]));
+ public ICache<StackTraceContext[]> getContexts(final String[] ids) {
+ class MyCache extends TokenCache<StackTraceContext[]> implements IStackTrace.DoneGetContext {
+ @Override
+ protected IToken retrieveToken() {
+ fRunControlStateResetMap.addPending(this);
+ return fService.getContext(ids, this);
+ }
+
+ public void doneGetContext(IToken token, Exception error, StackTraceContext[] contexts) {
+ fRunControlStateResetMap.removePending(this);
+ fRunControlStateResetMap.addValid(contexts[0].getParentID(), this);
+ set(token, contexts, error);
+ }
}
- return caches;
+
+ return mapCache(new IdKey<MyCache>(MyCache.class, Arrays.toString(ids)) {
+ @Override MyCache createCache() { return new MyCache(); }
+ });
}
+
+
public void contextAdded(RunControlContext[] contexts) {
// TODO Auto-generated method stub
@@ -110,53 +139,39 @@ public class StackTraceCM extends AbstractCacheManager implements IRunControl.Ru
public void contextChanged(RunControlContext[] contexts) {
for (RunControlContext context : contexts) {
- resetRunControlContext(context.getID());
+ fRunControlStateResetMap.reset(context.getID());
}
}
public void contextRemoved(String[] context_ids) {
for (String id : context_ids) {
- resetRunControlContext(id);
+ fRunControlStateResetMap.reset(id);
}
}
public void contextSuspended(String context, String pc, String reason, Map<String, Object> params) {
- resetRunControlContext(context);
+ fRunControlStateResetMap.reset(context);
}
public void contextResumed(String context) {
- resetRunControlContext(context);
+ fRunControlStateResetMap.reset(context);
}
public void containerSuspended(String context, String pc, String reason, Map<String, Object> params,
String[] suspended_ids)
{
for (String id : suspended_ids) {
- resetRunControlContext(id);
+ fRunControlStateResetMap.reset(id);
}
}
public void containerResumed(String[] context_ids) {
for (String id : context_ids) {
- resetRunControlContext(id);
+ fRunControlStateResetMap.reset(id);
}
}
public void contextException(String context, String msg) {
- resetRunControlContext(context);
- }
-
- private void resetRunControlContext(String id) {
- ChildrenCache childrenCache = getCache(new ChildrenCacheKey(id));
- if (childrenCache != null && childrenCache.isValid() && childrenCache.getData() != null) {
- String[] frameIds = childrenCache.getData();
- for (String frameId : frameIds) {
- ContextCache contextCache = getCache(new ContextCacheKey(frameId));
- if (contextCache != null) {
- contextCache.resetContext();
- }
- }
- childrenCache.resetChildren();
- }
+ fRunControlStateResetMap.reset(context);
}
}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/SymbolsCM.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/SymbolsCM.java
index 3a1ce11..b0c276c 100644
--- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/SymbolsCM.java
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/SymbolsCM.java
@@ -16,6 +16,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
+import org.eclipse.tcf.debug.test.services.ResetMap.IResettable;
import org.eclipse.tcf.debug.test.util.AbstractCache;
import org.eclipse.tcf.debug.test.util.CallbackCache;
import org.eclipse.tcf.debug.test.util.DataCallback;
@@ -63,7 +64,7 @@ public class SymbolsCM extends AbstractCacheManager {
ANY_ID_PARENTS.add(ResetMap.ANY_ID);
}
- abstract private class SymbolCache<V> extends CallbackCache<V> {
+ abstract private class SymbolCache<V> extends CallbackCache<V> implements IResettable {
protected final AbstractCache<V> fInner;
private Symbol fSymbol;
private List<String> fParents = new ArrayList<String>(4);
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/AbstractCache.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/AbstractCache.java
index fcb7372..0b74b1b 100644
--- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/AbstractCache.java
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/AbstractCache.java
@@ -221,37 +221,45 @@ public abstract class AbstractCache<V> implements ICache<V> {
* canceled, or if there are no clients waiting at all.
*/
protected boolean isCanceled() {
+ Object waitingList = null;
+ synchronized (this) {
+ if (fWaitingList instanceof Callback[]) {
+ waitingList = new Callback[((Callback[])fWaitingList).length];
+ System.arraycopy(fWaitingList, 0, waitingList, 0, ((Callback[]) fWaitingList).length);
+ } else {
+ waitingList = fWaitingList;
+ }
+ }
boolean canceled;
List<Callback> canceledRms = null;
- synchronized (this) {
- if (fWaitingList instanceof Callback) {
- if ( ((Callback)fWaitingList).isCanceled() ) {
- canceledRms = new ArrayList<Callback>(1);
- canceledRms.add((Callback)fWaitingList);
- canceled = true;
- } else {
- canceled = false;
- }
- } else if(fWaitingList instanceof Callback[]) {
+ if (waitingList instanceof Callback) {
+ if ( ((Callback)waitingList).isCanceled() ) {
+ canceledRms = new ArrayList<Callback>(1);
+ canceledRms.add((Callback)waitingList);
canceled = true;
- Callback[] waitingList = (Callback[])fWaitingList;
- for (int i = 0; i < waitingList.length; i++) {
- if (waitingList[i] != null) {
- if (waitingList[i].isCanceled()) {
- if (canceledRms == null) {
- canceledRms = new ArrayList<Callback>(1);
- }
- canceledRms.add( waitingList[i] );
- } else {
- canceled = false;
+ } else {
+ canceled = false;
+ }
+ } else if(waitingList instanceof Callback[]) {
+ canceled = true;
+ Callback[] waitingListArray = (Callback[])waitingList;
+ for (int i = 0; i < waitingListArray.length; i++) {
+ if (waitingListArray[i] != null) {
+ if (waitingListArray[i].isCanceled()) {
+ if (canceledRms == null) {
+ canceledRms = new ArrayList<Callback>(1);
}
+ canceledRms.add( waitingListArray[i] );
+ } else {
+ canceled = false;
}
}
- } else {
- assert fWaitingList == null;
- canceled = true;
}
+ } else {
+ assert waitingList == null;
+ canceled = true;
}
+
if (canceledRms != null) {
final List<Callback> _canceledRms = canceledRms;
Protocol.invokeLater(new Runnable() {
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Callback.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Callback.java
index 227112f..dadc2bc 100644
--- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Callback.java
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Callback.java
@@ -265,22 +265,24 @@ public class Callback {
* issued. Even if the request was canceled.
* </p>
*/
- public synchronized void done() {
- assert Protocol.getEventQueue().isDispatchThread();
-
- if (fDone) {
- throw new IllegalStateException("Callback: " + this + ", done() method called more than once"); //$NON-NLS-1$//$NON-NLS-2$
- }
- fDone = true;
-
- // This Callback is done, it can no longer be canceled.
- // We must clear the list of cancelListeners because it causes a
- // circular reference between parent and child Callback, which
- // causes a memory leak.
- fCancelListeners = null;
-
- if (fParentCallback != null) {
- fParentCallback.removeCancelListener(fCanceledListener);
+ public void done() {
+ synchronized(this) {
+ assert Protocol.getEventQueue().isDispatchThread();
+
+ if (fDone) {
+ throw new IllegalStateException("Callback: " + this + ", done() method called more than once"); //$NON-NLS-1$//$NON-NLS-2$
+ }
+ fDone = true;
+
+ // This Callback is done, it can no longer be canceled.
+ // We must clear the list of cancelListeners because it causes a
+ // circular reference between parent and child Callback, which
+ // causes a memory leak.
+ fCancelListeners = null;
+
+ if (fParentCallback != null) {
+ fParentCallback.removeCancelListener(fCanceledListener);
+ }
}
handleCompleted();
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/CallbackCache.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/CallbackCache.java
index 9275118..4929a4d 100644
--- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/CallbackCache.java
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/CallbackCache.java
@@ -48,7 +48,7 @@ public abstract class CallbackCache<V> extends AbstractCache<V> {
@Override
public boolean isCanceled() {
return super.isCanceled() || CallbackCache.this.isCanceled();
- };
+ }
};
retrieve(fRm);
}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/RangeCache.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/RangeCache.java
new file mode 100644
index 0000000..3ef3f4b
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/RangeCache.java
@@ -0,0 +1,329 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Wind River Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.debug.test.util;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.ExecutionException;
+
+import org.eclipse.tcf.protocol.Protocol;
+
+/**
+ * Cache for efficiently retrieving overlapping ranges of elements from an
+ * asynchronous data source. The results of a range request (see
+ * {@link #getRange(long, int)}) are kept around and reused in subsequent
+ * requests. E.g., two individual, initial requests for 4 bytes at offsets 0 and
+ * 4 will relieve a subsequent request for 4 bytes at offset 2 from having to
+ * asynchronously retrieve the data from the source. The results from the first
+ * two range requests are used to service the third.
+ *
+ * <p>
+ * Clients of this cache should call {@link #getRange(long, int)} to get a cache
+ * for that given range of elements. Sub-classes must implement
+ * {@link #retrieve(long, int, DataRequestMonitor)} to retrieve data from the
+ * asynchronous data source.
+ *
+ *
+ *
+ * @since 2.2
+ */
+abstract public class RangeCache<V> {
+
+ private class Request extends CallbackCache<List<V>> implements Comparable<Request> {
+ long fOffset;
+ int fCount;
+ @Override
+ protected void retrieve(DataCallback<java.util.List<V>> rm) {
+ RangeCache.this.retrieve(fOffset, fCount, rm);
+ }
+
+ Request(long offset, int count) {
+ fOffset = offset;
+ fCount = count;
+ }
+
+ public int compareTo(RangeCache<V>.Request o) {
+ if (fOffset > o.fOffset) {
+ return 1;
+ } else if (fOffset == o.fOffset) {
+ return 0;
+ } else /*if (fOffset < o.fOffset)*/ {
+ return -1;
+ }
+ }
+
+ @Override
+ public boolean equals(Object _o) {
+ if (_o instanceof RangeCache<?>.Request) {
+ RangeCache<?>.Request o = (RangeCache<?>.Request)_o;
+ return fOffset == o.fOffset && fCount == o.fCount;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return (int)fOffset^fCount;
+ }
+
+ @Override
+ public String toString() {
+ return "" + fOffset + "(" + fCount + ") -> " + (fOffset + fCount); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ }
+
+ /**
+ * This transaction class implements the main logic of the range cache.
+ * It examines the current requests held by the cache and and creates
+ * requests ones as needed. Once the requests are all valid it returns
+ * the completed data to the client.
+ */
+ private class RangeTransaction extends Transaction<List<V>> {
+
+ long fOffset;
+ int fCount;
+
+ RangeTransaction(long offset, int count) {
+ fOffset = offset;
+ fCount = count;
+ }
+ @Override
+ protected List<V> process() throws InvalidCacheException, ExecutionException {
+ clearCanceledRequests();
+
+ List<Request> transactionRequests = getRequests(fOffset, fCount);
+ validate(transactionRequests);
+
+ return makeElementsListFromRequests(transactionRequests, fOffset, fCount);
+ }
+
+ private void clearCanceledRequests() {
+ for (Iterator<Request> itr = fRequests.iterator(); itr.hasNext();) {
+ Request request = itr.next();
+ if (!request.isValid() && request.isCanceled()) {
+ itr.remove();
+ }
+ }
+ }
+ }
+
+ /**
+ * Requests currently held by this cache. The requests should be for
+ * non-overlapping ranges of elements.
+ */
+
+ private SortedSet<Request> fRequests = new TreeSet<Request>();
+
+ /**
+ * Retrieves data from the data source.
+ *
+ * @param offset Offset in data range where the requested list of data should start.
+ * @param count Number of elements requests.
+ * @param rm Callback for the data.
+ */
+ protected abstract void retrieve(long offset, int count, DataCallback<List<V>> rm);
+
+ /**
+ * Returns a cache for the range of requested data. The cache object needs
+ * to be retrieved from range cache every time that the cache is a
+ *
+ * @param offset Offset in data range where the requested list of data should start.
+ * @param count Number of elements requests.
+ * @return Cache object for the requested data.
+ */
+ public ICache<List<V>> getRange(final long offset, final int count) {
+ assert Protocol.isDispatchThread();
+
+ List<Request> requests = getRequests(offset, count);
+
+ CallbackCache<List<V>> range = new CallbackCache<List<V>>() {
+ @Override
+ protected void retrieve(DataCallback<List<V>> rm) {
+ new RangeTransaction(offset, count).request(rm);
+ }
+ };
+
+ boolean valid = true;
+ List<Throwable> errors = null;
+ for (ICache<?> request : requests) {
+ if (request.isValid()) {
+ if (request.getError() != null) {
+ errors = errors != null ? errors : new ArrayList<Throwable>(requests.size());
+ errors.add(request.getError());
+ }
+ } else {
+ valid = false;
+ break;
+ }
+ }
+
+ if (valid) {
+ if (errors == null) {
+ range.set(makeElementsListFromRequests(requests, offset, count), null, true);
+ } else {
+ if (errors.size() == 1) {
+ range.set(null, errors.get(0), true);
+ } else {
+ AggregateError aggregateError = new AggregateError("Multiple Errors");
+ for (Throwable error : errors) aggregateError.add(error);
+ }
+ }
+ }
+
+ return range;
+ }
+
+ /**
+ * Sets the given list and status to the cache. Subsequent range requests
+ * that fall in its the range will return the given data. Requests outside
+ * of its range will trigger a call to {@link #retrieve(long, int, DataRequestMonitor)}.<br>
+ * The given data parameter can be <code>null</code> if the given status
+ * parameter contains an error. In this case all requests in the given
+ * range will return the error.
+ *
+ * @param offset Offset of the given data to set to cache.
+ * @param count Count of the given data to set to cache.
+ * @param data List of elements to set to cache. Can be <code>null</code>.
+ * @param error Error object to set to cache or <code>null</code> if none.
+ */
+ public void set(long offset, int count, List<V> data, Throwable error) {
+ for (Request request : fRequests) {
+ if (!request.isValid()) {
+ request.set(null, null, false);
+ }
+ }
+ fRequests.clear();
+ Request request = new Request(offset, count);
+ request.set(data, error, true);
+ fRequests.add(request);
+ }
+
+ /**
+ * Forces the cache into an invalid state. If there are any pending
+ * requests, their will continue and their results will be cached.
+ */
+ protected void reset() {
+ for (Iterator<Request> itr = fRequests.iterator(); itr.hasNext();) {
+ Request request = itr.next();
+ if (request.isValid()) {
+ request.reset();
+ itr.remove();
+ }
+ }
+ }
+
+ private List<Request> getRequests(long fOffset, int fCount) {
+ List<Request> requests = new ArrayList<Request>(1);
+
+ // Create a new request for the data to retrieve.
+ Request current = new Request(fOffset, fCount);
+
+ current = adjustRequestHead(current, requests, fOffset, fCount);
+ if (current != null) {
+ current = adjustRequestTail(current, requests, fOffset, fCount);
+ }
+ if (current != null) {
+ requests.add(current);
+ fRequests.add(current);
+ }
+ return requests;
+ }
+
+ // Adjust the beginning of the requested range of data. If there
+ // is already an overlapping range in front of the requested range,
+ // then use it.
+ private Request adjustRequestHead(Request request, List<Request> transactionRequests, long offset, int count) {
+ SortedSet<Request> headRequests = fRequests.headSet(request);
+ if (!headRequests.isEmpty()) {
+ Request headRequest = headRequests.last();
+ long headEndOffset = headRequest.fOffset + headRequest.fCount;
+ if (headEndOffset > offset) {
+ transactionRequests.add(headRequest);
+ request.fCount = (int)(request.fCount - (headEndOffset - offset));
+ request.fOffset = headEndOffset;
+ }
+ }
+ if (request.fCount > 0) {
+ return request;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Adjust the end of the requested range of data.
+ * @param current
+ * @param transactionRequests
+ * @return
+ */
+ private Request adjustRequestTail(Request current, List<Request> transactionRequests, long offset, int count) {
+ // Create a duplicate of the tailSet, in order to avoid a concurrent modification exception.
+ List<Request> tailSet = new ArrayList<Request>(fRequests.tailSet(current));
+
+ // Iterate through the matching requests and add them to the requests list.
+ for (Request tailRequest : tailSet) {
+ if (tailRequest.fOffset < current.fOffset + count) {
+ // found overlapping request add it to list
+ if (tailRequest.fOffset <= current.fOffset) {
+ // next request starts off at the beginning of current request
+ transactionRequests.add(tailRequest);
+ current.fOffset = tailRequest.fOffset + tailRequest.fCount;
+ current.fCount = ((int)(offset - current.fOffset)) + count ;
+ if (current.fCount <= 0) {
+ return null;
+ }
+ } else {
+ current.fCount = (int)(tailRequest.fOffset - current.fOffset);
+ transactionRequests.add(current);
+ fRequests.add(current);
+ current = null;
+ transactionRequests.add(tailRequest);
+ long tailEndOffset = tailRequest.fOffset + tailRequest.fCount;
+ long rangeEndOffset = offset + count;
+ if (tailEndOffset >= rangeEndOffset) {
+ return null;
+ } else {
+ current = new Request(tailEndOffset, (int)(rangeEndOffset - tailEndOffset));
+ }
+ }
+ } else {
+ break;
+ }
+ }
+ return current;
+ }
+
+ private List<V> makeElementsListFromRequests(List<Request> requests, long offset, int count) {
+ List<V> retVal = new ArrayList<V>(count);
+ long index = offset;
+ long end = offset + count;
+ int requestIdx = 0;
+ while (index < end ) {
+ Request request = requests.get(requestIdx);
+ if (index < request.fOffset + request.fCount) {
+ int dataIndex = (int)(index - request.fOffset);
+ if (dataIndex < request.getData().size()) {
+ retVal.add( request.getData().get(dataIndex) );
+ } else {
+ // If request didnt' return enough data, pad range with nulls.
+ retVal.add(null);
+ }
+ index ++;
+ } else {
+ requestIdx++;
+ }
+ }
+ return retVal;
+ }
+}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TokenCache.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TokenCache.java
index e19b734..1edb92a 100644
--- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TokenCache.java
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TokenCache.java
@@ -12,12 +12,13 @@ package org.eclipse.tcf.debug.test.util;
import java.util.concurrent.atomic.AtomicReference;
+import org.eclipse.tcf.debug.test.services.ResetMap.IResettable;
import org.eclipse.tcf.protocol.IToken;
/**
*
*/
-public abstract class TokenCache<V> extends AbstractCache<V> {
+public abstract class TokenCache<V> extends AbstractCache<V> implements IResettable {
private AtomicReference<IToken> fToken = new AtomicReference<IToken>();
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Transaction.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Transaction.java
index 7729ad1..26101a0 100644
--- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Transaction.java
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Transaction.java
@@ -22,7 +22,7 @@ import org.eclipse.tcf.protocol.Protocol;
/**
* @since 2.2
*/
-public abstract class Transaction<V> implements Future<V> {
+public abstract class Transaction<V> implements Future<V>, Runnable {
/**
* The exception we throw when the client transaction logic asks us to
@@ -60,7 +60,7 @@ public abstract class Transaction<V> implements Future<V> {
}
fRm = rm;
assert fRm != null;
- execute();
+ run();
}
protected void preProcess() {}
@@ -112,7 +112,9 @@ public abstract class Transaction<V> implements Future<V> {
* @throws CoreException Exception indicating that one of the caches is
* in error state and transaction cannot be processed.
*/
- abstract protected V process() throws InvalidCacheException, ExecutionException;
+ protected V process() throws InvalidCacheException, ExecutionException {
+ return null;
+ }
/**
* Can be called only while in process().
@@ -135,10 +137,16 @@ public abstract class Transaction<V> implements Future<V> {
/**
* Method which invokes the transaction logic and handles any exception that
* may result. If that logic encounters a stale/unset cache object, then we
- * simply do nothing. This method will be called again once the cache
- * objects tell us it has obtained an updated value form the source.
+ * simply do nothing. This method can be invoked by transaction logic when
+ * caches have become valid, thus unblocking transaction processing.
*/
- private void execute() {
+ public void run() {
+ // If execute is called after transaction completes (as a result of a
+ // cancelled request completing for example), ignore it.
+ if (fRm == null) {
+ return;
+ }
+
if (fRm.isCanceled()) {
fRm.done();
fRm = null;
@@ -156,8 +164,8 @@ public abstract class Transaction<V> implements Future<V> {
}
/**
- * Clients must call one of our validate methods prior to using (calling
- * getData()) on data cache object.
+ * Clients must call one of the validate methods prior to using (calling
+ * getData()) on data cache object.
*
* @param cache
* the object being validated
@@ -180,7 +188,7 @@ public abstract class Transaction<V> implements Future<V> {
cache.update(new Callback(fRm) {
@Override
protected void handleCompleted() {
- execute();
+ run();
}
});
throw INVALID_CACHE_EXCEPTION;
@@ -188,15 +196,15 @@ public abstract class Transaction<V> implements Future<V> {
}
/**
- * See {@link #validate(RequestCache)}. This variant simply validates
+ * See {@link #validate(ICache)}. This variant simply validates
* multiple cache objects.
*/
- public <T> void validate(ICache<?> ... caches) throws InvalidCacheException, ExecutionException {
+ public void validate(ICache<?> ... caches) throws InvalidCacheException, ExecutionException {
validate(Arrays.asList(caches));
}
/**
- * See {@link #validate(RequestCache)}. This variant simply validates
+ * See {@link #validate(ICache)}. This variant validates
* multiple cache objects.
*/
public void validate(@SuppressWarnings("rawtypes") Iterable caches) throws InvalidCacheException, ExecutionException {
@@ -220,7 +228,7 @@ public abstract class Transaction<V> implements Future<V> {
AggregateCallback countringRm = new AggregateCallback(fRm) {
@Override
protected void handleCompleted() {
- execute();
+ run();
}
};
int count = 0;
@@ -236,6 +244,81 @@ public abstract class Transaction<V> implements Future<V> {
}
}
+ /**
+ * See {@link #validate(ICache)}. This variant does not throw exceptions,
+ * instead it returns <code>false</code> if the cache is not valid. If the
+ * given cache is valid, and this method returns <code>true</code>, clients
+ * must still check if the cache contains an error before retrieving its
+ * data through {@link ICache#getData()}.
+ *
+ * @param cache the object being validated
+ * @return returns <code>false</code> if the cache is not yet valid and
+ * transaction processing should be interrupted.
+ */
+ public boolean validateUnchecked(ICache<?> cache) {
+ if (cache.isValid()) {
+ return true;
+ } else {
+ // Just sk the cache to update itself from its source, and schedule a
+ // re-attempt of the transaction logic to occur when the stale/unset
+ // cache has been updated
+ cache.update(new Callback(fRm) {
+ @Override
+ protected void handleCompleted() {
+ run();
+ }
+ });
+ return false;
+ }
+ }
+
+ /**
+ * See {@link #validate(ICache)}. This variant validates
+ * multiple cache objects.
+ */
+ public boolean validateUnchecked(ICache<?> ... caches) {
+ return validateUnchecked(Arrays.asList(caches));
+ }
+
+
+ /**
+ * See {@link #validate(ICache)}. This variant validates
+ * multiple cache objects.
+ */
+ public boolean validateUnchecked(@SuppressWarnings("rawtypes") Iterable caches) {
+ // Check if all caches are valid
+ boolean allValid = true;
+
+ for (Object cacheObj : caches) {
+ ICache<?> cache = (ICache<?>)cacheObj;
+ if (!cache.isValid()) {
+ allValid = false;
+ }
+ }
+ if (allValid) {
+ return true;
+ }
+
+ // Just schedule a re-attempt of the transaction logic, to occur
+ // when the stale/unset cache objects have been updated
+ AggregateCallback countringRm = new AggregateCallback(fRm) {
+ @Override
+ protected void handleCompleted() {
+ run();
+ }
+ };
+ int count = 0;
+ for (Object cacheObj : caches) {
+ ICache<?> cache = (ICache<?>)cacheObj;
+ if (!cache.isValid()) {
+ cache.update(countringRm);
+ count++;
+ }
+ }
+ countringRm.setDoneCount(count);
+ return false;
+ }
+
private synchronized Query<V> getQuery(boolean create) {
if (fQuery == null && create) {
fQuery = new Query<V>() {
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TransactionCache.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TransactionCache.java
index 8749453..f862c65 100644
--- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TransactionCache.java
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TransactionCache.java
@@ -87,7 +87,7 @@ public abstract class TransactionCache<V> extends Transaction<V> implements ICac
* Can be called while in {@link #process()}
* @param cache
*/
- public void addDependsOn(ICache cache) {
+ public void addDependsOn(ICache<?> cache) {
fDependsOn.add(cache);
}
@@ -99,7 +99,7 @@ public abstract class TransactionCache<V> extends Transaction<V> implements ICac
}
@Override
- public void validate(Iterable caches) throws InvalidCacheException, ExecutionException {
+ public void validate(@SuppressWarnings("rawtypes") Iterable caches) throws InvalidCacheException, ExecutionException {
for (Object cacheObj : caches) {
ICache<?> cache = (ICache<?>)cacheObj;
if (cache.isValid()) {
@@ -108,4 +108,23 @@ public abstract class TransactionCache<V> extends Transaction<V> implements ICac
}
super.validate(caches);
}
+
+ @Override
+ public boolean validateUnchecked(ICache<?> cache) {
+ if (cache.isValid()) {
+ addDependsOn(cache);
+ }
+ return super.validateUnchecked(cache);
+ }
+
+ @Override
+ public boolean validateUnchecked(@SuppressWarnings("rawtypes") Iterable caches) {
+ for (Object cacheObj : caches) {
+ ICache<?> cache = (ICache<?>)cacheObj;
+ if (cache.isValid()) {
+ addDependsOn(cache);
+ }
+ }
+ return super.validateUnchecked(caches);
+ }
}