Bug 416164 - Correctly handle spurious wake ups.

Replace forStop may with a single AtomicReference that we can hand
out to waiting threads and notify when the framework stops

Change-Id: I1f6da6b9c896ba1bc2397bccfd9b34899a497bce
Signed-off-by: Thomas Watson <tjwatson@us.ibm.com>
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/SystemModule.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/SystemModule.java
index df498f6..0f4b65e 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/SystemModule.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/SystemModule.java
@@ -10,8 +10,10 @@
  *******************************************************************************/
 package org.eclipse.osgi.container;
 
-import java.util.*;
+import java.util.Arrays;
+import java.util.EnumSet;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
 import org.eclipse.osgi.container.ModuleContainer.ContainerStartLevel;
 import org.eclipse.osgi.container.ModuleContainerAdaptor.ContainerEvent;
 import org.eclipse.osgi.container.ModuleContainerAdaptor.ModuleEvent;
@@ -22,13 +24,13 @@
 import org.osgi.service.resolver.ResolutionException;
 
 /**
- * A special kind of module that represents the system module for the container.  Additinal
+ * A special kind of module that represents the system module for the container.  Additional
  * methods are available on the system module for operations that effect the whole container.
  * For example, initializing the container, restarting and waiting for the container to stop.
  * @since 3.10
  */
 public abstract class SystemModule extends Module {
-	private final Map<Thread, ContainerEvent> forStop = new HashMap<Thread, ContainerEvent>(2);
+	private volatile AtomicReference<ContainerEvent> forStop = new AtomicReference<ContainerEvent>();
 
 	public SystemModule(ModuleContainer container) {
 		super(new Long(0), Constants.SYSTEM_BUNDLE_LOCATION, container, EnumSet.of(Settings.AUTO_START, Settings.USE_ACTIVATION_POLICY), Integer.valueOf(0));
@@ -82,6 +84,11 @@
 			}
 
 			setState(State.STARTING);
+			AtomicReference<ContainerEvent> existingForStop = forStop;
+			if (existingForStop.get() != null) {
+				// There was a previous launch, reset the reference forStop
+				forStop = new AtomicReference<ContainerEvent>();
+			}
 			publishEvent(ModuleEvent.STARTING);
 			try {
 				initWorker();
@@ -116,49 +123,53 @@
 	public ContainerEvent waitForStop(long timeout) throws InterruptedException {
 		final boolean waitForever = timeout == 0;
 		final long start = System.currentTimeMillis();
-		final Thread current = Thread.currentThread();
 		long timeLeft = timeout;
-		ContainerEvent event = null;
-		boolean stateLocked;
-		if (timeout == 0) {
-			stateChangeLock.lockInterruptibly();
-			stateLocked = true;
-		} else {
-			stateLocked = stateChangeLock.tryLock(timeLeft, TimeUnit.MILLISECONDS);
+		AtomicReference<ContainerEvent> stopEvent = null;
+		State currentState = null;
+		boolean stateLocked = false;
+		try {
+			if (timeout == 0) {
+				stateChangeLock.lockInterruptibly();
+				stateLocked = true;
+			} else {
+				stateLocked = stateChangeLock.tryLock(timeLeft, TimeUnit.MILLISECONDS);
+			}
+			if (stateLocked) {
+				stopEvent = forStop;
+				currentState = getState();
+			}
+		} finally {
+			if (stateLocked) {
+				stateChangeLock.unlock();
+			}
 		}
-		if (stateLocked) {
-			synchronized (forStop) {
-				try {
-					if (!Module.ACTIVE_SET.contains(getState())) {
-						return ContainerEvent.STOPPED;
-					}
-					event = forStop.remove(current);
-					if (event == null) {
-						forStop.put(current, null);
-					}
-				} finally {
-					stateChangeLock.unlock();
+
+		if (stopEvent == null || currentState == null) {
+			// Could not lock system module stateChangeLock; timeout
+			return ContainerEvent.STOPPED_TIMEOUT;
+		}
+		if (!ACTIVE_SET.contains(currentState)) {
+			// check if a past event is waiting for us
+			ContainerEvent result = stopEvent.get();
+			if (result != null) {
+				return result;
+			}
+			// framework must not have even been started yet
+			return ContainerEvent.STOPPED;
+		}
+		synchronized (stopEvent) {
+			do {
+				ContainerEvent result = stopEvent.get();
+				if (result != null) {
+					return result;
 				}
 				timeLeft = waitForever ? 0 : start + timeout - System.currentTimeMillis();
-				while (event == null && (waitForever || timeLeft > 0)) {
-					forStop.wait(timeLeft);
-					event = forStop.remove(current);
-					if (!waitForever) {
-						timeLeft = start + timeout - System.currentTimeMillis();
-					}
+				if (waitForever || timeLeft > 0) {
+					stopEvent.wait(timeLeft);
+				} else {
+					return ContainerEvent.STOPPED_TIMEOUT;
 				}
-			}
-		}
-		return event == null ? ContainerEvent.STOPPED_TIMEOUT : event;
-	}
-
-	private void notifyWaitForStop(ContainerEvent event) {
-		synchronized (forStop) {
-			Collection<Thread> waiting = new ArrayList<Thread>(forStop.keySet());
-			for (Thread t : waiting) {
-				forStop.put(t, event);
-			}
-			forStop.notifyAll();
+			} while (true);
 		}
 	}
 
@@ -203,15 +214,21 @@
 					getRevisions().getContainer().adaptor.publishContainerEvent(containerEvent, this, null);
 					getRevisions().getContainer().close();
 				} finally {
+					AtomicReference<ContainerEvent> eventReference = forStop;
+					eventReference.compareAndSet(null, containerEvent);
 					stateChangeLock.unlock();
+					synchronized (eventReference) {
+						eventReference.notifyAll();
+					}
 				}
 			} else {
 				throw new BundleException(Msg.SystemModule_LockError);
 			}
 		} catch (InterruptedException e) {
 			getRevisions().getContainer().adaptor.publishContainerEvent(ContainerEvent.ERROR, this, e);
+			throw new BundleException(Msg.Module_LockError + toString(), BundleException.STATECHANGE_ERROR, e);
 		}
-		notifyWaitForStop(containerEvent);
+
 	}
 
 	/**