Bug 431363 - [coordinator] unexpected orphaned failures may happen when
ending a coordination

Change-Id: I6261e34228d63c71df3f381b36bc7fb4c2ab5219
Signed-off-by: Thomas Watson <tjwatson@us.ibm.com>
diff --git a/bundles/org.eclipse.equinox.coordinator/src/org/eclipse/equinox/coordinator/CoordinationImpl.java b/bundles/org.eclipse.equinox.coordinator/src/org/eclipse/equinox/coordinator/CoordinationImpl.java
index 2ad246c..ceeef42 100644
--- a/bundles/org.eclipse.equinox.coordinator/src/org/eclipse/equinox/coordinator/CoordinationImpl.java
+++ b/bundles/org.eclipse.equinox.coordinator/src/org/eclipse/equinox/coordinator/CoordinationImpl.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2010, 2013 IBM Corporation and others.
+ * Copyright (c) 2010, 2014 IBM Corporation 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
@@ -33,7 +33,8 @@
 
 	private volatile Throwable failure;
 	private volatile boolean terminated;
-
+	private volatile boolean ending = false;
+	
 	private Date deadline;
 	private CoordinationImpl enclosingCoordination;
 	private Thread thread;
@@ -128,6 +129,23 @@
 		coordinator.checkPermission(CoordinationPermission.INITIATE, name);
 		// Terminating the coordination must be atomic.
 		synchronized (this) {
+			/*
+			 * Set the ending flag to avoid spurious failures for orphans
+			 * It appears the VM can aggressively puts objects on the queue if the last call is done in a finally
+			 * Coordination c = coordinator.begin("name", 0);
+			 * try {
+			 *   ...
+			 * } finally {
+			 *   c.end()
+			 * }
+			 * In some cases it appears that while in the finally call to c.end()
+			 * that c can become put on the queue for GC.
+			 * This makes it eligible for orphan processing which will cause 
+			 * issues below when calling methods that invoke
+			 * CoordinationWeakReference.processOrphanedCoordinations()
+			 * We set an ending flag so that we can detect this
+			 */
+			ending = true;
 			// If this coordination is associated with a thread, an additional
 			// check is required.
 			if (thread != null) {
@@ -335,6 +353,10 @@
 		return terminated;
 	}
 
+	public boolean isEnding() {
+		return ending;
+	}
+
 	public void join(final long timeInMillis) throws InterruptedException {
 		coordinator.checkPermission(CoordinationPermission.PARTICIPATE, name);
 		validateTimeout(timeInMillis);
diff --git a/bundles/org.eclipse.equinox.coordinator/src/org/eclipse/equinox/coordinator/CoordinationWeakReference.java b/bundles/org.eclipse.equinox.coordinator/src/org/eclipse/equinox/coordinator/CoordinationWeakReference.java
index 33d5ef4..7ffb66e 100644
--- a/bundles/org.eclipse.equinox.coordinator/src/org/eclipse/equinox/coordinator/CoordinationWeakReference.java
+++ b/bundles/org.eclipse.equinox.coordinator/src/org/eclipse/equinox/coordinator/CoordinationWeakReference.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2011, 2013 IBM Corporation and others.
+ * Copyright (c) 2011, 2014 IBM Corporation 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
@@ -25,25 +25,27 @@
 		CoordinationWeakReference r;
 		while ((r = (CoordinationWeakReference)referenceQueue.poll()) != null) {
 			CoordinationImpl c = r.getCoordination();
-			try {
-				c.fail(Coordination.ORPHANED);
-			}
-			catch (Exception e) {
-				c.getLogService().log(LogService.LOG_WARNING, NLS.bind(Messages.OrphanedCoordinationError, c.getName(), c.getId()), e);
-			}
-			finally {
+			if (!c.isEnding()) {
 				try {
-					c.end();
-				}
-				catch (CoordinationException e) {
-					// This is expected since we already failed the coordination...
-					if (!Coordination.ORPHANED.equals(e.getCause()))
-						// ...but only if the cause is ORPHANED.
-						c.getLogService().log(LogService.LOG_DEBUG, NLS.bind(Messages.OrphanedCoordinationError, c.getName(), c.getId()), e);
+					c.fail(Coordination.ORPHANED);
 				}
 				catch (Exception e) {
 					c.getLogService().log(LogService.LOG_WARNING, NLS.bind(Messages.OrphanedCoordinationError, c.getName(), c.getId()), e);
 				}
+				finally {
+					try {
+						c.end();
+					}
+					catch (CoordinationException e) {
+						// This is expected since we already failed the coordination...
+						if (!Coordination.ORPHANED.equals(e.getCause()))
+							// ...but only if the cause is ORPHANED.
+							c.getLogService().log(LogService.LOG_DEBUG, NLS.bind(Messages.OrphanedCoordinationError, c.getName(), c.getId()), e);
+					}
+					catch (Exception e) {
+						c.getLogService().log(LogService.LOG_WARNING, NLS.bind(Messages.OrphanedCoordinationError, c.getName(), c.getId()), e);
+					}
+				}
 			}
 		}
 	}