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) 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    }