1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.mina.transport.socket.nio;
21
22 import java.io.IOException;
23 import java.nio.channels.SelectionKey;
24 import java.nio.channels.Selector;
25 import java.nio.channels.SocketChannel;
26 import java.util.Iterator;
27 import java.util.Set;
28
29 import org.apache.mina.common.ByteBuffer;
30 import org.apache.mina.common.ExceptionMonitor;
31 import org.apache.mina.common.IdleStatus;
32 import org.apache.mina.common.IoFilter.WriteRequest;
33 import org.apache.mina.common.WriteTimeoutException;
34 import org.apache.mina.util.NamePreservingRunnable;
35 import org.apache.mina.util.Queue;
36 import edu.emory.mathcs.backport.java.util.concurrent.Executor;
37
38
39
40
41
42
43
44 class SocketIoProcessor {
45 private final Object lock = new Object();
46
47 private final String threadName;
48
49 private final Executor executor;
50
51
52
53
54 private Selector selector;
55
56 private final Queue newSessions = new Queue();
57
58 private final Queue removingSessions = new Queue();
59
60 private final Queue flushingSessions = new Queue();
61
62 private final Queue trafficControllingSessions = new Queue();
63
64 private Worker worker;
65
66 private long lastIdleCheckTime = System.currentTimeMillis();
67
68 SocketIoProcessor(String threadName, Executor executor) {
69 this.threadName = threadName;
70 this.executor = executor;
71 }
72
73 void addNew(SocketSessionImpl session) throws IOException {
74 synchronized (newSessions) {
75 newSessions.push(session);
76 }
77
78 startupWorker();
79 }
80
81 void remove(SocketSessionImpl session) throws IOException {
82 scheduleRemove(session);
83 startupWorker();
84 }
85
86 private Selector getSelector() {
87 synchronized (lock) {
88 return this.selector;
89 }
90 }
91
92 private void startupWorker() throws IOException {
93 synchronized (lock) {
94 if (worker == null) {
95 selector = Selector.open();
96 worker = new Worker();
97 executor.execute(new NamePreservingRunnable(worker, threadName));
98 }
99 selector.wakeup();
100 }
101 }
102
103 void flush(SocketSessionImpl session) {
104 if (scheduleFlush(session)) {
105 Selector selector = getSelector();
106 if (selector != null) {
107 selector.wakeup();
108 }
109 }
110 }
111
112 void updateTrafficMask(SocketSessionImpl session) {
113 scheduleTrafficControl(session);
114 Selector selector = getSelector();
115 if (selector != null) {
116 selector.wakeup();
117 }
118 }
119
120 private void scheduleRemove(SocketSessionImpl session) {
121 synchronized (removingSessions) {
122 removingSessions.push(session);
123 }
124 }
125
126 private boolean scheduleFlush(SocketSessionImpl session) {
127 if (session.setScheduledForFlush(true)) {
128 synchronized (flushingSessions) {
129 flushingSessions.push(session);
130 }
131
132 return true;
133 }
134
135 return false;
136 }
137
138 private void scheduleTrafficControl(SocketSessionImpl session) {
139 synchronized (trafficControllingSessions) {
140 trafficControllingSessions.push(session);
141 }
142 }
143
144 private void doAddNew() {
145 if (newSessions.isEmpty())
146 return;
147
148 Selector selector = getSelector();
149 for (;;) {
150 SocketSessionImpl session;
151
152 synchronized (newSessions) {
153 session = (SocketSessionImpl) newSessions.pop();
154 }
155
156 if (session == null)
157 break;
158
159 SocketChannel ch = session.getChannel();
160 try {
161 ch.configureBlocking(false);
162 session.setSelectionKey(ch.register(selector,
163 SelectionKey.OP_READ, session));
164
165
166
167 session.getServiceListeners().fireSessionCreated(session);
168 } catch (IOException e) {
169
170
171 session.getFilterChain().fireExceptionCaught(session, e);
172 }
173 }
174 }
175
176 private void doRemove() {
177 if (removingSessions.isEmpty())
178 return;
179
180 for (;;) {
181 SocketSessionImpl session;
182
183 synchronized (removingSessions) {
184 session = (SocketSessionImpl) removingSessions.pop();
185 }
186
187 if (session == null)
188 break;
189
190 SocketChannel ch = session.getChannel();
191 SelectionKey key = session.getSelectionKey();
192
193
194 if (key == null) {
195 scheduleRemove(session);
196 break;
197 }
198
199 if (!key.isValid()) {
200 continue;
201 }
202
203 try {
204 key.cancel();
205 ch.close();
206 } catch (IOException e) {
207 session.getFilterChain().fireExceptionCaught(session, e);
208 } finally {
209 releaseWriteBuffers(session);
210 session.getServiceListeners().fireSessionDestroyed(session);
211 }
212 }
213 }
214
215 private void process(Set selectedKeys) {
216 Iterator it = selectedKeys.iterator();
217
218 while (it.hasNext()) {
219 SelectionKey key = (SelectionKey) it.next();
220 SocketSessionImpl session = (SocketSessionImpl) key.attachment();
221
222 if (key.isReadable() && session.getTrafficMask().isReadable()) {
223 read(session);
224 }
225
226 if (key.isWritable() && session.getTrafficMask().isWritable()) {
227 scheduleFlush(session);
228 }
229 }
230
231 selectedKeys.clear();
232 }
233
234 private void read(SocketSessionImpl session) {
235 ByteBuffer buf = ByteBuffer.allocate(session.getReadBufferSize());
236 SocketChannel ch = session.getChannel();
237
238 try {
239 int readBytes = 0;
240 int ret;
241
242 try {
243 while ((ret = ch.read(buf.buf())) > 0) {
244 readBytes += ret;
245 }
246 } finally {
247 buf.flip();
248 }
249
250 session.increaseReadBytes(readBytes);
251
252 if (readBytes > 0) {
253 session.getFilterChain().fireMessageReceived(session, buf);
254 buf = null;
255
256 if (readBytes * 2 < session.getReadBufferSize()) {
257 session.decreaseReadBufferSize();
258 } else if (readBytes == session.getReadBufferSize()) {
259 session.increaseReadBufferSize();
260 }
261 }
262 if (ret < 0) {
263 scheduleRemove(session);
264 }
265 } catch (Throwable e) {
266 if (e instanceof IOException)
267 scheduleRemove(session);
268 session.getFilterChain().fireExceptionCaught(session, e);
269 } finally {
270 if (buf != null)
271 buf.release();
272 }
273 }
274
275 private void notifyIdleness() {
276
277 long currentTime = System.currentTimeMillis();
278 if ((currentTime - lastIdleCheckTime) >= 1000) {
279 lastIdleCheckTime = currentTime;
280 Selector selector = getSelector();
281 Set keys = selector.keys();
282 if (keys != null) {
283 for (Iterator it = keys.iterator(); it.hasNext();) {
284 SelectionKey key = (SelectionKey) it.next();
285 SocketSessionImpl session = (SocketSessionImpl) key
286 .attachment();
287 notifyIdleness(session, currentTime);
288 }
289 }
290 }
291 }
292
293 private void notifyIdleness(SocketSessionImpl session, long currentTime) {
294 notifyIdleness0(session, currentTime, session
295 .getIdleTimeInMillis(IdleStatus.BOTH_IDLE),
296 IdleStatus.BOTH_IDLE, Math.max(session.getLastIoTime(), session
297 .getLastIdleTime(IdleStatus.BOTH_IDLE)));
298 notifyIdleness0(session, currentTime, session
299 .getIdleTimeInMillis(IdleStatus.READER_IDLE),
300 IdleStatus.READER_IDLE, Math.max(session.getLastReadTime(),
301 session.getLastIdleTime(IdleStatus.READER_IDLE)));
302 notifyIdleness0(session, currentTime, session
303 .getIdleTimeInMillis(IdleStatus.WRITER_IDLE),
304 IdleStatus.WRITER_IDLE, Math.max(session.getLastWriteTime(),
305 session.getLastIdleTime(IdleStatus.WRITER_IDLE)));
306
307 notifyWriteTimeout(session, currentTime, session
308 .getWriteTimeoutInMillis(), session.getLastWriteTime());
309 }
310
311 private void notifyIdleness0(SocketSessionImpl session, long currentTime,
312 long idleTime, IdleStatus status, long lastIoTime) {
313 if (idleTime > 0 && lastIoTime != 0
314 && (currentTime - lastIoTime) >= idleTime) {
315 session.increaseIdleCount(status);
316 session.getFilterChain().fireSessionIdle(session, status);
317 }
318 }
319
320 private void notifyWriteTimeout(SocketSessionImpl session,
321 long currentTime, long writeTimeout, long lastIoTime) {
322 SelectionKey key = session.getSelectionKey();
323 if (writeTimeout > 0 && (currentTime - lastIoTime) >= writeTimeout
324 && key != null && key.isValid()
325 && (key.interestOps() & SelectionKey.OP_WRITE) != 0) {
326 session.getFilterChain().fireExceptionCaught(session,
327 new WriteTimeoutException());
328 }
329 }
330
331 private void doFlush() {
332 if (flushingSessions.size() == 0)
333 return;
334
335 for (;;) {
336 SocketSessionImpl session;
337
338 synchronized (flushingSessions) {
339 session = (SocketSessionImpl) flushingSessions.pop();
340 }
341
342 if (session == null)
343 break;
344
345 session.setScheduledForFlush(false);
346
347 if (!session.isConnected()) {
348 releaseWriteBuffers(session);
349 continue;
350 }
351
352 SelectionKey key = session.getSelectionKey();
353
354
355 if (key == null) {
356 scheduleFlush(session);
357 break;
358 }
359
360
361 if (!key.isValid()) {
362 continue;
363 }
364
365 try {
366 boolean flushedAll = doFlush(session);
367 if (flushedAll && !session.getWriteRequestQueue().isEmpty() && !session.isScheduledForFlush()) {
368 scheduleFlush(session);
369 }
370 } catch (IOException e) {
371 scheduleRemove(session);
372 session.getFilterChain().fireExceptionCaught(session, e);
373 }
374 }
375 }
376
377 private void releaseWriteBuffers(SocketSessionImpl session) {
378 Queue writeRequestQueue = session.getWriteRequestQueue();
379 WriteRequest req;
380
381 if ((req = (WriteRequest) writeRequestQueue.pop()) != null) {
382 ByteBuffer buf = (ByteBuffer) req.getMessage();
383 try {
384 buf.release();
385 } catch (IllegalStateException e) {
386 session.getFilterChain().fireExceptionCaught(session, e);
387 } finally {
388
389
390 if (buf.hasRemaining()) {
391 req.getFuture().setWritten(false);
392 } else {
393 session.getFilterChain().fireMessageSent(session, req);
394 }
395 }
396
397
398 while ((req = (WriteRequest) writeRequestQueue.pop()) != null) {
399 try {
400 ((ByteBuffer) req.getMessage()).release();
401 } catch (IllegalStateException e) {
402 session.getFilterChain().fireExceptionCaught(session, e);
403 } finally {
404 req.getFuture().setWritten(false);
405 }
406 }
407 }
408 }
409
410 private boolean doFlush(SocketSessionImpl session) throws IOException {
411
412 SelectionKey key = session.getSelectionKey();
413 key.interestOps(key.interestOps() & (~SelectionKey.OP_WRITE));
414
415 SocketChannel ch = session.getChannel();
416 Queue writeRequestQueue = session.getWriteRequestQueue();
417
418 int writtenBytes = 0;
419 int maxWrittenBytes = ((SocketSessionConfig) session.getConfig()).getSendBufferSize() << 1;
420 try {
421 for (;;) {
422 WriteRequest req;
423
424 synchronized (writeRequestQueue) {
425 req = (WriteRequest) writeRequestQueue.first();
426 }
427
428 if (req == null)
429 break;
430
431 ByteBuffer buf = (ByteBuffer) req.getMessage();
432 if (buf.remaining() == 0) {
433 synchronized (writeRequestQueue) {
434 writeRequestQueue.pop();
435 }
436
437 buf.reset();
438
439 if (!buf.hasRemaining()) {
440 session.increaseWrittenMessages();
441 }
442
443 session.getFilterChain().fireMessageSent(session, req);
444 continue;
445 }
446
447 if (key.isWritable()) {
448 writtenBytes += ch.write(buf.buf());
449 }
450
451 if (buf.hasRemaining() || writtenBytes >= maxWrittenBytes) {
452
453 key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
454 return false;
455 }
456 }
457 } finally {
458 session.increaseWrittenBytes(writtenBytes);
459 }
460
461 return true;
462 }
463
464 private void doUpdateTrafficMask() {
465 if (trafficControllingSessions.isEmpty())
466 return;
467
468 for (;;) {
469 SocketSessionImpl session;
470
471 synchronized (trafficControllingSessions) {
472 session = (SocketSessionImpl) trafficControllingSessions.pop();
473 }
474
475 if (session == null)
476 break;
477
478 SelectionKey key = session.getSelectionKey();
479
480
481
482 if (key == null) {
483 scheduleTrafficControl(session);
484 break;
485 }
486
487 if (!key.isValid()) {
488 continue;
489 }
490
491
492
493 int ops = SelectionKey.OP_READ;
494 Queue writeRequestQueue = session.getWriteRequestQueue();
495 synchronized (writeRequestQueue) {
496 if (!writeRequestQueue.isEmpty()) {
497 ops |= SelectionKey.OP_WRITE;
498 }
499 }
500
501
502 int mask = session.getTrafficMask().getInterestOps();
503 key.interestOps(ops & mask);
504 }
505 }
506
507 private class Worker implements Runnable {
508 public void run() {
509 Selector selector = getSelector();
510 for (;;) {
511 try {
512 int nKeys = selector.select(1000);
513 doAddNew();
514 doUpdateTrafficMask();
515
516 if (nKeys > 0) {
517 process(selector.selectedKeys());
518 }
519
520 doFlush();
521 doRemove();
522 notifyIdleness();
523
524 if (selector.keys().isEmpty()) {
525 synchronized (lock) {
526 if (selector.keys().isEmpty()
527 && newSessions.isEmpty()) {
528 worker = null;
529
530 try {
531 selector.close();
532 } catch (IOException e) {
533 ExceptionMonitor.getInstance()
534 .exceptionCaught(e);
535 } finally {
536 selector = null;
537 }
538
539 break;
540 }
541 }
542 }
543 } catch (Throwable t) {
544 ExceptionMonitor.getInstance().exceptionCaught(t);
545
546 try {
547 Thread.sleep(1000);
548 } catch (InterruptedException e1) {
549 ExceptionMonitor.getInstance().exceptionCaught(e1);
550 }
551 }
552 }
553 }
554 }
555 }