001: /*
002: * (c) Copyright 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
003: * [See end of file]
004: */
005:
006: package com.hp.hpl.jena.shared;
007:
008: import com.hp.hpl.jena.shared.JenaException;
009:
010: import EDU.oswego.cs.dl.util.concurrent.WriterPreferenceReadWriteLock;
011: import EDU.oswego.cs.dl.util.concurrent.SynchronizedInt;
012: import java.util.*;
013: import org.apache.commons.logging.*;
014:
015: /**
016: * Lock implemenetation using a Multiple Reader, Single Writer policy.
017: * All the locking work is done by the imported WriterPreferenceReadWriteLock.
018: * Ths class adds:
019: * <ul>
020: * <li>The same thread that acquired a lock should release it</li>
021: * <li>Lock promotion (turning read locks into write locks) is
022: * deteched as an error</li>
023: * <ul>
024: *
025: * @author Andy Seaborne
026: * @version $Id: LockMRSW.java,v 1.5 2008/01/02 12:06:11 andy_seaborne Exp $
027: */
028:
029: public class LockMRSW implements Lock {
030: static Log log = LogFactory.getLog(LockMRSW.class);
031:
032: // Map of threads to lock state for this lock
033: Map threadStates = new HashMap();
034: // We keep this is a variable because it is tested outside of a lock.
035: int threadStatesSize = threadStates.size();
036:
037: //ReentrantWriterPreferenceReadWriteLock lock = new ReentrantWriterPreferenceReadWriteLock();
038: WriterPreferenceReadWriteLock mrswLock = new WriterPreferenceReadWriteLock();
039:
040: SynchronizedInt activeReadLocks = new SynchronizedInt(0);
041: SynchronizedInt activeWriteLocks = new SynchronizedInt(0);
042:
043: public LockMRSW() {
044: if (log.isDebugEnabled())
045: log.debug("Lock : " + Thread.currentThread().getName());
046: }
047:
048: /** Application controlled locking - enter a critical section.
049: * Locking is reentrant so an application can have nested critical sections.
050: * Typical code:
051: * <pre>
052: * try {
053: * enterCriticalSection(Lock.READ) ;
054: * ... application code ...
055: * } finally { leaveCriticalSection() ; }
056: * </pre>
057: */
058:
059: final public void enterCriticalSection(boolean readLockRequested) {
060: // Don't make {enter|leave}CriticalSection synchronized - deadlock will occur.
061: // The current thread will hold the model lock thread
062: // and will attempt to grab the MRSW lock.
063: // But if it waits, no other thread will even get
064: // to release the lock as it can't enter leaveCriticalSection
065:
066: LockState state = getLockState();
067:
068: // At this point we have the state object which is unique per
069: // model per thread. Thus, we can do updates to via state.???
070: // because we know no other thread is active on it.
071:
072: if (log.isDebugEnabled())
073: log.debug(Thread.currentThread().getName()
074: + " >> enterCS: " + report(state));
075:
076: // If we have a read lock, but no write locks, then the thread is attempting
077: // a lock promotion. We do not allow this.
078: if (state.readLocks > 0 && state.writeLocks == 0
079: && !readLockRequested) {
080: // Increment the readlock so a later leaveCriticialSection
081: // keeps the counters aligned.
082: state.readLocks++;
083: activeReadLocks.increment();
084:
085: if (log.isDebugEnabled())
086: log.debug(Thread.currentThread().getName()
087: + " << enterCS: promotion attempt: "
088: + report(state));
089:
090: throw new JenaException(
091: "enterCriticalSection: Write lock request while holding read lock - potential deadlock");
092: }
093:
094: // Trying to get a read lock after a write lock - get a write lock instead.
095: if (state.writeLocks > 0 && readLockRequested)
096: readLockRequested = false;
097:
098: try {
099: if (readLockRequested) {
100: if (state.readLocks == 0)
101: mrswLock.readLock().acquire();
102: state.readLocks++;
103: activeReadLocks.increment();
104: } else {
105: if (state.writeLocks == 0)
106: mrswLock.writeLock().acquire();
107: state.writeLocks++;
108: activeWriteLocks.increment();
109: }
110: } catch (InterruptedException intEx) {
111: } finally {
112: if (log.isDebugEnabled())
113: log.debug(Thread.currentThread().getName()
114: + " << enterCS: " + report(state));
115: }
116: }
117:
118: /** Application controlled locking - leave a critical section.
119: * @see #enterCriticalSection
120: */
121:
122: final public void leaveCriticalSection() {
123:
124: LockState state = getLockState();
125:
126: if (log.isDebugEnabled())
127: log.debug(Thread.currentThread().getName()
128: + " >> leaveCS: " + report(state));
129:
130: try {
131: if (state.readLocks > 0) {
132: state.readLocks--;
133: activeReadLocks.decrement();
134:
135: if (state.readLocks == 0)
136: mrswLock.readLock().release();
137:
138: state.clean();
139: return;
140: }
141:
142: if (state.writeLocks > 0) {
143: state.writeLocks--;
144: activeWriteLocks.decrement();
145:
146: if (state.writeLocks == 0)
147: mrswLock.writeLock().release();
148:
149: state.clean();
150: return;
151: }
152:
153: // No lock held.
154:
155: throw new JenaException(
156: "leaveCriticalSection: No lock held ("
157: + Thread.currentThread().getName() + ")");
158: } finally {
159: if (log.isDebugEnabled())
160: log.debug(Thread.currentThread().getName()
161: + " << leaveCS: " + report(state));
162: }
163: }
164:
165: private String report(LockState state) {
166: StringBuffer sb = new StringBuffer();
167: sb.append("Thread R/W: ");
168: sb.append(Integer.toString(state.readLocks));
169: sb.append("/");
170: sb.append(Integer.toString(state.writeLocks));
171: sb.append(" :: Model R/W: ");
172: sb.append(Integer.toString(activeReadLocks.get()));
173: sb.append("/");
174: sb.append(Integer.toString(activeWriteLocks.get()));
175: sb.append(" (thread: ");
176: sb.append(state.thread.getName());
177: sb.append(")");
178: return sb.toString();
179: }
180:
181: // Model internal functions -----------------------------
182:
183: synchronized LockState getLockState() {
184: Thread this Thread = Thread.currentThread();
185: LockState state = (LockState) threadStates.get(this Thread);
186: if (state == null) {
187: state = new LockState(this );
188: threadStates.put(this Thread, state);
189: threadStatesSize = threadStates.size();
190: }
191: return state;
192: }
193:
194: synchronized void removeLockState(Thread thread) {
195: threadStates.remove(thread);
196: }
197:
198: static class LockState {
199: // Counters for this lock object.
200: // Instances of ModelLockState are held per thread per model.
201: // These do not need to be atomic because a thread is the
202: // only accessor of its own counters
203:
204: int readLocks = 0;
205: int writeLocks = 0;
206: LockMRSW lock;
207: Thread thread;
208:
209: // Need to pass in the containing model lock
210: // because we want a lock on it.
211: LockState(LockMRSW theLock) {
212: lock = theLock;
213: thread = Thread.currentThread();
214: }
215:
216: void clean() {
217: if (lock.activeReadLocks.get() == 0
218: && lock.activeWriteLocks.get() == 0) {
219: // A bit simple - but it churns (LockState creation) in the
220: // case of a thread looping around a critical section.
221: // The alternative, to delay now and do a more sophisticated global GC
222: // could require a global pause which is worse.
223: lock.removeLockState(thread);
224: }
225: }
226: }
227: }
228:
229: /*
230: * (c) Copyright 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
231: * All rights reserved.
232: *
233: * Redistribution and use in source and binary forms, with or without
234: * modification, are permitted provided that the following conditions
235: * are met:
236: * 1. Redistributions of source code must retain the above copyright
237: * notice, this list of conditions and the following disclaimer.
238: * 2. Redistributions in binary form must reproduce the above copyright
239: * notice, this list of conditions and the following disclaimer in the
240: * documentation and/or other materials provided with the distribution.
241: * 3. The name of the author may not be used to endorse or promote products
242: * derived from this software without specific prior written permission.
243: *
244: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
245: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
246: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
247: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
248: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
249: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
250: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
251: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
252: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
253: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
254: */
|