/*******************************************************************************
 * Copyright (c) 2010 itemis AG (http://www.itemis.eu) 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
 *******************************************************************************/
package org.eclipse.xtext.util.concurrent;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.eclipse.emf.common.util.WrappedException;

/**
 * @author Sven Efftinge  - Initial contribution and API
 * @author Jan Koehnlein
 */
public abstract class AbstractReadWriteAcces<P> implements IReadAccess<P> {

	protected abstract P getState();

	protected final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
	protected final Lock writeLock = rwLock.writeLock();
	protected final Lock readLock = rwLock.readLock();

	public <T> T readOnly(IUnitOfWork<T, P> work) {
		readLock.lock();
		try {
			P state = getState();
			beforeReadOnly(state,work);
			T exec = work.exec(state);
			afterReadOnly(state,exec,work);
			return exec;
		} catch (RuntimeException e) {
			throw e;
		} catch (Exception e) {
			throw new WrappedException(e);
		} finally {
			readLock.unlock();
		}
	}

	public <T> T modify(IUnitOfWork<T, P> work) {
		writeLock.lock();
		P state = null;
		T exec = null;
		try {
			state = getState();
			beforeModify(state, work);
			exec = work.exec(state);
			return exec;
		} catch (RuntimeException e) {
			throw e;
		} catch (Exception e) {
			throw new WrappedException(e);
		} finally {
			writeLock.unlock();
			try {
				readLock.lock();
				afterModify(state, exec, work);
			} finally {
				readLock.unlock();
			}
		}
	}
	
	/**
	 * Is called before a write lock is obtained
	 * @param work - the unit of work to be processed
	 */
	protected void beforeModify(P state, IUnitOfWork<?, P> work) {}
	
	
	/**
	 * is called before a read lock is obtained
	 * @param work - the unit of work to be processed
	 */
	protected void beforeReadOnly(P state, IUnitOfWork<?, P> work) {}
	
	/**
	 * is executed within the transaction right after the unit of work has been executed and delivered the result.
	 * @param result
	 * @param work
	 */
	protected void afterModify(P state, Object result, IUnitOfWork<?, P> work) {}
	
	/**
	 * is executed within the transaction right after the unit of work has been executed and delivered the result.
	 * @param result
	 * @param work
	 */
	protected void afterReadOnly(P state, Object result, IUnitOfWork<?, P> work) {}

}
@@ -612,16 +612,48 @@
 eLock;%0A%0A
+import org.apache.log4j.Logger;%0A
 import o
@@ -865,16 +865,96 @@
 s%3CP%3E %7B%0A%0A
+%09private static Logger log = Logger.getLogger(AbstractReadWriteAcces.class); %0A%09%0A
 %09protect
@@ -1218,25 +1218,170 @@
 k) %7B
-%0A%09%09readLock.lock(
+%09%0A%09%09if(log.isTraceEnabled()) %0A%09%09%09log.trace(%22Trying to acquire read lock...%22);%0A%09%09readLock.lock();%0A%09%09if(log.isTraceEnabled()) %0A%09%09%09log.trace(%22...read lock acquired.%22
 );%0A%09
@@ -1671,78 +1671,291 @@
 ;%0A%09%09
-%7D%0A%09%7D%0A%0A%09public %3CT%3E T modify(IUnitOfWork%3CT, P%3E work) %7B%0A%09%09writeLock.lock(
+%09if(log.isTraceEnabled()) %0A%09%09%09%09log.trace(%22Read lock released.%22);%0A%09%09%7D%0A%09%7D%0A%0A%09public %3CT%3E T modify(IUnitOfWork%3CT, P%3E work) %7B%0A%09%09if(log.isTraceEnabled()) %0A%09%09%09log.trace(%22Trying to acquire write lock...%22);%0A%09%09writeLock.lock();%0A%09%09if(log.isTraceEnabled()) %0A%09%09%09log.trace(%22...write lock acquired.%22
 );%0A%09
@@ -2242,106 +2242,395 @@
 %0A%09%09%09
-try %7B%0A%09%09%09%09readLock.lock();%0A%09%09%09%09afterModify(state, exec, work);%0A%09%09%09%7D finally %7B%0A%09%09%09%09readLock.unlock(
+if(log.isTraceEnabled()) %0A%09%09%09%09log.trace(%22Write lock released.%22);%0A%09%09%09try %7B%0A%09%09%09%09if(log.isTraceEnabled()) %0A%09%09%09%09%09log.trace(%22Trying to acquire read lock...%22);%0A%09%09%09%09readLock.lock();%0A%09%09%09%09if(log.isTraceEnabled()) %0A%09%09%09%09%09log.trace(%22...read lock acquired.%22);%0A%09%09%09%09afterModify(state, exec, work);%0A%09%09%09%7D finally %7B%0A%09%09%09%09readLock.unlock();%0A%09%09%09%09if(log.isTraceEnabled()) %0A%09%09%09%09%09log.trace(%22Read lock released.%22
 );%0A%09

fix condition for performNecessaryUpdates avoid deadlock when switch ing form read to write lock and added tracing for locks https://bugs.eclipse.org/bugs/show_bug.cgi?id=408742