001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.commons.transaction.locking;
018:
019: import org.apache.commons.transaction.util.LoggerFacade;
020:
021: /**
022: * Convenience implementation of a read/write lock with an option for upgrade
023: * based on {@link ReadWriteUpgradeLock}.<br>
024: * <br>
025: * Reads are shared which means there can be any number of concurrent read
026: * accesses allowed by this lock. Writes are exclusive. This means when there is
027: * a write access no other access neither read nor write are allowed by this
028: * lock. <br>
029: * <br>
030: * <p>
031: * The idea (as explained by Jim LoVerde) on an upgrade lock is that only one owner can hold an
032: * upgrade lock, but while that is held, it is possible for read locks to exist
033: * and/or be obtained, and when the request is made to upgrade to a write lock
034: * by the same owner, the lock manager prevents additional read locks until the
035: * write lock can be aquired.
036: * </p>
037: * <p>
038: * In this sense the write lock becomes preferred over all other locks when it gets upgraded from
039: * a upgrate lock. Preferred means that if it has to wait and others wait as well it will be
040: * served before all other none preferred locking requests.
041: * </p>
042: *
043: * Calls to {@link #acquireRead(Object, long)}, {@link #acquireUpgrade(Object, long)} and
044: * {@link #acquireWrite(Object, long)} are blocking and reentrant. Blocking
045: * means they will wait if they can not acquire the descired access, reentrant
046: * means that a lock request by a specific owner will always be compatible with
047: * other accesses on this lock by the same owner. E.g. if you already have a
048: * lock for writing and you try to acquire write access again you will not be
049: * blocked by this first lock, while others of course will be. This is the
050: * natural way you already know from Java monitors and synchronized blocks.
051: *
052: * @version $Id: ReadWriteUpgradeLock.java 493628 2007-01-07 01:42:48Z joerg $
053: *
054: * @see GenericLock
055: * @see org.apache.commons.transaction.locking.ReadWriteLock
056: * @see ReadWriteUpgradeLockManager
057: * @since 1.1
058: */
059: public class ReadWriteUpgradeLock extends GenericLock {
060:
061: public static final int NO_LOCK = 0;
062:
063: public static final int READ_LOCK = 1;
064:
065: public static final int UPGRADE_LOCK = 2;
066:
067: public static final int WRITE_LOCK = 3;
068:
069: /**
070: * Creates a new read/write/upgrade lock.
071: *
072: * @param resourceId
073: * identifier for the resource associated to this lock
074: * @param logger
075: * generic logger used for all kind of debug logging
076: */
077: public ReadWriteUpgradeLock(Object resourceId, LoggerFacade logger) {
078: super (resourceId, WRITE_LOCK, logger);
079: }
080:
081: /**
082: * Tries to acquire a blocking, reentrant read lock. A read lock is
083: * compatible with other read locks, but not with a write lock.
084: *
085: * @param ownerId
086: * a unique id identifying the entity that wants to acquire a
087: * certain lock level on this lock
088: * @param timeoutMSecs
089: * if blocking is enabled by the <code>wait</code> parameter
090: * this specifies the maximum wait time in milliseconds
091: * @return <code>true</code> if the lock actually was acquired
092: * @throws InterruptedException
093: * when the thread waiting on this method is interrupted
094: */
095: public boolean acquireRead(Object ownerId, long timeoutMSecs)
096: throws InterruptedException {
097: return acquire(ownerId, READ_LOCK, true, true, timeoutMSecs);
098: }
099:
100: /**
101: * Tries to acquire a reentrant upgrade lock on a resource. <br>
102: *
103: * @param ownerId
104: * a unique id identifying the entity that wants to acquire a
105: * certain lock level on this lock
106: * @param timeoutMSecs
107: * if blocking is enabled by the <code>wait</code> parameter
108: * this specifies the maximum wait time in milliseconds
109: * @return <code>true</code> if the lock actually was acquired
110: * @throws InterruptedException
111: * when the thread waiting on this method is interrupted
112: */
113: public boolean acquireUpgrade(Object ownerId, long timeoutMSecs)
114: throws InterruptedException {
115: return acquire(ownerId, UPGRADE_LOCK, true, true, timeoutMSecs);
116: }
117:
118: /**
119: * Tries to acquire a blocking, reentrant write lock. A write lock is
120: * incompatible with any another read or write lock and is thus exclusive.
121: *
122: * @param ownerId
123: * a unique id identifying the entity that wants to acquire a
124: * certain lock level on this lock
125: * @param timeoutMSecs
126: * if blocking is enabled by the <code>wait</code> parameter
127: * this specifies the maximum wait time in milliseconds
128: * @return <code>true</code> if the lock actually was acquired
129: * @throws InterruptedException
130: * when the thread waiting on this method is interrupted
131: */
132: public boolean acquireWrite(Object ownerId, long timeoutMSecs)
133: throws InterruptedException {
134: // in case we already had an upgrade lock, this wait lock will become preferred
135: boolean preferred = getLockLevel(ownerId) == UPGRADE_LOCK;
136: return acquire(ownerId, WRITE_LOCK, true,
137: COMPATIBILITY_REENTRANT, preferred, timeoutMSecs);
138: }
139:
140: /**
141: * @see GenericLock#acquire(Object, int, boolean, int, boolean, long)
142: */
143: public synchronized boolean acquire(Object ownerId,
144: int targetLockLevel, boolean wait, int compatibility,
145: boolean preferred, long timeoutMSecs)
146: throws InterruptedException {
147: if (targetLockLevel == WRITE_LOCK
148: && getLockLevel(ownerId) == UPGRADE_LOCK) {
149: preferred = true;
150: }
151: return super.acquire(ownerId, targetLockLevel, wait,
152: compatibility, preferred, timeoutMSecs);
153: }
154:
155: }
|