1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.mina.core.future;
21
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.concurrent.TimeUnit;
25
26 import org.apache.mina.core.polling.AbstractPollingIoProcessor;
27 import org.apache.mina.core.service.IoProcessor;
28 import org.apache.mina.core.session.IoSession;
29 import org.apache.mina.util.ExceptionMonitor;
30
31
32
33
34
35
36
37
38
39 public class DefaultIoFuture implements IoFuture {
40
41
42 private static final long DEAD_LOCK_CHECK_INTERVAL = 5000L;
43
44
45 private final IoSession session;
46
47
48 private final Object lock;
49 private IoFutureListener<?> firstListener;
50 private List<IoFutureListener<?>> otherListeners;
51 private Object result;
52 private boolean ready;
53 private int waiters;
54
55
56
57
58
59
60 public DefaultIoFuture(IoSession session) {
61 this.session = session;
62 this.lock = this;
63 }
64
65
66
67
68 public IoSession getSession() {
69 return session;
70 }
71
72
73
74
75 @Deprecated
76 public void join() {
77 awaitUninterruptibly();
78 }
79
80
81
82
83 @Deprecated
84 public boolean join(long timeoutMillis) {
85 return awaitUninterruptibly(timeoutMillis);
86 }
87
88
89
90
91 public IoFuture await() throws InterruptedException {
92 synchronized (lock) {
93 while (!ready) {
94 waiters++;
95 try {
96
97
98
99 lock.wait(DEAD_LOCK_CHECK_INTERVAL);
100 } finally {
101 waiters--;
102 if (!ready) {
103 checkDeadLock();
104 }
105 }
106 }
107 }
108 return this;
109 }
110
111
112
113
114 public boolean await(long timeout, TimeUnit unit)
115 throws InterruptedException {
116 return await(unit.toMillis(timeout));
117 }
118
119
120
121
122 public boolean await(long timeoutMillis) throws InterruptedException {
123 return await0(timeoutMillis, true);
124 }
125
126
127
128
129 public IoFuture awaitUninterruptibly() {
130 try {
131 await0(Long.MAX_VALUE, false);
132 } catch ( InterruptedException ie) {
133
134 }
135
136 return this;
137 }
138
139
140
141
142 public boolean awaitUninterruptibly(long timeout, TimeUnit unit) {
143 return awaitUninterruptibly(unit.toMillis(timeout));
144 }
145
146
147
148
149 public boolean awaitUninterruptibly(long timeoutMillis) {
150 try {
151 return await0(timeoutMillis, false);
152 } catch (InterruptedException e) {
153 throw new InternalError();
154 }
155 }
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170 private boolean await0(long timeoutMillis, boolean interruptable) throws InterruptedException {
171 long endTime = System.currentTimeMillis() + timeoutMillis;
172
173 synchronized (lock) {
174 if (ready) {
175 return ready;
176 } else if (timeoutMillis <= 0) {
177 return ready;
178 }
179
180 waiters++;
181 try {
182 for (;;) {
183 try {
184 long timeOut = Math.min(timeoutMillis, DEAD_LOCK_CHECK_INTERVAL);
185 lock.wait(timeOut);
186 } catch (InterruptedException e) {
187 if (interruptable) {
188 throw e;
189 }
190 }
191
192 if (ready) {
193 return true;
194 } else {
195 if (endTime < System.currentTimeMillis()) {
196 return ready;
197 }
198 }
199 }
200 } finally {
201 waiters--;
202 if (!ready) {
203 checkDeadLock();
204 }
205 }
206 }
207 }
208
209
210
211
212
213
214
215 private void checkDeadLock() {
216
217 if (!(this instanceof CloseFuture || this instanceof WriteFuture ||
218 this instanceof ReadFuture || this instanceof ConnectFuture)) {
219 return;
220 }
221
222
223
224
225
226
227
228 StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
229
230
231 for (StackTraceElement s: stackTrace) {
232 if (AbstractPollingIoProcessor.class.getName().equals(s.getClassName())) {
233 IllegalStateException e = new IllegalStateException( "t" );
234 e.getStackTrace();
235 throw new IllegalStateException(
236 "DEAD LOCK: " + IoFuture.class.getSimpleName() +
237 ".await() was invoked from an I/O processor thread. " +
238 "Please use " + IoFutureListener.class.getSimpleName() +
239 " or configure a proper thread model alternatively.");
240 }
241 }
242
243
244 for (StackTraceElement s: stackTrace) {
245 try {
246 Class<?> cls = DefaultIoFuture.class.getClassLoader().loadClass(s.getClassName());
247 if (IoProcessor.class.isAssignableFrom(cls)) {
248 throw new IllegalStateException(
249 "DEAD LOCK: " + IoFuture.class.getSimpleName() +
250 ".await() was invoked from an I/O processor thread. " +
251 "Please use " + IoFutureListener.class.getSimpleName() +
252 " or configure a proper thread model alternatively.");
253 }
254 } catch (Exception cnfe) {
255
256 }
257 }
258 }
259
260
261
262
263 public boolean isDone() {
264 synchronized (lock) {
265 return ready;
266 }
267 }
268
269
270
271
272 public void setValue(Object newValue) {
273 synchronized (lock) {
274
275 if (ready) {
276 return;
277 }
278
279 result = newValue;
280 ready = true;
281 if (waiters > 0) {
282 lock.notifyAll();
283 }
284 }
285
286 notifyListeners();
287 }
288
289
290
291
292 protected Object getValue() {
293 synchronized (lock) {
294 return result;
295 }
296 }
297
298
299
300
301 public IoFuture addListener(IoFutureListener<?> listener) {
302 if (listener == null) {
303 throw new NullPointerException("listener");
304 }
305
306 boolean notifyNow = false;
307 synchronized (lock) {
308 if (ready) {
309 notifyNow = true;
310 } else {
311 if (firstListener == null) {
312 firstListener = listener;
313 } else {
314 if (otherListeners == null) {
315 otherListeners = new ArrayList<IoFutureListener<?>>(1);
316 }
317 otherListeners.add(listener);
318 }
319 }
320 }
321
322 if (notifyNow) {
323 notifyListener(listener);
324 }
325 return this;
326 }
327
328
329
330
331 public IoFuture removeListener(IoFutureListener<?> listener) {
332 if (listener == null) {
333 throw new NullPointerException("listener");
334 }
335
336 synchronized (lock) {
337 if (!ready) {
338 if (listener == firstListener) {
339 if (otherListeners != null && !otherListeners.isEmpty()) {
340 firstListener = otherListeners.remove(0);
341 } else {
342 firstListener = null;
343 }
344 } else if (otherListeners != null) {
345 otherListeners.remove(listener);
346 }
347 }
348 }
349
350 return this;
351 }
352
353 private void notifyListeners() {
354
355
356
357 if (firstListener != null) {
358 notifyListener(firstListener);
359 firstListener = null;
360
361 if (otherListeners != null) {
362 for (IoFutureListener<?> l : otherListeners) {
363 notifyListener(l);
364 }
365 otherListeners = null;
366 }
367 }
368 }
369
370 @SuppressWarnings("unchecked")
371 private void notifyListener(IoFutureListener l) {
372 try {
373 l.operationComplete(this);
374 } catch (Throwable t) {
375 ExceptionMonitor.getInstance().exceptionCaught(t);
376 }
377 }
378 }