1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.regionserver;
20
21 import org.apache.commons.logging.Log;
22 import org.apache.commons.logging.LogFactory;
23 import org.apache.hadoop.classification.InterfaceAudience;
24 import org.apache.hadoop.hbase.util.HasThread;
25
26 import java.util.ConcurrentModificationException;
27 import java.util.HashMap;
28 import java.util.Iterator;
29 import java.util.Map;
30 import java.util.concurrent.ConcurrentHashMap;
31 import java.util.concurrent.Delayed;
32 import java.util.concurrent.DelayQueue;
33 import java.util.concurrent.TimeUnit;
34
35 import java.io.IOException;
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 @InterfaceAudience.Private
57 public class Leases extends HasThread {
58 private static final Log LOG = LogFactory.getLog(Leases.class.getName());
59 private final Map<String, Lease> leases = new ConcurrentHashMap<String, Lease>();
60
61 protected final int leaseCheckFrequency;
62 protected volatile boolean stopRequested = false;
63
64
65
66
67
68
69
70 public Leases(final int leaseCheckFrequency) {
71 this.leaseCheckFrequency = leaseCheckFrequency;
72 setDaemon(true);
73 }
74
75
76
77
78 @Override
79 public void run() {
80 long toWait = leaseCheckFrequency;
81 Lease nextLease = null;
82 long nextLeaseDelay = Long.MAX_VALUE;
83
84 while (!stopRequested || (stopRequested && !leases.isEmpty()) ) {
85
86 try {
87 if (nextLease != null) {
88 toWait = nextLease.getDelay(TimeUnit.MILLISECONDS);
89 }
90 toWait = Math.min(leaseCheckFrequency, toWait);
91 Thread.sleep(toWait);
92 } catch (InterruptedException e) {
93 continue;
94 } catch (ConcurrentModificationException e) {
95 continue;
96 } catch (Throwable e) {
97 LOG.fatal("Unexpected exception killed leases thread", e);
98 break;
99 }
100
101 nextLease = null;
102 nextLeaseDelay = Long.MAX_VALUE;
103 for (Iterator<Map.Entry<String, Lease>> it = leases.entrySet().iterator(); it.hasNext();) {
104 Map.Entry<String, Lease> entry = it.next();
105 Lease lease = entry.getValue();
106 long thisLeaseDelay = lease.getDelay(TimeUnit.MILLISECONDS);
107 if ( thisLeaseDelay > 0) {
108 if (nextLease == null || thisLeaseDelay < nextLeaseDelay) {
109 nextLease = lease;
110 nextLeaseDelay = thisLeaseDelay;
111 }
112 } else {
113
114
115 if (lease.getListener() == null) {
116 LOG.error("lease listener is null for lease " + lease.getLeaseName());
117 } else {
118 lease.getListener().leaseExpired();
119 }
120 it.remove();
121 }
122 }
123 }
124 close();
125 }
126
127
128
129
130
131
132
133
134 public void closeAfterLeasesExpire() {
135 this.stopRequested = true;
136 }
137
138
139
140
141
142 public void close() {
143 LOG.info(Thread.currentThread().getName() + " closing leases");
144 this.stopRequested = true;
145 leases.clear();
146 LOG.info(Thread.currentThread().getName() + " closed leases");
147 }
148
149
150
151
152
153
154
155
156
157 public void createLease(String leaseName, int leaseTimeoutPeriod, final LeaseListener listener)
158 throws LeaseStillHeldException {
159 addLease(new Lease(leaseName, leaseTimeoutPeriod, listener));
160 }
161
162
163
164
165
166
167 public void addLease(final Lease lease) throws LeaseStillHeldException {
168 if (this.stopRequested) {
169 return;
170 }
171 lease.resetExpirationTime();
172 if (leases.containsKey(lease.getLeaseName())) {
173 throw new LeaseStillHeldException(lease.getLeaseName());
174 }
175 leases.put(lease.getLeaseName(), lease);
176 }
177
178
179
180
181
182
183
184 public void renewLease(final String leaseName) throws LeaseException {
185 Lease lease = leases.get(leaseName);
186
187
188
189 if (lease == null ) {
190 throw new LeaseException("lease '" + leaseName +
191 "' does not exist or has already expired");
192 }
193 lease.resetExpirationTime();
194 }
195
196
197
198
199
200
201 public void cancelLease(final String leaseName) throws LeaseException {
202 removeLease(leaseName);
203 }
204
205
206
207
208
209
210
211
212
213
214 Lease removeLease(final String leaseName) throws LeaseException {
215 Lease lease = leases.remove(leaseName);
216 if (lease == null) {
217 throw new LeaseException("lease '" + leaseName + "' does not exist");
218 }
219 return lease;
220 }
221
222
223
224
225
226 @SuppressWarnings("serial")
227 public static class LeaseStillHeldException extends IOException {
228 private final String leaseName;
229
230
231
232
233 public LeaseStillHeldException(final String name) {
234 this.leaseName = name;
235 }
236
237
238 public String getName() {
239 return this.leaseName;
240 }
241 }
242
243
244 static class Lease implements Delayed {
245 private final String leaseName;
246 private final LeaseListener listener;
247 private int leaseTimeoutPeriod;
248 private long expirationTime;
249
250 Lease(final String leaseName, int leaseTimeoutPeriod, LeaseListener listener) {
251 this.leaseName = leaseName;
252 this.listener = listener;
253 this.leaseTimeoutPeriod = leaseTimeoutPeriod;
254 this.expirationTime = 0;
255 }
256
257
258 public String getLeaseName() {
259 return leaseName;
260 }
261
262
263 public LeaseListener getListener() {
264 return this.listener;
265 }
266
267 @Override
268 public boolean equals(Object obj) {
269 if (this == obj) {
270 return true;
271 }
272 if (obj == null) {
273 return false;
274 }
275 if (getClass() != obj.getClass()) {
276 return false;
277 }
278 return this.hashCode() == obj.hashCode();
279 }
280
281 @Override
282 public int hashCode() {
283 return this.leaseName.hashCode();
284 }
285
286 public long getDelay(TimeUnit unit) {
287 return unit.convert(this.expirationTime - System.currentTimeMillis(),
288 TimeUnit.MILLISECONDS);
289 }
290
291 public int compareTo(Delayed o) {
292 long delta = this.getDelay(TimeUnit.MILLISECONDS) -
293 o.getDelay(TimeUnit.MILLISECONDS);
294
295 return this.equals(o) ? 0 : (delta > 0 ? 1 : -1);
296 }
297
298
299
300
301 public void resetExpirationTime() {
302 this.expirationTime = System.currentTimeMillis() + this.leaseTimeoutPeriod;
303 }
304 }
305 }