[361302] Potential deadlock with shared lock JobSafeStructuredDocument
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/model/AbstractStructuredModel.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/model/AbstractStructuredModel.java
index 06e54162..c07c4f0 100644
--- a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/model/AbstractStructuredModel.java
+++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/model/AbstractStructuredModel.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2001, 2008 IBM Corporation and others.
+ * Copyright (c) 2001, 2011 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,7 +25,6 @@
 import org.eclipse.core.runtime.Platform;
 import org.eclipse.core.runtime.jobs.ILock;
 import org.eclipse.wst.sse.core.StructuredModelManager;
-import org.eclipse.wst.sse.core.internal.ILockable;
 import org.eclipse.wst.sse.core.internal.Logger;
 import org.eclipse.wst.sse.core.internal.SSECoreMessages;
 import org.eclipse.wst.sse.core.internal.encoding.EncodingRule;
@@ -305,32 +304,12 @@
 
 	/**
 	 * This lock to lock the small bits of data and operations in the models
-	 * themselfes. this lock is "shared" with document, so, eventually,
-	 * changes can be made safefly from either side.
+	 * themselves. this lock is "shared" with document, so, eventually,
+	 * changes can be made safely from either side.
+	 * 
+	 * @deprecated
 	 */
 	protected final void beginLock() {
-
-		// if we get a different lock object
-		// than we had before, besure to release
-		// old one first before losing it.
-		// ISSUE: this smells like an error condition,
-		// when would this happen? better to check in set document?
-		ILock documentLock = getLockObjectFromDocument();
-
-		if (fLockObject != null && fLockObject != documentLock) {
-			fLockObject.release();
-			if (Logger.DEBUG_MODELSTATE) {
-				Logger.log(Logger.INFO, "Model lock released early" + fLockObject + " apparently document switched?"); //$NON-NLS-1$ //$NON-NLS-2$
-			}
-
-		}
-		fLockObject = documentLock;
-		if (fLockObject != null) {
-			fLockObject.acquire();
-			if (Logger.DEBUG_MODELSTATE) {
-				Logger.log(Logger.INFO, "Model lock acquired: " + fLockObject); //$NON-NLS-1$
-			}
-		}
 	}
 
 	public void beginRecording(Object requester) {
@@ -474,15 +453,9 @@
 	 * call it to end the lock after updates have been made, but before
 	 * notifications are sent
 	 * 
+	 * @deprecated
 	 */
 	protected final void endLock() {
-		if (fLockObject != null) {
-			fLockObject.release();
-			if (Logger.DEBUG_MODELSTATE) {
-				Logger.log(Logger.INFO, "Model lock released: " + fLockObject); //$NON-NLS-1$
-			}
-
-		}
 	}
 
 	public void endRecording(Object requester) {
@@ -699,26 +672,6 @@
 	public abstract IndexedRegion getIndexedRegion(int offset);
 
 	/**
-	 * @return
-	 */
-	private ILock getLockObjectFromDocument() {
-
-		// we always "get afresh" the lock object from our document,
-		// just in case the instance of the document changes.
-		ILock result = null;
-		IStructuredDocument doc = fStructuredDocument;
-		if (doc != null) {
-			if (doc instanceof ILockable) {
-				// remember, more than one client can get the
-				// lock object, its during the aquire that the
-				// lock on the thread is obtained.
-				result = ((ILockable) doc).getLockObject();
-			}
-		}
-		return result;
-	}
-
-	/**
 	 * Gets the contentTypeDescription.
 	 * 
 	 * @return Returns a ContentTypeDescription
@@ -822,11 +775,19 @@
 		fId = id;
 	}
 
+	private final Object STATE_LOCK = new Object();
+
 	final protected void internalAboutToBeChanged() {
+		int state = 0;
+		// we always increment counter, for every request (so *must* receive
+		// corresponding number of 'changedModel' requests)
+		synchronized (STATE_LOCK) {
+			state = fModelStateChanging++;
+		}
 
 		// notice we only fire this event if we are not
 		// already in a model state changing sequence
-		if (fModelStateChanging == 0) {
+		if (state == 0) {
 
 			if (Logger.DEBUG_MODELSTATE) {
 				Logger.log(Logger.INFO, "IModelStateListener event for " + getId() + " : modelAboutToBeChanged"); //$NON-NLS-1$ //$NON-NLS-2$
@@ -838,16 +799,9 @@
 			catch (Exception e) {
 				Logger.logException("Exception while notifying model state listers of about to change", e); //$NON-NLS-1$
 			}
-			finally {
-				// begin lock after listeners notified, otherwise
-				// deadlock could occur if they call us back.
-				beginLock();
-			}
 
 		}
-		// we always increment counter, for every request (so *must* receive
-		// corresponding number of 'changedModel' requests)
-		fModelStateChanging++;
+		
 	}
 
 	/**
@@ -856,19 +810,18 @@
 	 * since it keeps track of counts.
 	 */
 	final protected void internalModelChanged() {
-
+		int state = 0;
 		// always decrement
-		fModelStateChanging--;
-
+		synchronized (STATE_LOCK) {
+			state = --fModelStateChanging;
+		}
 
 		// Check integrity
 		// to be less than zero is a programming error,
 		// but we'll reset to zero
 		// and try to continue
-		if (fModelStateChanging < 0) {
-			fModelStateChanging = 0;
-			// should not be locked, but just in case
-			endLock();
+		if (state < 0) {
+			state = 0;
 			throw new IllegalStateException("Program Error: modelStateChanging was less than zero"); //$NON-NLS-1$
 		}
 
@@ -876,14 +829,11 @@
 		// We only fire this event if all pending requests are done.
 		// That is, if we've received the same number of modelChanged as
 		// we have aboutToChangeModel.
-		if (fModelStateChanging == 0) {
+		if (state == 0) {
 			if (Logger.DEBUG_MODELSTATE) {
 				Logger.log(Logger.INFO, "IModelStateListener event for " + getId() + " : modelChanged"); //$NON-NLS-1$ //$NON-NLS-2$
 			}
 
-			endLock();
-			// notifify listeners outside of locked state (or deadlock
-			// can occur if one of them calls us back.
 			fireModelChanged();
 		}
 	}