View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.transaction.locking;
18  
19  import org.apache.commons.transaction.util.LoggerFacade;
20  
21  /**
22   * Convenience implementation of a read/write lock with an option for upgrade
23   * based on {@link ReadWriteUpgradeLock}.<br>
24   * <br>
25   * Reads are shared which means there can be any number of concurrent read
26   * accesses allowed by this lock. Writes are exclusive. This means when there is
27   * a write access no other access neither read nor write are allowed by this
28   * lock. <br>
29   * <br>
30   * <p>
31   * The idea (as explained by Jim LoVerde) on an upgrade lock is that only one owner can hold an
32   * upgrade lock, but while that is held, it is possible for read locks to exist
33   * and/or be obtained, and when the request is made to upgrade to a write lock
34   * by the same owner, the lock manager prevents additional read locks until the
35   * write lock can be aquired.
36   * </p>
37   * <p>
38   * In this sense the write lock becomes preferred over all other locks when it gets upgraded from
39   * a upgrate lock. Preferred means that if it has to wait and others wait as well it will be
40   * served before all other none preferred locking requests.
41   * </p>
42   * 
43   * Calls to {@link #acquireRead(Object, long)}, {@link #acquireUpgrade(Object, long)} and
44   * {@link #acquireWrite(Object, long)} are blocking and reentrant. Blocking
45   * means they will wait if they can not acquire the descired access, reentrant
46   * means that a lock request by a specific owner will always be compatible with
47   * other accesses on this lock by the same owner. E.g. if you already have a
48   * lock for writing and you try to acquire write access again you will not be
49   * blocked by this first lock, while others of course will be. This is the
50   * natural way you already know from Java monitors and synchronized blocks.
51   * 
52   * @version $Id: ReadWriteUpgradeLock.java 493628 2007-01-07 01:42:48Z joerg $
53   * 
54   * @see GenericLock
55   * @see org.apache.commons.transaction.locking.ReadWriteLock
56   * @see ReadWriteUpgradeLockManager
57   * @since 1.1
58   */
59  public class ReadWriteUpgradeLock extends GenericLock {
60  
61      public static final int NO_LOCK = 0;
62  
63      public static final int READ_LOCK = 1;
64  
65      public static final int UPGRADE_LOCK = 2;
66  
67      public static final int WRITE_LOCK = 3;
68  
69      /**
70       * Creates a new read/write/upgrade lock.
71       * 
72       * @param resourceId
73       *            identifier for the resource associated to this lock
74       * @param logger
75       *            generic logger used for all kind of debug logging
76       */
77      public ReadWriteUpgradeLock(Object resourceId, LoggerFacade logger) {
78          super(resourceId, WRITE_LOCK, logger);
79      }
80  
81      /**
82       * Tries to acquire a blocking, reentrant read lock. A read lock is
83       * compatible with other read locks, but not with a write lock.
84       * 
85       * @param ownerId
86       *            a unique id identifying the entity that wants to acquire a
87       *            certain lock level on this lock
88       * @param timeoutMSecs
89       *            if blocking is enabled by the <code>wait</code> parameter
90       *            this specifies the maximum wait time in milliseconds
91       * @return <code>true</code> if the lock actually was acquired
92       * @throws InterruptedException
93       *             when the thread waiting on this method is interrupted
94       */
95      public boolean acquireRead(Object ownerId, long timeoutMSecs) throws InterruptedException {
96          return acquire(ownerId, READ_LOCK, true, true, timeoutMSecs);
97      }
98  
99      /**
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 }