1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-08-03 22:35: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:
John Cortell 2010-10-18 16:11:20 +00:00
parent 42197fda9c
commit 400a4127fd
2 changed files with 306 additions and 0 deletions

View file

@ -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);
}
}

View file

@ -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;
}
}
}