001: /**
002: * Sequoia: Database clustering technology.
003: * Copyright (C) 2002-2004 French National Institute For Research In Computer
004: * Science And Control (INRIA).
005: * Contact: sequoia@continuent.org
006: *
007: * Licensed under the Apache License, Version 2.0 (the "License");
008: * you may not use this file except in compliance with the License.
009: * You may obtain a copy of the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS,
015: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: * See the License for the specific language governing permissions and
017: * limitations under the License.
018: *
019: * Initial developer(s): Emmanuel Cecchet.
020: * Contributor(s): ______________________.
021: */package org.continuent.sequoia.common.locks;
022:
023: import java.util.ArrayList;
024:
025: /**
026: * Reader/Writer lock with write priority.
027: * <p>
028: * If a reader holds the lock, all incoming readers are allowed to acquire the
029: * lock until a write arrives. A writer must wait for all current readers to
030: * release the lock before acquiring it. <br>
031: * When a writer has the lock, everybody else is blocked. <br>
032: * When a writer release the lock, there is a writer priority so if another
033: * writer is waiting it will have the lock even if it arrived later than
034: * readers. Writers are prioritary against readers but all writers get the lock
035: * in a FIFO order.
036: *
037: * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
038: * @version 1.0
039: */
040: public class ReadPrioritaryFIFOWriteLock {
041: /** Threads executing read. */
042: private int activeReaders;
043:
044: /** Is there an active writer? */
045: private boolean activeWriter;
046:
047: /** Threads not yet in read. */
048: private int waitingReaders;
049:
050: /** Threads not yet in write. */
051: private int waitingWriters;
052:
053: private Object readSync;
054: private ArrayList writeWaitingQueue;
055:
056: /**
057: * Creates a new <code>ReadPrioritaryFIFOWriteLock</code> instance.
058: */
059: public ReadPrioritaryFIFOWriteLock() {
060: activeReaders = 0;
061: activeWriter = false;
062: waitingReaders = 0;
063: waitingWriters = 0;
064: readSync = new Object();
065: writeWaitingQueue = new ArrayList();
066: }
067:
068: /**
069: * Acquires the lock for a read.
070: *
071: * @throws InterruptedException if wait failed (the lock should remain in a
072: * correct state)
073: */
074: public void acquireRead() throws InterruptedException {
075: synchronized (this ) {
076: if ((waitingWriters == 0) && !activeWriter) { // No active or pending writer
077: activeReaders++;
078: return;
079: }
080: }
081:
082: // Beyond this point, we have to wait
083: synchronized (readSync) {
084: // It becomes a little bit tricky here but a writer could have
085: // released the lock between the first test at the beginning of
086: // this function and this point. Therefore we have to recheck if
087: // the lock is not completely idle else we'll never wake up !
088: synchronized (this ) {
089: if (!activeWriter) { // No active or pending writer
090: activeReaders++;
091: return;
092: }
093: }
094:
095: waitingReaders++;
096: try {
097: readSync.wait();
098: } catch (InterruptedException ie) {
099: waitingReaders--; // roll back state
100: throw ie;
101: }
102: waitingReaders--;
103: }
104: synchronized (this ) {
105: activeReaders++;
106: }
107: }
108:
109: /**
110: * Releases a lock previously acquired for reading.
111: */
112: public synchronized void releaseRead() {
113: activeReaders--;
114: if ((activeReaders == 0) && (waitingWriters > 0)) { // Wake up first waiting write if any
115: Object thread = writeWaitingQueue.remove(0);
116: synchronized (thread) {
117: thread.notify();
118: activeWriter = true;
119: waitingWriters--;
120: }
121: }
122: }
123:
124: /**
125: * Acquires the lock for a write.
126: *
127: * @throws InterruptedException if wait failed (the lock should remain in a
128: * correct state)
129: */
130: public void acquireWrite() throws InterruptedException {
131: synchronized (Thread.currentThread()) {
132: synchronized (this ) {
133: if ((activeReaders == 0) && !activeWriter) {
134: activeWriter = true;
135: return;
136: } else {
137: waitingWriters++;
138: writeWaitingQueue.add(Thread.currentThread());
139: }
140: }
141: try {
142: Thread.currentThread().wait();
143: } catch (InterruptedException ie) {
144: releaseWrite();
145: throw ie;
146: }
147: }
148: }
149:
150: /**
151: * Releases a lock previously acquired for writing.
152: */
153: public void releaseWrite() {
154: // Writer priority
155: synchronized (this ) {
156: activeWriter = false;
157: if (waitingWriters > 0) { // Wake up first waiting write if any
158: Object thread = writeWaitingQueue.remove(0);
159: synchronized (thread) {
160: thread.notify();
161: activeWriter = true;
162: waitingWriters--;
163: }
164: return;
165: }
166: }
167:
168: // Wake up readers
169: //
170: // Note that we cannot do the following inside synchronized(this) because
171: // acquireRead() takes synchronized(readSync) first and then
172: // synchronized(this).
173: synchronized (readSync) {
174: if (waitingReaders > 0)
175: readSync.notifyAll();
176: }
177: }
178:
179: /**
180: * Tests if the lock is currently held by at least one reader.
181: *
182: * @return <code>true</code> if the lock is held by a reader
183: */
184: public final synchronized boolean isReadLocked() {
185: return activeReaders > 0;
186: }
187:
188: /**
189: * Tests if the lock is currently held by a writer.
190: *
191: * @return <code>true</code> if the lock is held by a writer
192: */
193: public final synchronized boolean isWriteLocked() {
194: return activeWriter;
195: }
196: }
|