diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/RequestCache.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/RequestCache.java
new file mode 100644
index 00000000000..634716b2e34
--- /dev/null
+++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/RequestCache.java
@@ -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.
+ *
+ * This cache requires an executor to use. The executor is used to synchronize
+ * access to the cache state and data.
+ *
+ * @since 2.1
+ */
+@ConfinedToDsfExecutor("fExecutor")
+public abstract class RequestCache extends AbstractCache {
+
+ protected DataRequestMonitor 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(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 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);
+ }
+}
diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/Transaction.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/Transaction.java
new file mode 100644
index 00000000000..8de8d5ae07f
--- /dev/null
+++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/Transaction.java
@@ -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 {
+
+ /**
+ * 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 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 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;
+ }
+ }
+
+}