68
|
1 /*
|
|
2 Copyright (c) 2008 Franklin Schmidt <fschmidt@gmail.com>
|
|
3
|
|
4 Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5 of this software and associated documentation files (the "Software"), to deal
|
|
6 in the Software without restriction, including without limitation the rights
|
|
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8 copies of the Software, and to permit persons to whom the Software is
|
|
9 furnished to do so, subject to the following conditions:
|
|
10
|
|
11 The above copyright notice and this permission notice shall be included in
|
|
12 all copies or substantial portions of the Software.
|
|
13
|
|
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
20 THE SOFTWARE.
|
|
21 */
|
|
22
|
|
23 package fschmidt.util.locks;
|
|
24
|
|
25 import java.util.Set;
|
|
26 import java.util.HashSet;
|
|
27 import java.util.Collections;
|
|
28 import java.util.concurrent.TimeUnit;
|
|
29 import java.util.concurrent.locks.Lock;
|
|
30 import org.slf4j.Logger;
|
|
31 import org.slf4j.LoggerFactory;
|
|
32
|
|
33
|
|
34 public final class TimedLock extends ProxyLock {
|
|
35 private static final Logger logger = LoggerFactory.getLogger(TimedLock.class);
|
|
36
|
|
37 private static long timeoutDefault = 10L*60L*1000L; // 10 minutes
|
|
38 private static long waitTimeDefault = 60L*1000L; // 1 minutes
|
|
39
|
|
40 public static long getTimeoutDefault() {
|
|
41 return timeoutDefault;
|
|
42 }
|
|
43
|
|
44 public static void setTimeoutDefault(long timeoutDefault) {
|
|
45 TimedLock.timeoutDefault = timeoutDefault;
|
|
46 }
|
|
47
|
|
48 public static long getWaitTimeDefault() {
|
|
49 return waitTimeDefault;
|
|
50 }
|
|
51
|
|
52 public static void setWaitTimeDefault(long waitTimeDefault) {
|
|
53 TimedLock.waitTimeDefault = waitTimeDefault;
|
|
54 }
|
|
55
|
|
56 private static Set<TimedLock> locks = Collections.synchronizedSet(new HashSet<TimedLock>());
|
|
57 private int count = 0;
|
|
58 private final Object sync = new Object();
|
|
59 private long whenLocked;
|
|
60 private long whenChecked;
|
|
61 private long timeout = timeoutDefault;
|
|
62 private long waitTime = waitTimeDefault;
|
|
63 private Thread thread = null;
|
|
64
|
|
65 public TimedLock(Lock lock) {
|
|
66 super(lock);
|
|
67 }
|
|
68
|
|
69 public long getTimeout() {
|
|
70 return timeout;
|
|
71 }
|
|
72
|
|
73 public void setTimeout(long timeout) {
|
|
74 this.timeout = timeout;
|
|
75 }
|
|
76
|
|
77 public long getWaitTime() {
|
|
78 return waitTime;
|
|
79 }
|
|
80
|
|
81 public void setWaitTime(long waitTime) {
|
|
82 this.waitTime = waitTime;
|
|
83 }
|
|
84
|
|
85 private void locked() {
|
|
86 synchronized(sync) {
|
|
87 if( count++ == 0 ) {
|
|
88 whenLocked = System.currentTimeMillis();
|
|
89 whenChecked = whenLocked;
|
|
90 thread = Thread.currentThread();
|
|
91 if( !locks.add(this) )
|
|
92 throw new RuntimeException();
|
|
93 }
|
|
94 }
|
|
95 }
|
|
96
|
|
97 public boolean tryLock() {
|
|
98 if( !super.tryLock() )
|
|
99 return false;
|
|
100 locked();
|
|
101 return true;
|
|
102 }
|
|
103
|
|
104 public boolean tryLock(long time,TimeUnit unit)
|
|
105 throws InterruptedException
|
|
106 {
|
|
107 long t = TimeUnit.MILLISECONDS.convert(time,unit);
|
|
108 while( t > waitTime ) {
|
|
109 if( super.tryLock(waitTime,TimeUnit.MILLISECONDS) ) {
|
|
110 locked();
|
|
111 return true;
|
|
112 }
|
|
113 t -= waitTime;
|
|
114 check();
|
|
115 }
|
|
116 if( super.tryLock(t,TimeUnit.MILLISECONDS) ) {
|
|
117 locked();
|
|
118 return true;
|
|
119 }
|
|
120 return false;
|
|
121 }
|
|
122
|
|
123 public void lockInterruptibly()
|
|
124 throws InterruptedException
|
|
125 {
|
|
126 while(true) {
|
|
127 if( super.tryLock(waitTime,TimeUnit.MILLISECONDS) ) {
|
|
128 locked();
|
|
129 return;
|
|
130 }
|
|
131 check();
|
|
132 }
|
|
133 }
|
|
134
|
|
135 public void lock() {
|
|
136 try {
|
|
137 lockInterruptibly();
|
|
138 } catch(InterruptedException e) {
|
|
139 throw new RuntimeException(e);
|
|
140 }
|
|
141 }
|
|
142
|
|
143 public void unlock() {
|
|
144 synchronized(sync) {
|
|
145 if( --count == 0 ) {
|
|
146 if( !locks.remove(this) )
|
|
147 throw new RuntimeException();
|
|
148 thread = null;
|
|
149 }
|
|
150 }
|
|
151 super.unlock();
|
|
152 }
|
|
153
|
|
154 private void check() {
|
|
155 long now = System.currentTimeMillis();
|
|
156 long tooOld = now - timeout;
|
|
157 TimedLock[] a = locks.toArray(new TimedLock[0]);
|
|
158 for( int i=0; i<a.length; i++ ) {
|
|
159 TimedLock tl = a[i];
|
|
160 if( tl.whenChecked < tooOld ) {
|
|
161 StringBuilder buf = new StringBuilder();
|
|
162 buf.append("lock "+tl+" timed out, locked for "+(System.currentTimeMillis()-tl.whenLocked)+" millis");
|
|
163 buf.append("\n"+tl.thread);
|
|
164 for( StackTraceElement ste : tl.thread.getStackTrace() ) {
|
|
165 buf.append("\n\t"+ste);
|
|
166 }
|
|
167 logger.error(buf.toString());
|
|
168 tl.whenChecked = now;
|
|
169 }
|
|
170 }
|
|
171 }
|
|
172
|
|
173 }
|