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 }