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 */
017package org.apache.commons.transaction.locking;
018
019import 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 */
059public 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) throws InterruptedException {
096        return acquire(ownerId, READ_LOCK, true, true, timeoutMSecs);
097    }
098
099    /**
100     * Tries to acquire a reentrant upgrade lock on a resource. <br>
101     * 
102     * @param ownerId
103     *            a unique id identifying the entity that wants to acquire a
104     *            certain lock level on this lock
105     * @param timeoutMSecs
106     *            if blocking is enabled by the <code>wait</code> parameter
107     *            this specifies the maximum wait time in milliseconds
108     * @return <code>true</code> if the lock actually was acquired
109     * @throws InterruptedException
110     *             when the thread waiting on this method is interrupted
111     */
112    public boolean acquireUpgrade(Object ownerId, long timeoutMSecs) throws InterruptedException {
113        return acquire(ownerId, UPGRADE_LOCK, true, true, timeoutMSecs);
114    }
115
116    /**
117     * Tries to acquire a blocking, reentrant write lock. A write lock is
118     * incompatible with any another read or write lock and is thus exclusive.
119     * 
120     * @param ownerId
121     *            a unique id identifying the entity that wants to acquire a
122     *            certain lock level on this lock
123     * @param timeoutMSecs
124     *            if blocking is enabled by the <code>wait</code> parameter
125     *            this specifies the maximum wait time in milliseconds
126     * @return <code>true</code> if the lock actually was acquired
127     * @throws InterruptedException
128     *             when the thread waiting on this method is interrupted
129     */
130    public boolean acquireWrite(Object ownerId, long timeoutMSecs) throws InterruptedException {
131        // in case we already had an upgrade lock, this wait lock will become preferred
132        boolean preferred = getLockLevel(ownerId) == UPGRADE_LOCK;
133        return acquire(ownerId, WRITE_LOCK, true, COMPATIBILITY_REENTRANT, preferred, timeoutMSecs);
134    }
135
136    /**
137     * @see GenericLock#acquire(Object, int, boolean, int, boolean, long)
138     */
139    public synchronized boolean acquire(Object ownerId, int targetLockLevel, boolean wait,
140            int compatibility, boolean preferred, long timeoutMSecs) throws InterruptedException {
141        if (targetLockLevel == WRITE_LOCK && getLockLevel(ownerId) == UPGRADE_LOCK) {
142            preferred = true;
143        }
144        return super.acquire(ownerId, targetLockLevel, wait, compatibility, preferred, timeoutMSecs);
145    }
146
147}