mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-06-07 17:56:01 +02:00
Bug 310345 - Updated range cache to immediately return a valid range if range data is available.
This commit is contained in:
parent
cb844bdaed
commit
24af3b858a
2 changed files with 185 additions and 101 deletions
|
@ -16,8 +16,10 @@ import java.util.List;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import org.eclipse.cdt.dsf.internal.DsfPlugin;
|
||||||
import org.eclipse.core.runtime.CoreException;
|
import org.eclipse.core.runtime.CoreException;
|
||||||
import org.eclipse.core.runtime.IStatus;
|
import org.eclipse.core.runtime.IStatus;
|
||||||
|
import org.eclipse.core.runtime.MultiStatus;
|
||||||
import org.eclipse.core.runtime.Status;
|
import org.eclipse.core.runtime.Status;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -93,108 +95,13 @@ abstract public class RangeCache<V> {
|
||||||
protected List<V> process() throws InvalidCacheException, CoreException {
|
protected List<V> process() throws InvalidCacheException, CoreException {
|
||||||
clearCanceledRequests();
|
clearCanceledRequests();
|
||||||
|
|
||||||
List<ICache<?>> transactionRequests = new ArrayList<ICache<?>>(1);
|
List<ICache<?>> transactionRequests = getRequests(fOffset, fCount);
|
||||||
|
|
||||||
// Create a new request for the data to retrieve.
|
|
||||||
Request current = new Request(fOffset, fCount);
|
|
||||||
|
|
||||||
current = adjustRequestHead(current, transactionRequests);
|
|
||||||
if (current != null) {
|
|
||||||
current = adjustRequestTail(current, transactionRequests);
|
|
||||||
}
|
|
||||||
if (current != null) {
|
|
||||||
transactionRequests.add(current);
|
|
||||||
fRequests.add(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
validate(transactionRequests);
|
validate(transactionRequests);
|
||||||
|
|
||||||
return makeElementsListFromRequests(transactionRequests);
|
return makeElementsListFromRequests(transactionRequests, fOffset, fCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 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<ICache<?>> transactionRequests) {
|
|
||||||
SortedSet<Request> headRequests = fRequests.headSet(request);
|
|
||||||
if (!headRequests.isEmpty()) {
|
|
||||||
Request headRequest = headRequests.last();
|
|
||||||
long headEndOffset = headRequest.fOffset + headRequest.fCount;
|
|
||||||
if (headEndOffset > fOffset) {
|
|
||||||
transactionRequests.add(headRequest);
|
|
||||||
request.fCount = (int)(request.fCount - (headEndOffset - fOffset));
|
|
||||||
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<ICache<?>> transactionRequests) {
|
|
||||||
// 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 + fCount) {
|
|
||||||
// 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)(fOffset - current.fOffset)) + fCount ;
|
|
||||||
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 = fOffset + fCount;
|
|
||||||
if (tailEndOffset >= rangeEndOffset) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
current = new Request(tailEndOffset, (int)(rangeEndOffset - tailEndOffset));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<V> makeElementsListFromRequests(List<ICache<?>> requests) {
|
|
||||||
List<V> retVal = new ArrayList<V>(fCount);
|
|
||||||
long index = fOffset;
|
|
||||||
long end = fOffset + fCount;
|
|
||||||
int requestIdx = 0;
|
|
||||||
while (index < end ) {
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
Request request = (Request)requests.get(requestIdx);
|
|
||||||
if (index < request.fOffset + request.fCount) {
|
|
||||||
retVal.add( request.getData().get((int)(index - request.fOffset)) );
|
|
||||||
index ++;
|
|
||||||
} else {
|
|
||||||
requestIdx++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void clearCanceledRequests() {
|
private void clearCanceledRequests() {
|
||||||
for (Iterator<Request> itr = fRequests.iterator(); itr.hasNext();) {
|
for (Iterator<Request> itr = fRequests.iterator(); itr.hasNext();) {
|
||||||
Request request = itr.next();
|
Request request = itr.next();
|
||||||
|
@ -237,12 +144,41 @@ abstract public class RangeCache<V> {
|
||||||
public ICache<List<V>> getRange(final long offset, final int count) {
|
public ICache<List<V>> getRange(final long offset, final int count) {
|
||||||
assert fExecutor.getDsfExecutor().isInExecutorThread();
|
assert fExecutor.getDsfExecutor().isInExecutorThread();
|
||||||
|
|
||||||
return new RequestCache<List<V>>(fExecutor) {
|
List<ICache<?>> requests = getRequests(offset, count);
|
||||||
|
|
||||||
|
RequestCache<List<V>> range = new RequestCache<List<V>>(fExecutor) {
|
||||||
@Override
|
@Override
|
||||||
protected void retrieve(DataRequestMonitor<List<V>> rm) {
|
protected void retrieve(DataRequestMonitor<List<V>> rm) {
|
||||||
new RangeTransaction(offset, count).request(rm);
|
new RangeTransaction(offset, count).request(rm);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
boolean valid = true;
|
||||||
|
MultiStatus status = new MultiStatus(DsfPlugin.PLUGIN_ID, 0, "", null); //$NON-NLS-1$
|
||||||
|
for (ICache<?> request : requests) {
|
||||||
|
if (request.isValid()) {
|
||||||
|
if (!request.getStatus().isOK()) {
|
||||||
|
status.add(request.getStatus());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
valid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valid) {
|
||||||
|
if (status.isOK()) {
|
||||||
|
range.set(makeElementsListFromRequests(requests, offset, count), Status.OK_STATUS);
|
||||||
|
} else {
|
||||||
|
if (status.getChildren().length == 1) {
|
||||||
|
range.set(null, status.getChildren()[0]);
|
||||||
|
} else {
|
||||||
|
range.set(null, status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return range;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -283,4 +219,103 @@ abstract public class RangeCache<V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<ICache<?>> getRequests(long fOffset, int fCount) {
|
||||||
|
List<ICache<?>> requests = new ArrayList<ICache<?>>(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<ICache<?>> 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<ICache<?>> 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<ICache<?>> 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 ) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Request request = (Request)requests.get(requestIdx);
|
||||||
|
if (index < request.fOffset + request.fCount) {
|
||||||
|
retVal.add( request.getData().get((int)(index - request.fOffset)) );
|
||||||
|
index ++;
|
||||||
|
} else {
|
||||||
|
requestIdx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,6 +162,11 @@ public class RangeCacheTests {
|
||||||
Assert.assertTrue(cache.getStatus().isOK());
|
Assert.assertTrue(cache.getStatus().isOK());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void assertCacheValidWithError(ICache<List<Integer>> cache) {
|
||||||
|
Assert.assertTrue(cache.isValid());
|
||||||
|
Assert.assertFalse(cache.getStatus().isOK());
|
||||||
|
}
|
||||||
|
|
||||||
private void assertCacheWaiting(ICache<List<Integer>> cache) {
|
private void assertCacheWaiting(ICache<List<Integer>> cache) {
|
||||||
Assert.assertFalse(cache.isValid());
|
Assert.assertFalse(cache.isValid());
|
||||||
try {
|
try {
|
||||||
|
@ -388,16 +393,29 @@ public class RangeCacheTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setOneRangeTest() throws InterruptedException, ExecutionException {
|
public void setRangeErrorTest() throws InterruptedException, ExecutionException {
|
||||||
|
|
||||||
|
// Retrieve a range of data.
|
||||||
getRange(0, 100, new long[] { 0 }, new int[] { 100 });
|
getRange(0, 100, new long[] { 0 }, new int[] { 100 });
|
||||||
|
|
||||||
|
// Force an error status into the range cache.
|
||||||
fExecutor.submit(new DsfRunnable() {
|
fExecutor.submit(new DsfRunnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
fTestCache.set(0, 100, null, new Status(IStatus.ERROR, DsfTestPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "Cache invalid", null));
|
fTestCache.set(0, 100, null, new Status(IStatus.ERROR, DsfTestPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "Cache invalid", null));
|
||||||
};
|
};
|
||||||
}).get();
|
}).get();
|
||||||
|
|
||||||
|
// Retrieve a range cache and check that it immediately contains the error status in it.
|
||||||
|
fRangeCache = null;
|
||||||
|
fExecutor.submit(new DsfRunnable() {
|
||||||
|
public void run() {
|
||||||
|
fRangeCache = fTestCache.getRange(0, 100);
|
||||||
|
}
|
||||||
|
}).get();
|
||||||
|
|
||||||
|
assertCacheValidWithError(fRangeCache);
|
||||||
|
|
||||||
// Request data from cache
|
// Request an update from the range and check for exception.
|
||||||
TestQuery q = new TestQuery(10, 100);
|
TestQuery q = new TestQuery(10, 100);
|
||||||
|
|
||||||
fRangeCache = null;
|
fRangeCache = null;
|
||||||
|
@ -410,6 +428,37 @@ public class RangeCacheTests {
|
||||||
Assert.fail("Expected an ExecutionException");
|
Assert.fail("Expected an ExecutionException");
|
||||||
} catch (ExecutionException e) {}
|
} catch (ExecutionException e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getOneRangeUsingDifferentRangeInstanceTest() throws InterruptedException, ExecutionException {
|
||||||
|
// Request data from cache
|
||||||
|
TestQuery q = new TestQuery(0, 100);
|
||||||
|
|
||||||
|
fRangeCache = null;
|
||||||
|
fRetrieveInfos.clear();
|
||||||
|
|
||||||
|
fExecutor.execute(q);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
fExecutor.submit(new DsfRunnable() {
|
||||||
|
public void run() {
|
||||||
|
fRangeCache = fTestCache.getRange(0, 100);
|
||||||
|
}
|
||||||
|
}).get();
|
||||||
|
|
||||||
|
// Check state while waiting for data
|
||||||
|
assertCacheValidWithData(fRangeCache, 0, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue