1 /*
2 * Copyright 1999,2004 The Apache Software Foundation.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package org.apache.catalina.cluster.util;
18
19 /**
20 * The class <b>SingleRemoveSynchronizedAddLock</b> implement locking for accessing the queue
21 * by a single remove thread and multiple add threads.
22 *
23 * A thread is only allowed to be either the remove or
24 * an add thread.
25 *
26 * The lock can either be owned by the remove thread
27 * or by a single add thread.
28 *
29 * If the remove thread tries to get the lock,
30 * but the queue is empty, it will block (poll)
31 * until an add threads adds an entry to the queue and
32 * releases the lock.
33 *
34 * If the remove thread and add threads compete for
35 * the lock and an add thread releases the lock, then
36 * the remove thread will get the lock first.
37 *
38 * The remove thread removes all entries in the queue
39 * at once and proceeses them without further
40 * polling the queue.
41 *
42 * The lock is not reentrant, in the sense, that all
43 * threads must release an owned lock before competing
44 * for the lock again!
45 *
46 * @author Rainer Jung
47 * @author Peter Rossbach
48 * @version 1.1
49 */
50
51 public class SingleRemoveSynchronizedAddLock {
52
53 public SingleRemoveSynchronizedAddLock() {
54 }
55
56 public SingleRemoveSynchronizedAddLock(boolean dataAvailable) {
57 this.dataAvailable=dataAvailable;
58 }
59
60 /**
61 * Time in milliseconds after which threads
62 * waiting for an add lock are woken up.
63 * This is used as a safety measure in case
64 * thread notification via the unlock methods
65 * has a bug.
66 */
67 private long addWaitTimeout = 10000L;
68
69 /**
70 * Time in milliseconds after which threads
71 * waiting for a remove lock are woken up.
72 * This is used as a safety measure in case
73 * thread notification via the unlock methods
74 * has a bug.
75 */
76 private long removeWaitTimeout = 30000L;
77
78 /**
79 * The current remove thread.
80 * It is set to the remove thread polling for entries.
81 * It is reset to null when the remove thread
82 * releases the lock and proceeds processing
83 * the removed entries.
84 */
85 private Thread remover = null;
86
87 /**
88 * A flag indicating, if an add thread owns the lock.
89 */
90 private boolean addLocked = false;
91
92 /**
93 * A flag indicating, if the remove thread owns the lock.
94 */
95 private boolean removeLocked = false;
96
97 /**
98 * A flag indicating, if the remove thread is allowed
99 * to wait for the lock. The flag is set to false, when aborting.
100 */
101 private boolean removeEnabled = true;
102
103 /**
104 * A flag indicating, if the remover needs polling.
105 * It indicates, if the locked object has data available
106 * to be removed.
107 */
108 private boolean dataAvailable = false;
109
110 /**
111 * @return Value of addWaitTimeout
112 */
113 public synchronized long getAddWaitTimeout() {
114 return addWaitTimeout;
115 }
116
117 /**
118 * Set value of addWaitTimeout
119 */
120 public synchronized void setAddWaitTimeout(long timeout) {
121 addWaitTimeout = timeout;
122 }
123
124 /**
125 * @return Value of removeWaitTimeout
126 */
127 public synchronized long getRemoveWaitTimeout() {
128 return removeWaitTimeout;
129 }
130
131 /**
132 * Set value of removeWaitTimeout
133 */
134 public synchronized void setRemoveWaitTimeout(long timeout) {
135 removeWaitTimeout = timeout;
136 }
137
138 /**
139 * Check if the locked object has data available
140 * i.e. the remover can stop poling and get the lock.
141 * @return True iff the lock Object has data available.
142 */
143 public synchronized boolean isDataAvailable() {
144 return dataAvailable;
145 }
146
147 /**
148 * Check if an add thread owns the lock.
149 * @return True iff an add thread owns the lock.
150 */
151 public synchronized boolean isAddLocked() {
152 return addLocked;
153 }
154
155 /**
156 * Check if the remove thread owns the lock.
157 * @return True iff the remove thread owns the lock.
158 */
159 public synchronized boolean isRemoveLocked() {
160 return removeLocked;
161 }
162
163 /**
164 * Check if the remove thread is polling.
165 * @return True iff the remove thread is polling.
166 */
167 public synchronized boolean isRemovePolling() {
168 if ( remover != null ) {
169 return true;
170 }
171 return false;
172 }
173
174 /**
175 * Acquires the lock by an add thread and sets the add flag.
176 * If any add thread or the remove thread already acquired the lock
177 * this add thread will block until the lock is released.
178 */
179 public synchronized void lockAdd() {
180 if ( addLocked || removeLocked ) {
181 do {
182 try {
183 wait(addWaitTimeout);
184 } catch ( InterruptedException e ) {
185 }
186 } while ( addLocked || removeLocked );
187 }
188 addLocked=true;
189 }
190
191 /**
192 * Acquires the lock by the remove thread and sets the remove flag.
193 * If any add thread already acquired the lock or the queue is
194 * empty, the remove thread will block until the lock is released
195 * and the queue is not empty.
196 */
197 public synchronized boolean lockRemove() {
198 removeLocked=false;
199 removeEnabled=true;
200 if ( ( addLocked || ! dataAvailable ) && removeEnabled ) {
201 remover=Thread.currentThread();
202 do {
203 try {
204 wait(removeWaitTimeout);
205 } catch ( InterruptedException e ) {
206 }
207 } while ( ( addLocked || ! dataAvailable ) && removeEnabled );
208 remover=null;
209 }
210 if ( removeEnabled ) {
211 removeLocked=true;
212 }
213 return removeLocked;
214 }
215
216 /**
217 * Releases the lock by an add thread and reset the remove flag.
218 * If the reader thread is polling, notify it.
219 */
220 public synchronized void unlockAdd(boolean dataAvailable) {
221 addLocked=false;
222 this.dataAvailable=dataAvailable;
223 if ( ( remover != null ) && ( dataAvailable || ! removeEnabled ) ) {
224 remover.interrupt();
225 } else {
226 notifyAll();
227 }
228 }
229
230 /**
231 * Releases the lock by the remove thread and reset the add flag.
232 * Notify all waiting add threads,
233 * that the lock has been released by the remove thread.
234 */
235 public synchronized void unlockRemove() {
236 removeLocked=false;
237 dataAvailable=false;
238 notifyAll();
239 }
240
241 /**
242 * Abort any polling remover thread
243 */
244 public synchronized void abortRemove() {
245 removeEnabled=false;
246 if ( remover != null ) {
247 remover.interrupt();
248 }
249 }
250
251 }