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