001: /**********************************************************************
002: Copyright (c) 2002 Mike Martin (TJDO) and others. All rights reserved.
003: Licensed under the Apache License, Version 2.0 (the "License");
004: you may not use this file except in compliance with the License.
005: You may obtain a copy of the License at
006:
007: http://www.apache.org/licenses/LICENSE-2.0
008:
009: Unless required by applicable law or agreed to in writing, software
010: distributed under the License is distributed on an "AS IS" BASIS,
011: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: See the License for the specific language governing permissions and
013: limitations under the License.
014:
015:
016: Contributors:
017: 2003 Andy Jefferson - commented
018: ...
019: **********************************************************************/package org.jpox.util;
020:
021: /**
022: * A simple read-write lock implementation. Multiple threads may lock using
023: * readLock(), only one can lock using writeLock(). The caller is responsible
024: * for coding a try-finally that ensures unlock() is called for every readLock()
025: * and writeLock() call.
026: * <p>
027: * A ReadWriteLock is recursive; with one exception, a thread can re-lock an
028: * object it already has locked. Multiple read locks can be acquired by the
029: * same thread, as can multiple write locks. The exception however is that a
030: * write lock cannot be acquired when a read lock is already held (to allow
031: * this would cause deadlocks).
032: * </p>
033: * <p>
034: * Successive lock calls from the same thread must be matched by an
035: * equal number of unlock() calls.
036: * </p>
037: *
038: * @version $Revision: 1.4 $
039: */
040: public class ReadWriteLock {
041: private static final Localiser LOCALISER = Localiser
042: .getInstance("org.jpox.Localisation");
043:
044: private static final int WAIT_LOG_INTERVAL = 5000;
045:
046: /** A count for each thread indicating the number of read locks it holds. */
047: private ThreadLocal readLocksByThread;
048:
049: /** The number of read locks held across all threads. */
050: private int readLocks;
051:
052: /** The number of write locks held (by writeLockedBy). */
053: private int writeLocks;
054:
055: /** The thread holding the write lock(s), if any. */
056: private Thread writeLockedBy;
057:
058: /**
059: * An object holding a per-thread read-lock count.
060: */
061: private static class Count {
062: /** count value **/
063: public int value = 0;
064: }
065:
066: /**
067: * Constructs read-write lock.
068: */
069: public ReadWriteLock() {
070: readLocksByThread = new ThreadLocal() {
071: public Object initialValue() {
072: return new Count();
073: }
074: };
075:
076: readLocks = 0;
077: writeLocks = 0;
078: writeLockedBy = null;
079: }
080:
081: /**
082: * Acquire a read lock. The calling thread will be suspended until no other
083: * thread holds a write lock.
084: *
085: * <p>If the calling thread already owns a write lock for the object a read
086: * lock is immediately acquired.
087: *
088: * @exception InterruptedException
089: * If the thread is interrupted while attempting to acquire the lock.
090: */
091: public synchronized void readLock() throws InterruptedException {
092: Thread me = Thread.currentThread();
093: Count myReadLocks = (Count) readLocksByThread.get();
094:
095: if (writeLockedBy != me) {
096: while (writeLocks > 0) {
097: wait(WAIT_LOG_INTERVAL);
098:
099: if (writeLocks > 0) {
100: if (JPOXLogger.GENERAL.isDebugEnabled()) {
101: JPOXLogger.GENERAL.debug(LOCALISER.msg(
102: "030000", this ),
103: new InterruptedException());
104: }
105: }
106: }
107: }
108:
109: ++readLocks;
110: ++myReadLocks.value;
111: }
112:
113: /**
114: * Acquire a write lock. The calling thread will be suspended until no
115: * other thread holds a read or write lock.
116: *
117: * <p>This method cannot be called if the thread already owns a read lock on
118: * the same ReadWriteLock object, otherwise an
119: * <code>IllegalStateException</code> is thrown.
120: *
121: * @exception IllegalStateException
122: * If the thread already holds a read lock on the same object.
123: * @exception InterruptedException
124: * If the thread is interrupted while attempting to acquire the lock.
125: */
126: public synchronized void writeLock() throws InterruptedException {
127: Thread me = Thread.currentThread();
128: Count myReadLocks = (Count) readLocksByThread.get();
129:
130: if (myReadLocks.value > 0) {
131: throw new IllegalStateException(LOCALISER.msg("030001"));
132: }
133:
134: if (writeLockedBy != me) {
135: while (writeLocks > 0 || readLocks > 0) {
136: wait(WAIT_LOG_INTERVAL);
137:
138: if (writeLocks > 0 || readLocks > 0) {
139: if (JPOXLogger.GENERAL.isDebugEnabled()) {
140: JPOXLogger.GENERAL.debug(LOCALISER.msg(
141: "030002", this ),
142: new InterruptedException());
143: }
144: }
145: }
146:
147: writeLockedBy = me;
148: }
149:
150: ++writeLocks;
151: }
152:
153: /**
154: * Release a read or write lock. Must be called in a finally block after
155: * acquiring a lock.
156: */
157: public synchronized void unlock() {
158: Thread me = Thread.currentThread();
159: Count myReadLocks = (Count) readLocksByThread.get();
160:
161: if (myReadLocks.value > 0) {
162: --myReadLocks.value;
163: --readLocks;
164: } else if (writeLockedBy == me) {
165: if (writeLocks > 0) {
166: if (--writeLocks == 0) {
167: writeLockedBy = null;
168: }
169: }
170: }
171:
172: notifyAll();
173: }
174:
175: /**
176: * Method to return this object as a String.
177: * @return String version of this object.
178: **/
179: public String toString() {
180: StringBuffer s = new StringBuffer(super .toString());
181:
182: s.append(": readLocks = ").append(readLocks).append(
183: ", writeLocks = ").append(writeLocks).append(
184: ", writeLockedBy = ").append(writeLockedBy);
185:
186: return s.toString();
187: }
188: }
|