001: // You can redistribute this software and/or modify it under the terms of
002: // the Ozone Core License version 1 published by ozone-db.org.
003: //
004: // The original code and portions created by SMB are
005: // Copyright (C) 1997-@year@ by SMB GmbH. All rights reserved.
006: //
007: // $Id: MROWLock.java,v 1.2 2002/06/08 00:49:38 mediumnet Exp $
008:
009: package org.ozoneDB.core;
010:
011: import java.io.*;
012: import org.ozoneDB.*;
013: import org.ozoneDB.DxLib.*;
014: import org.ozoneDB.util.*;
015:
016: /**
017: * This class implements a Multiple Reader One Writer lock policy.
018: *
019: *
020: * @author <a href="http://www.softwarebuero.de/">SMB</a>
021: * @author <a href="http://www.medium.net/">Medium.net</a>
022: * @version $Revision: 1.2 $Date: 2002/06/08 00:49:38 $
023: */
024: public final class MROWLock extends AbstractLock {
025:
026: protected final static long serialVersionUID = 1;
027: protected final static byte subSerialVersionUID = 1;
028:
029: protected final static boolean enableReleaseOfReadlockOnFailedWritelock = false;
030:
031: private int level = LEVEL_NONE;
032:
033: private SharedLock readLock;
034:
035: private ExclusiveLock writeLock;
036:
037: protected transient org.ozoneDB.core.wizardStore.ClusterID clusterID;
038:
039: public MROWLock() {
040: reset();
041: }
042:
043: public synchronized void reset() {
044: level = LEVEL_NONE;
045: readLock = new SharedLock();
046: writeLock = new ExclusiveLock();
047: }
048:
049: public void setClusterID(org.ozoneDB.core.wizardStore.ClusterID to) {
050: this .clusterID = to;
051: }
052:
053: public int tryAcquire(Transaction ta, int newLevel) {
054: if (false && ta.env.logWriter.hasTarget(LogWriter.DEBUG3)) {
055: ta.env.logWriter.newEntry(this , "tryAcquire(): ta.getID()="
056: + ta.taID() + ", current:" + level + " new:"
057: + newLevel + ", writeLock.getLocker()="
058: + writeLock.getLocker() + ".", LogWriter.DEBUG3);
059: }
060:
061: if (readLock.isAcquiredBy(ta) && level == newLevel) {
062: return level;
063: }
064:
065: if (ta.env.logWriter.hasTarget(LogWriter.DEBUG2)) {
066: if (false && (newLevel > level || (!ta.taID().equals(
067: writeLock.getLocker())))) {
068: ta.env.logWriter
069: .newEntry(this , this
070: + ".tryAcquire(): ta.getID()="
071: + ta.taID() + ", current:" + level
072: + " new:" + newLevel
073: + ", writeLock.getLocker()="
074: + writeLock.getLocker() + ".",
075: LogWriter.DEBUG2);
076: }
077: }
078:
079: synchronized (this ) {
080: int prevLevel = NOT_ACQUIRED;
081:
082: if (newLevel <= LEVEL_NONE || newLevel >= LEVEL_MAX) {
083: ta.env.logWriter.newEntry(this ,
084: "tryAcquire(): newLevel == " + newLevel,
085: LogWriter.WARN);
086: }
087:
088: boolean result = false;
089:
090: // no transaction holds this lock
091: if (level == LEVEL_NONE) {
092: prevLevel = LEVEL_NONE;
093:
094: int readLockPrevLevel;
095:
096: if ((readLockPrevLevel = readLock.tryAcquire(ta,
097: LEVEL_READ)) != NOT_ACQUIRED) {
098: result = true;
099: if (newLevel > LEVEL_READ) {
100: if (writeLock.tryAcquire(ta, LEVEL_WRITE) == NOT_ACQUIRED) {
101: result = false;
102: if (enableReleaseOfReadlockOnFailedWritelock) {
103: readLock.release(ta);
104: } else {
105: if (readLockPrevLevel < LEVEL_READ) { // If we had acquired the read lock only now.
106: readLock.release(ta);
107: }
108: }
109: }
110: }
111: }
112: } else if (readLock.isAcquiredBy(ta)) {
113: // the given ta itself holds the lock
114: prevLevel = level;
115: result = true;
116: if (newLevel > LEVEL_READ) {
117: /*
118: The given transaction itself holds the lock, fine. But what if other transactions hold the readl lock, too?
119: I think that we cannot give the requesting transaction the write lock if other transactions are still
120: holding the read lock. In this case the requesting transaction may overwrite (and touch) data which the other
121: transactions may access.
122: */
123: if (!readLock.areMultipleLockersHoldingLocks()) {
124: if (writeLock.tryAcquire(ta, LEVEL_WRITE) == NOT_ACQUIRED) {
125: result = false;
126: readLock.release(ta);
127: }
128: } else {
129: result = false;
130:
131: if (enableReleaseOfReadlockOnFailedWritelock) {
132: // FIXME: I'm really unsure why I should release a read lock if just the write lock obtaining failed.
133: readLock.release(ta);
134: }
135: }
136: }
137: } else {
138: // another ta holds a READ lock and we want READ lock too
139: if (newLevel == LEVEL_READ && level == LEVEL_READ) {
140: prevLevel = LEVEL_NONE;
141: if (readLock.tryAcquire(ta, LEVEL_READ) != NOT_ACQUIRED) {
142: result = true;
143: }
144: }
145: }
146:
147: if (result) {
148: level = newLevel > level ? newLevel : level;
149: return prevLevel;
150: } else {
151: return NOT_ACQUIRED;
152: }
153: }
154: }
155:
156: protected synchronized boolean acquire(Transaction ta, int newLevel) {
157: int prevLevel = readLock.tryAcquire(ta, LEVEL_READ);
158: if (newLevel > LEVEL_READ && prevLevel > NOT_ACQUIRED) {
159: prevLevel = writeLock.tryAcquire(ta, LEVEL_WRITE);
160: if (prevLevel == NOT_ACQUIRED) {
161: readLock.release(ta);
162: }
163: }
164: return prevLevel != NOT_ACQUIRED;
165: }
166:
167: public void release(Transaction ta) {
168: if (false && ta.env.logWriter.hasTarget(LogWriter.DEBUG2)) {
169: ta.env.logWriter.newEntry(this , this
170: + ".release(): ta.getID()=" + ta.taID() + ".",
171: LogWriter.DEBUG2);
172: }
173:
174: if (level == LEVEL_NONE) {
175: ta.env.logWriter.newEntry(this , this
176: + ".release(): current level is LEVEL_NONE.",
177: new Exception(), LogWriter.WARN);
178: }
179:
180: synchronized (this ) {
181: if (writeLock.isAcquiredBy(ta)) {
182: writeLock.release(ta);
183: level = LEVEL_READ;
184: }
185: if (readLock.isAcquiredBy(ta)) {
186: readLock.release(ta);
187: if (readLock.level(null) == LEVEL_NONE) {
188: level = LEVEL_NONE;
189: }
190: }
191: }
192:
193: /*
194: DxIterator it = lockers.iterator();
195: while (it.next() != null) {
196: TransactionID taID = (TransactionID)it.object();
197: if (ta.env.transactionManager.taForID (taID) == null) {
198: System.out.println ("*********** INPROPER LOCK ********");
199: it.removeObject();
200: }
201: }
202: */
203: }
204:
205: public boolean isAcquiredBy(Transaction ta) {
206: switch (level) {
207: case LEVEL_NONE: {
208: return false;
209: }
210: case LEVEL_READ: {
211: return readLock.isAcquiredBy(ta);
212: }
213: default: {
214: return writeLock.isAcquiredBy(ta);
215: }
216: }
217: }
218:
219: public DxCollection lockerIDs() {
220: switch (level) {
221: case LEVEL_NONE: {
222: return new DxArrayBag();
223: }
224: default: {
225: // since all write lockers are read lockers too, we can
226: // return readlocker no metter what the lock level is
227: return readLock.lockerIDs();
228: }
229: }
230: }
231:
232: public int level(Transaction ta) {
233: if (ta == null) {
234: return level;
235: } else {
236: switch (level) {
237: case LEVEL_NONE: {
238: return LEVEL_NONE;
239: }
240: case LEVEL_READ: {
241: return readLock.isAcquiredBy(ta) ? level : LEVEL_NONE;
242: }
243: default: {
244: return writeLock.isAcquiredBy(ta) ? level : LEVEL_NONE;
245: }
246: }
247: }
248: }
249:
250: public String toString() {
251: return "MROWLock[clusterID=" + clusterID
252: + ",writeLock.getLocker()=" + writeLock.getLocker()
253: + ",readLock=" + readLock + "]";
254: }
255: }
|