mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-08-04 06:45:43 +02:00
Bug 310345: [concurrent] Asynchronous Cache Programming Model (ACPM) utilities for DSF (committing my commented version of Pawel's file. Pawel will be adding the others).
This commit is contained in:
parent
42197fda9c
commit
400a4127fd
2 changed files with 306 additions and 0 deletions
|
@ -0,0 +1,123 @@
|
||||||
|
package org.eclipse.cdt.dsf.concurrent;
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2008 Wind River Systems, Inc. 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
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
import org.eclipse.core.runtime.IStatus;
|
||||||
|
import org.eclipse.core.runtime.Status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A general purpose cache, which caches the result of a single request.
|
||||||
|
* Sub classes need to implement {@link #retrieve(DataRequestMonitor)} to fetch
|
||||||
|
* data from the data source. Clients are responsible for calling
|
||||||
|
* {@link #disable()} and {@link #reset()} to manage the state of the cache in
|
||||||
|
* response to events from the data source.
|
||||||
|
* <p>
|
||||||
|
* This cache requires an executor to use. The executor is used to synchronize
|
||||||
|
* access to the cache state and data.
|
||||||
|
* </p>
|
||||||
|
* @since 2.1
|
||||||
|
*/
|
||||||
|
@ConfinedToDsfExecutor("fExecutor")
|
||||||
|
public abstract class RequestCache<V> extends AbstractCache<V> {
|
||||||
|
|
||||||
|
protected DataRequestMonitor<V> fRm;
|
||||||
|
|
||||||
|
|
||||||
|
public RequestCache(ImmediateInDsfExecutor executor) {
|
||||||
|
super(executor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sub-classes should override this method to retrieve the cache data
|
||||||
|
* from its source.
|
||||||
|
*
|
||||||
|
* @param rm Request monitor for completion of data retrieval.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void retrieve() {
|
||||||
|
// Make sure to cancel the previous rm. This may lead to the rm being
|
||||||
|
// canceled twice, but that's not harmful.
|
||||||
|
if (fRm != null) {
|
||||||
|
fRm.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
fRm = new DataRequestMonitor<V>(getExecutor(), null) {
|
||||||
|
|
||||||
|
private IStatus fRawStatus = Status.OK_STATUS;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void handleCompleted() {
|
||||||
|
if (this == fRm) {
|
||||||
|
fRm = null;
|
||||||
|
IStatus status;
|
||||||
|
synchronized (this) {
|
||||||
|
status = fRawStatus;
|
||||||
|
}
|
||||||
|
set(getData(), status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void setStatus(IStatus status) {
|
||||||
|
fRawStatus = status;
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCanceled() {
|
||||||
|
return super.isCanceled() || RequestCache.this.isCanceled();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
retrieve(fRm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sub-classes should override this method to retrieve the cache data
|
||||||
|
* from its source.
|
||||||
|
*
|
||||||
|
* @param rm Request monitor for completion of data retrieval.
|
||||||
|
*/
|
||||||
|
protected abstract void retrieve(DataRequestMonitor<V> rm);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected synchronized void canceled() {
|
||||||
|
if (fRm != null) {
|
||||||
|
fRm.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset(V data, IStatus status) {
|
||||||
|
if (fRm != null) {
|
||||||
|
fRm.cancel();
|
||||||
|
fRm = null;
|
||||||
|
}
|
||||||
|
super.reset(data, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disable() {
|
||||||
|
if (fRm != null) {
|
||||||
|
fRm.cancel();
|
||||||
|
fRm = null;
|
||||||
|
}
|
||||||
|
super.disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(V data, IStatus status) {
|
||||||
|
if (fRm != null) {
|
||||||
|
fRm.cancel();
|
||||||
|
fRm = null;
|
||||||
|
}
|
||||||
|
super.set(data, status);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,183 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2008 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.cdt.dsf.concurrent;
|
||||||
|
|
||||||
|
import org.eclipse.core.runtime.CoreException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 2.2
|
||||||
|
*/
|
||||||
|
public abstract class Transaction<V> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The exception we throw when the client transaction logic asks us to
|
||||||
|
* validate a cache object that is stale (or has never obtained a value from
|
||||||
|
* the source)
|
||||||
|
*/
|
||||||
|
private static final InvalidCacheException INVALID_CACHE_EXCEPTION = new InvalidCacheException();
|
||||||
|
|
||||||
|
/** The request object we've been given to set the transaction results in */
|
||||||
|
private DataRequestMonitor<V> fRm;
|
||||||
|
|
||||||
|
public static class InvalidCacheException extends Exception {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kicks off the transaction. We'll either complete the request monitor
|
||||||
|
* immediately if all the data points the transaction needs are cached and
|
||||||
|
* valid, or we'll end up asynchronously completing the monitor if and when
|
||||||
|
* either (a) all the data points are available and up-to-date, or (b)
|
||||||
|
* obtaining them from the source encountered an error. Note that there is
|
||||||
|
* potential in (b) for us to never complete the monitor. If one or more
|
||||||
|
* data points are perpetually becoming stale, then we'll indefinitely wait
|
||||||
|
* for them to stabilize. The caller should cancel its request monitor in
|
||||||
|
* order to get us to stop waiting.
|
||||||
|
*
|
||||||
|
* @param rm Request completion monitor.
|
||||||
|
*/
|
||||||
|
public void request(DataRequestMonitor<V> rm) {
|
||||||
|
if (fRm != null) {
|
||||||
|
assert fRm.isCanceled();
|
||||||
|
fRm.done();
|
||||||
|
}
|
||||||
|
fRm = rm;
|
||||||
|
execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The transaction logic--code that tries to synchronously make use of,
|
||||||
|
* usually, multiple data points that are normally obtained asynchronously.
|
||||||
|
* Each data point is represented by a cache object. The transaction logic
|
||||||
|
* must check the validity of each cache object just prior to using it
|
||||||
|
* (calling its getData()). It should do that check by calling one of our
|
||||||
|
* validate() methods. Those methods will throw InvalidCacheException if the
|
||||||
|
* cached data is invalid (stale, e.g.,) or CoreException if an error was
|
||||||
|
* encountered the last time it got data form the source. The exception will
|
||||||
|
* abort the transaction, but in the case of InvalidCacheException, we
|
||||||
|
* schedule an asynchronous call that will re-invoke the transaction
|
||||||
|
* logic once the cache object has been updated from the source.
|
||||||
|
*
|
||||||
|
* @return the cached data if it's valid, otherwise an exception is thrown
|
||||||
|
* @throws InvalidCacheException
|
||||||
|
* @throws CoreException
|
||||||
|
*/
|
||||||
|
abstract protected V process() throws InvalidCacheException, CoreException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
private void execute() {
|
||||||
|
if (fRm.isCanceled()) {
|
||||||
|
fRm.done();
|
||||||
|
fRm = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Execute the transaction logic
|
||||||
|
V data = process();
|
||||||
|
|
||||||
|
// No exception means all cache objects used by the transaction
|
||||||
|
// were valid and up to date. Complete the request
|
||||||
|
fRm.setData(data);
|
||||||
|
fRm.done();
|
||||||
|
fRm = null;
|
||||||
|
}
|
||||||
|
catch (CoreException e) {
|
||||||
|
// At least one of the cache objects encountered a failure obtaining
|
||||||
|
// the data from the source. Complete the request.
|
||||||
|
fRm.setStatus(e.getStatus());
|
||||||
|
fRm.done();
|
||||||
|
fRm = null;
|
||||||
|
}
|
||||||
|
catch (InvalidCacheException e) {
|
||||||
|
// At least one of the cache objects was stale/unset. Keep the
|
||||||
|
// request monitor in the incomplete state, thus leaving our client
|
||||||
|
// "waiting" (asynchronously). We'll get called again once the cache
|
||||||
|
// objects are updated, thus re-starting the whole transaction
|
||||||
|
// attempt.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clients must call one of our validate methods prior to using (calling
|
||||||
|
* getData()) on data cache object.
|
||||||
|
*
|
||||||
|
* @param cache
|
||||||
|
* the object being validated
|
||||||
|
* @throws InvalidCacheException
|
||||||
|
* if the data is stale/unset
|
||||||
|
* @throws CoreException
|
||||||
|
* if an error was encountered getting the data from the source
|
||||||
|
*/
|
||||||
|
protected void validate(RequestCache<?> cache) throws InvalidCacheException, CoreException {
|
||||||
|
if (cache.isValid()) {
|
||||||
|
if (!cache.getStatus().isOK()) {
|
||||||
|
throw new CoreException(cache.getStatus());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Throw the invalid cache exception, but first schedule a
|
||||||
|
// re-attempt of the transaction logic, to occur when the
|
||||||
|
// stale/unset cache object has been updated
|
||||||
|
cache.wait(new RequestMonitor(ImmediateExecutor.getInstance(), fRm) {
|
||||||
|
@Override
|
||||||
|
protected void handleCompleted() {
|
||||||
|
execute();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
throw INVALID_CACHE_EXCEPTION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See {@link #validate(RequestCache)}. This variant simply validates
|
||||||
|
* multiple cache objects.
|
||||||
|
*/
|
||||||
|
protected void validate(RequestCache<?> ... caches) throws InvalidCacheException, CoreException {
|
||||||
|
// Check if any of the caches have errors:
|
||||||
|
boolean allValid = true;
|
||||||
|
|
||||||
|
for (RequestCache<?> cache : caches) {
|
||||||
|
if (cache.isValid()) {
|
||||||
|
if (!cache.getStatus().isOK()) {
|
||||||
|
throw new CoreException(cache.getStatus());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
allValid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!allValid) {
|
||||||
|
// Throw the invalid cache exception, but first schedule a
|
||||||
|
// re-attempt of the transaction logic, to occur when the
|
||||||
|
// stale/unset cache objects have been updated
|
||||||
|
CountingRequestMonitor countringRm = new CountingRequestMonitor(ImmediateExecutor.getInstance(), fRm) {
|
||||||
|
@Override
|
||||||
|
protected void handleCompleted() {
|
||||||
|
execute();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
int count = 0;
|
||||||
|
for (RequestCache<?> cache : caches) {
|
||||||
|
if (!cache.isValid()) {
|
||||||
|
cache.wait(countringRm);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
countringRm.setDoneCount(count);
|
||||||
|
throw INVALID_CACHE_EXCEPTION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue