You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by jv...@apache.org on 2012/11/07 13:02:09 UTC

svn commit: r1406579 - in /mina/mina/trunk/core/src: main/java/org/apache/mina/service/idlechecker/ main/java/org/apache/mina/transport/nio/ test/java/org/apache/mina/ test/java/org/apache/mina/transport/tcp/

Author: jvermillard
Date: Wed Nov  7 12:02:09 2012
New Revision: 1406579

URL: http://svn.apache.org/viewvc?rev=1406579&view=rev
Log:
now idle checker use its own thread

Added:
    mina/mina/trunk/core/src/test/java/org/apache/mina/transport/tcp/IdleTcpServerTest.java
Removed:
    mina/mina/trunk/core/src/test/java/org/apache/mina/AllTests.java
Modified:
    mina/mina/trunk/core/src/main/java/org/apache/mina/service/idlechecker/IdleChecker.java
    mina/mina/trunk/core/src/main/java/org/apache/mina/service/idlechecker/IndexedIdleChecker.java
    mina/mina/trunk/core/src/main/java/org/apache/mina/transport/nio/FixedSelectorLoopPool.java
    mina/mina/trunk/core/src/main/java/org/apache/mina/transport/nio/NioSelectorLoop.java
    mina/mina/trunk/core/src/main/java/org/apache/mina/transport/nio/NioTcpServer.java

Modified: mina/mina/trunk/core/src/main/java/org/apache/mina/service/idlechecker/IdleChecker.java
URL: http://svn.apache.org/viewvc/mina/mina/trunk/core/src/main/java/org/apache/mina/service/idlechecker/IdleChecker.java?rev=1406579&r1=1406578&r2=1406579&view=diff
==============================================================================
--- mina/mina/trunk/core/src/main/java/org/apache/mina/service/idlechecker/IdleChecker.java (original)
+++ mina/mina/trunk/core/src/main/java/org/apache/mina/service/idlechecker/IdleChecker.java Wed Nov  7 12:02:09 2012
@@ -21,7 +21,7 @@ package org.apache.mina.service.idlechec
 import org.apache.mina.session.AbstractIoSession;
 
 /**
- * Utility for checking detecting idle sessions. 
+ * Utility for checking detecting idle sessions.
  * 
  * @author <a href="http://mina.apache.org">Apache MINA Project</a>
  */
@@ -29,6 +29,7 @@ public interface IdleChecker {
 
     /**
      * Inform the IdleCheker a session have a write event
+     * 
      * @param session the session with the write event
      * @param time the data in ms (unix time) of the event
      */
@@ -36,6 +37,7 @@ public interface IdleChecker {
 
     /**
      * Inform the IdleCheker a session have a read event
+     * 
      * @param session the session with the read event
      * @param time the data in ms (unix time) of the event
      */
@@ -49,4 +51,13 @@ public interface IdleChecker {
      */
     int processIdleSession(long time);
 
+    /**
+     * Start the idle checker inner threads
+     */
+    void start();
+
+    /**
+     * Stop the idle checker.
+     */
+    void destroy();
 }

Modified: mina/mina/trunk/core/src/main/java/org/apache/mina/service/idlechecker/IndexedIdleChecker.java
URL: http://svn.apache.org/viewvc/mina/mina/trunk/core/src/main/java/org/apache/mina/service/idlechecker/IndexedIdleChecker.java?rev=1406579&r1=1406578&r2=1406579&view=diff
==============================================================================
--- mina/mina/trunk/core/src/main/java/org/apache/mina/service/idlechecker/IndexedIdleChecker.java (original)
+++ mina/mina/trunk/core/src/main/java/org/apache/mina/service/idlechecker/IndexedIdleChecker.java Wed Nov  7 12:02:09 2012
@@ -18,8 +18,9 @@
  */
 package org.apache.mina.service.idlechecker;
 
-import java.util.HashSet;
+import java.util.Collections;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.mina.api.IdleStatus;
 import org.apache.mina.session.AbstractIoSession;
@@ -36,6 +37,7 @@ import org.slf4j.LoggerFactory;
  * <li>we round it at the next second</li>
  * <li>we store a reference to this session in a circular buffer like :</li>
  * </ul>
+ * 
  * <pre>
  * 
  *               +--- Current time
@@ -48,9 +50,9 @@ import org.slf4j.LoggerFactory;
  *               |   +--> { S2, S7, S12...} (sessions that will TO in one second)
  *               +------> { S5, S6, S8...} (sessions that are idle for the maximum delay of 1 hour )
  * </pre>
- *
- *The maximum idle itme is one hour.
- *
+ * 
+ * The maximum idle itme is one hour.
+ * 
  * @author <a href="http://mina.apache.org">Apache MINA Project</a>
  */
 public class IndexedIdleChecker implements IdleChecker {
@@ -67,37 +69,68 @@ public class IndexedIdleChecker implemen
     private static final AttributeKey<Integer> WRITE_IDLE_INDEX = AttributeKey.createKey(Integer.class,
             "idle.write.index");
 
-    private long lastCheckTime = 0L;
+    private long lastCheckTimeMs = 0L;
 
     @SuppressWarnings("unchecked")
-    private Set<AbstractIoSession>[] readIdleSessionIndex = new Set[MAX_IDLE_TIME_IN_SEC];
+    private final Set<AbstractIoSession>[] readIdleSessionIndex = new Set[MAX_IDLE_TIME_IN_SEC];
 
     @SuppressWarnings("unchecked")
-    private Set<AbstractIoSession>[] writeIdleSessionIndex = new Set[MAX_IDLE_TIME_IN_SEC];
+    private final Set<AbstractIoSession>[] writeIdleSessionIndex = new Set[MAX_IDLE_TIME_IN_SEC];
+
+    private final int granularityInMs = 1000;
+
+    private final Worker worker = new Worker();
+
+    private volatile boolean running = true;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void start() {
+        worker.start();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void destroy() {
+        running = false;
+        try {
+            // interrupt the sleep
+            worker.interrupt();
+            // wait for worker to stop
+            worker.join();
+        } catch (final InterruptedException e) {
+            // interrupted, we don't care much
+        }
+    }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public void sessionRead(AbstractIoSession session, long timeInMs) {
+    public void sessionRead(final AbstractIoSession session, final long timeInMs) {
         LOG.debug("session read event, compute idle index of session {}", session);
 
         // remove from the old index position
-        Integer oldIndex = session.getAttribute(READ_IDLE_INDEX);
+        final Integer oldIndex = session.getAttribute(READ_IDLE_INDEX);
         if (oldIndex != null && readIdleSessionIndex[oldIndex] != null) {
             LOG.debug("remove for old index {}", oldIndex);
             readIdleSessionIndex[oldIndex].remove(session);
         }
 
-        long idleTimeInMs = session.getConfig().getIdleTimeInMillis(IdleStatus.READ_IDLE);
+        final long idleTimeInMs = session.getConfig().getIdleTimeInMillis(IdleStatus.READ_IDLE);
         // is idle enabled ?
         if (idleTimeInMs <= 0L) {
             LOG.debug("no read idle configuration");
         } else {
-            int nextIdleTimeInSeconds = (int) ((timeInMs + idleTimeInMs) / 1000L) + 1;
-            int index = nextIdleTimeInSeconds % MAX_IDLE_TIME_IN_SEC;
+            final int nextIdleTimeInSeconds = (int) ((timeInMs + idleTimeInMs) / 1000L) + 1;
+            final int index = nextIdleTimeInSeconds % MAX_IDLE_TIME_IN_SEC;
             if (readIdleSessionIndex[index] == null) {
-                readIdleSessionIndex[index] = new HashSet<AbstractIoSession>();
+                readIdleSessionIndex[index] = Collections
+                        .newSetFromMap(new ConcurrentHashMap<AbstractIoSession, Boolean>());
             }
 
             LOG.debug("marking session {} idle for index {}", session, index);
@@ -110,25 +143,26 @@ public class IndexedIdleChecker implemen
      * {@inheritDoc}
      */
     @Override
-    public void sessionWritten(AbstractIoSession session, long timeInMs) {
+    public void sessionWritten(final AbstractIoSession session, final long timeInMs) {
         LOG.debug("session write event, compute idle index of session {}", session);
 
         // remove from the old index position
-        Integer oldIndex = session.getAttribute(WRITE_IDLE_INDEX);
+        final Integer oldIndex = session.getAttribute(WRITE_IDLE_INDEX);
         if (oldIndex != null && writeIdleSessionIndex[oldIndex] != null) {
             LOG.debug("remove for old index {}", oldIndex);
             writeIdleSessionIndex[oldIndex].remove(session);
         }
 
-        long idleTimeInMs = session.getConfig().getIdleTimeInMillis(IdleStatus.WRITE_IDLE);
+        final long idleTimeInMs = session.getConfig().getIdleTimeInMillis(IdleStatus.WRITE_IDLE);
         // is idle enabled ?
         if (idleTimeInMs <= 0L) {
             LOG.debug("no write idle configuration");
         } else {
-            int nextIdleTimeInSeconds = (int) ((timeInMs + idleTimeInMs) / 1000L) + 1;
-            int index = nextIdleTimeInSeconds % MAX_IDLE_TIME_IN_SEC;
+            final int nextIdleTimeInSeconds = (int) ((timeInMs + idleTimeInMs) / 1000L) + 1;
+            final int index = nextIdleTimeInSeconds % MAX_IDLE_TIME_IN_SEC;
             if (writeIdleSessionIndex[index] == null) {
-                writeIdleSessionIndex[index] = new HashSet<AbstractIoSession>();
+                writeIdleSessionIndex[index] = Collections
+                        .newSetFromMap(new ConcurrentHashMap<AbstractIoSession, Boolean>());
             }
 
             writeIdleSessionIndex[index].add(session);
@@ -140,13 +174,13 @@ public class IndexedIdleChecker implemen
      * {@inheritDoc}
      */
     @Override
-    public int processIdleSession(long time) {
+    public int processIdleSession(final long timeMs) {
         int counter = 0;
-        long delta = time - lastCheckTime;
+        final long delta = timeMs - lastCheckTimeMs;
 
         if (LOG.isDebugEnabled()) {
-            LOG.debug("checking idle time, last = {}, now = {}, delta = {}",
-                    new Object[] { lastCheckTime, time, delta });
+            LOG.debug("checking idle time, last = {}, now = {}, delta = {}", new Object[] { lastCheckTimeMs, timeMs,
+                                    delta });
         }
 
         if (delta < 1000) {
@@ -154,8 +188,9 @@ public class IndexedIdleChecker implemen
             return 0;
         }
 
-        int startIdx = ((int) (Math.max(lastCheckTime, time - MAX_IDLE_TIME_IN_MS + 1) / 1000L)) % MAX_IDLE_TIME_IN_SEC;
-        int endIdx = ((int) (time / 1000L)) % MAX_IDLE_TIME_IN_SEC;
+        final int startIdx = ((int) (Math.max(lastCheckTimeMs, timeMs - MAX_IDLE_TIME_IN_MS + 1) / 1000L))
+                % MAX_IDLE_TIME_IN_SEC;
+        final int endIdx = ((int) (timeMs / 1000L)) % MAX_IDLE_TIME_IN_SEC;
 
         LOG.debug("scaning from index {} to index {}", startIdx, endIdx);
 
@@ -170,20 +205,20 @@ public class IndexedIdleChecker implemen
         } while (index != endIdx);
 
         // save last check time for next call
-        lastCheckTime = time;
+        lastCheckTimeMs = timeMs;
         LOG.debug("detected {} idleing sessions", counter);
         return counter;
     }
 
-    private int processIndex(Set<AbstractIoSession>[] indexByTime, int position, IdleStatus status) {
-        Set<AbstractIoSession> sessions = indexByTime[position];
+    private int processIndex(final Set<AbstractIoSession>[] indexByTime, final int position, final IdleStatus status) {
+        final Set<AbstractIoSession> sessions = indexByTime[position];
         if (sessions == null) {
             return 0;
         }
 
         int counter = 0;
 
-        for (AbstractIoSession idleSession : sessions) {
+        for (final AbstractIoSession idleSession : sessions) {
             idleSession.setAttribute(status == IdleStatus.READ_IDLE ? READ_IDLE_INDEX : WRITE_IDLE_INDEX, null);
             // check if idle detection wasn't disabled since the index update
             if (idleSession.getConfig().getIdleTimeInMillis(status) > 0) {
@@ -191,8 +226,31 @@ public class IndexedIdleChecker implemen
             }
             counter++;
         }
-        // clear the processed index entry 
+        // clear the processed index entry
         indexByTime[position] = null;
         return counter;
     }
+
+    /**
+     * Thread in charge of checking the idleing sessions and fire events
+     */
+    private class Worker extends Thread {
+
+        public Worker() {
+            super("IdleChecker");
+            setDaemon(true);
+        }
+
+        @Override
+        public void run() {
+            while (running) {
+                try {
+                    sleep(granularityInMs);
+                    processIdleSession(System.currentTimeMillis());
+                } catch (final InterruptedException e) {
+                    break;
+                }
+            }
+        }
+    }
 }

Modified: mina/mina/trunk/core/src/main/java/org/apache/mina/transport/nio/FixedSelectorLoopPool.java
URL: http://svn.apache.org/viewvc/mina/mina/trunk/core/src/main/java/org/apache/mina/transport/nio/FixedSelectorLoopPool.java?rev=1406579&r1=1406578&r2=1406579&view=diff
==============================================================================
--- mina/mina/trunk/core/src/main/java/org/apache/mina/transport/nio/FixedSelectorLoopPool.java (original)
+++ mina/mina/trunk/core/src/main/java/org/apache/mina/transport/nio/FixedSelectorLoopPool.java Wed Nov  7 12:02:09 2012
@@ -20,7 +20,6 @@ package org.apache.mina.transport.nio;
 
 import java.util.concurrent.atomic.AtomicInteger;
 
-
 /**
  * A fixed size pool of {@link SelectorLoop}.
  * 
@@ -29,20 +28,21 @@ import java.util.concurrent.atomic.Atomi
 public class FixedSelectorLoopPool implements SelectorLoopPool {
 
     // the pool of selector loop
-    private SelectorLoop[] pool;
-    
+    private final SelectorLoop[] pool;
+
     // the index of the next selector loop to be served
-    private AtomicInteger nextIndex = new AtomicInteger();
-    
+    private final AtomicInteger nextIndex = new AtomicInteger();
+
     /**
      * Create a pool of "size" {@link SelectorLoop}
+     * 
      * @param size
      */
-    public FixedSelectorLoopPool(int size) {
-        
+    public FixedSelectorLoopPool(final int size) {
+
         pool = new SelectorLoop[size];
-        for(int i=0;i<size;i++) {
-            pool[i] = new NioSelectorLoop();
+        for (int i = 0; i < size; i++) {
+            pool[i] = new NioSelectorLoop("I/O", i);
         }
     }
 

Modified: mina/mina/trunk/core/src/main/java/org/apache/mina/transport/nio/NioSelectorLoop.java
URL: http://svn.apache.org/viewvc/mina/mina/trunk/core/src/main/java/org/apache/mina/transport/nio/NioSelectorLoop.java?rev=1406579&r1=1406578&r2=1406579&view=diff
==============================================================================
--- mina/mina/trunk/core/src/main/java/org/apache/mina/transport/nio/NioSelectorLoop.java (original)
+++ mina/mina/trunk/core/src/main/java/org/apache/mina/transport/nio/NioSelectorLoop.java Wed Nov  7 12:02:09 2012
@@ -25,6 +25,7 @@ import java.nio.channels.ClosedChannelEx
 import java.nio.channels.SelectableChannel;
 import java.nio.channels.SelectionKey;
 import java.nio.channels.Selector;
+import java.util.Iterator;
 
 import org.apache.mina.api.RuntimeIoException;
 import org.slf4j.Logger;
@@ -35,11 +36,10 @@ import org.slf4j.LoggerFactory;
  */
 public class NioSelectorLoop implements SelectorLoop {
 
-    private static final Logger LOGGER = LoggerFactory.getLogger(NioSelectorLoop.class);
+    private final Logger logger;
 
     /**
-     * A timeout used for the select, as we need to get out to deal with idle
-     * sessions
+     * A timeout used for the select, as we need to get out to deal with idle sessions
      */
     private static final long SELECT_TIMEOUT = 1000L;
 
@@ -47,19 +47,22 @@ public class NioSelectorLoop implements 
     private Selector selector;
 
     /** the worker thread in charge of polling the selector */
-    private final SelectorWorker worker = new SelectorWorker();
+    private final SelectorWorker worker;
 
-    /**  the number of service using this selector */
+    /** the number of service using this selector */
     private int serviceCount = 0;
 
     /** Read buffer for all the incoming bytes (default to 64Kb) */
     private final ByteBuffer readBuffer = ByteBuffer.allocate(64 * 1024);
 
-    public NioSelectorLoop() {
+    public NioSelectorLoop(final String prefix, final int index) {
+        logger = LoggerFactory.getLogger(NioSelectorLoop.class.getName() + ":" + prefix + "-" + index);
+        worker = new SelectorWorker(prefix, index);
+
         try {
             selector = Selector.open();
-        } catch (IOException ioe) {
-            LOGGER.error("Impossible to open a new NIO selector, O/S is out of file descriptor ?");
+        } catch (final IOException ioe) {
+            logger.error("Impossible to open a new NIO selector, O/S is out of file descriptor ?");
             throw new RuntimeIoException(ioe);
         }
     }
@@ -69,9 +72,9 @@ public class NioSelectorLoop implements 
      */
     @Override
     public void register(final boolean accept, final boolean read, final boolean write,
-            final SelectorListener listener, SelectableChannel channel) {
-        LOGGER.debug("adding to registration queue : {} for accept : {}, read : {}, write : {}", new Object[] {
-                listener, accept, read, write });
+            final SelectorListener listener, final SelectableChannel channel) {
+        logger.debug("registering : {} for accept : {}, read : {}, write : {}", new Object[] { listener, accept, read,
+                                write });
         int ops = 0;
         if (accept) {
             ops |= SelectionKey.OP_ACCEPT;
@@ -84,8 +87,8 @@ public class NioSelectorLoop implements 
         }
         try {
             channel.register(selector, ops, listener);
-        } catch (ClosedChannelException e) {
-            LOGGER.error("Trying to register an already closed channel : ", e);
+        } catch (final ClosedChannelException e) {
+            logger.error("Trying to register an already closed channel : ", e);
         }
     }
 
@@ -94,13 +97,13 @@ public class NioSelectorLoop implements 
      */
     @Override
     public void modifyRegistration(final boolean accept, final boolean read, final boolean write,
-            final SelectorListener listener, SelectableChannel channel) {
-        LOGGER.debug("modifying registration : {} for accept : {}, read : {}, write : {}", new Object[] { listener,
-                accept, read, write });
+            final SelectorListener listener, final SelectableChannel channel) {
+        logger.debug("modifying registration : {} for accept : {}, read : {}, write : {}", new Object[] { listener,
+                                accept, read, write });
 
-        SelectionKey key = channel.keyFor(selector);
+        final SelectionKey key = channel.keyFor(selector);
         if (key == null) {
-            LOGGER.error("Trying to modify the registration of a not registered channel");
+            logger.error("Trying to modify the registration of a not registered channel");
             return;
         }
 
@@ -121,11 +124,11 @@ public class NioSelectorLoop implements 
      * {@inheritDoc}
      */
     @Override
-    public void unregister(final SelectorListener listener, SelectableChannel channel) {
-        LOGGER.debug("unregistering : {}", listener);
-        SelectionKey key = channel.keyFor(selector);
+    public void unregister(final SelectorListener listener, final SelectableChannel channel) {
+        logger.debug("unregistering : {}", listener);
+        final SelectionKey key = channel.keyFor(selector);
         if (key == null) {
-            LOGGER.error("Trying to modify the registration of a not registered channel");
+            logger.error("Trying to modify the registration of a not registered channel");
             return;
         }
         key.cancel();
@@ -138,7 +141,7 @@ public class NioSelectorLoop implements 
     @Override
     public synchronized void incrementServiceCount() {
         serviceCount++;
-        LOGGER.debug("service count: {}", serviceCount);
+        logger.debug("service count: {}", serviceCount);
         if (serviceCount == 1) {
             worker.start();
         }
@@ -150,52 +153,60 @@ public class NioSelectorLoop implements 
     @Override
     public synchronized void decrementServiceCount() {
         serviceCount--;
-        LOGGER.debug("service count: {}", serviceCount);
+        logger.debug("service count: {}", serviceCount);
         if (serviceCount < 0) {
-            LOGGER.error("service count should not be negative : bug ?");
+            logger.error("service count should not be negative : bug ?");
         }
     }
 
     /**
-     * The worker processing incoming session creation, session destruction
-     * requests, session write and reads. It will also bind new servers.
+     * The worker processing incoming session creation, session destruction requests, session write and reads. It will
+     * also bind new servers.
      */
     private class SelectorWorker extends Thread {
 
+        public SelectorWorker(final String prefix, final int index) {
+            super("SelectorWorker " + prefix + "-" + index);
+        }
+
         @Override
         public void run() {
             if (selector == null) {
-                LOGGER.debug("opening a new selector");
+                logger.debug("opening a new selector");
 
                 try {
                     selector = Selector.open();
-                } catch (IOException e) {
-                    LOGGER.error("IOException while opening a new Selector", e);
+                } catch (final IOException e) {
+                    logger.error("IOException while opening a new Selector", e);
                 }
             }
 
             for (;;) {
                 try {
-                    LOGGER.debug("selecting...");
-                    int readyCount = selector.select(SELECT_TIMEOUT);
-                    LOGGER.debug("... done selecting : {}", readyCount);
+                    logger.debug("selecting...");
+                    final int readyCount = selector.select();
+                    logger.debug("... done selecting : {} events", readyCount);
                     if (readyCount > 0) {
-                        for (SelectionKey key : selector.selectedKeys()) {
-                            SelectorListener listener = (SelectorListener) key.attachment();
+                        final Iterator<SelectionKey> it = selector.selectedKeys().iterator();
+
+                        while (it.hasNext()) {
+                            final SelectionKey key = it.next();
+                            final SelectorListener listener = (SelectorListener) key.attachment();
                             listener.ready(key.isAcceptable(), key.isReadable(), key.isReadable() ? readBuffer : null,
                                     key.isWritable());
+                            it.remove();
                         }
                     }
 
-                } catch (Exception e) {
-                    LOGGER.error("Unexpected exception : ", e);
+                } catch (final Exception e) {
+                    logger.error("Unexpected exception : ", e);
                 }
 
                 // stop the worker if needed (no more service)
                 synchronized (NioSelectorLoop.this) {
-                    LOGGER.debug("remaing {} service", serviceCount);
+                    logger.debug("remaing {} service", serviceCount);
                     if (serviceCount <= 0) {
-                        LOGGER.debug("stop the worker");
+                        logger.debug("stop the worker");
                         break;
                     }
                 }

Modified: mina/mina/trunk/core/src/main/java/org/apache/mina/transport/nio/NioTcpServer.java
URL: http://svn.apache.org/viewvc/mina/mina/trunk/core/src/main/java/org/apache/mina/transport/nio/NioTcpServer.java?rev=1406579&r1=1406578&r2=1406579&view=diff
==============================================================================
--- mina/mina/trunk/core/src/main/java/org/apache/mina/transport/nio/NioTcpServer.java (original)
+++ mina/mina/trunk/core/src/main/java/org/apache/mina/transport/nio/NioTcpServer.java Wed Nov  7 12:02:09 2012
@@ -55,13 +55,14 @@ public class NioTcpServer extends Abstra
     // the server socket for accepting clients
     private ServerSocketChannel serverChannel = null;
 
-    private final IdleChecker idleChecker = new IndexedIdleChecker();
+    private IdleChecker idleChecker;
 
     /**
      * Create a TCP server with new selector pool of default size.
      */
     public NioTcpServer() {
-        this(new NioSelectorLoop(), new FixedSelectorLoopPool(Runtime.getRuntime().availableProcessors() + 1));
+        this(new NioSelectorLoop("accept", 0),
+                new FixedSelectorLoopPool(Runtime.getRuntime().availableProcessors() + 1));
     }
 
     /**
@@ -114,6 +115,9 @@ public class NioTcpServer extends Abstra
 
         acceptSelectorLoop.register(true, false, false, this, serverChannel);
 
+        idleChecker = new IndexedIdleChecker();
+        idleChecker.start();
+
         // it's the first address bound, let's fire the event
         this.fireServiceActivated();
 
@@ -147,6 +151,8 @@ public class NioTcpServer extends Abstra
 
         // will stop the acceptor processor if we are the last service
         acceptSelectorLoop.decrementServiceCount();
+
+        idleChecker.destroy();
     }
 
     /**
@@ -254,10 +260,12 @@ public class NioTcpServer extends Abstra
         }
 
         // add the session to the queue for being added to the selector
-        readWriteSelectorLoop.register(false, true, false, session, socketChannel);
-        readWriteSelectorLoop.incrementServiceCount();
+        // readWriteSelectorLoop.register(false, true, false, session, socketChannel);
+        // readWriteSelectorLoop.incrementServiceCount();
         session.processSessionOpened();
         session.setConnected();
+        idleChecker.sessionRead(session, System.currentTimeMillis());
+        idleChecker.sessionWritten(session, System.currentTimeMillis());
     }
 
 }
\ No newline at end of file

Added: mina/mina/trunk/core/src/test/java/org/apache/mina/transport/tcp/IdleTcpServerTest.java
URL: http://svn.apache.org/viewvc/mina/mina/trunk/core/src/test/java/org/apache/mina/transport/tcp/IdleTcpServerTest.java?rev=1406579&view=auto
==============================================================================
--- mina/mina/trunk/core/src/test/java/org/apache/mina/transport/tcp/IdleTcpServerTest.java (added)
+++ mina/mina/trunk/core/src/test/java/org/apache/mina/transport/tcp/IdleTcpServerTest.java Wed Nov  7 12:02:09 2012
@@ -0,0 +1,98 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *  
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License. 
+ *  
+ */
+package org.apache.mina.transport.tcp;
+
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.mina.api.AbstractIoFilter;
+import org.apache.mina.api.IdleStatus;
+import org.apache.mina.api.IoSession;
+import org.apache.mina.transport.nio.NioTcpServer;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * Run a TCP server and wait for idle events to be generated.
+ * 
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ */
+public class IdleTcpServerTest {
+
+    private static final int CLIENT_COUNT = 3;
+
+    @BeforeClass
+    public static void setup() {
+        // BasicConfigurator.configure();
+    }
+
+    @Test
+    public void readIdleTest() throws IOException {
+        final NioTcpServer server = new NioTcpServer();
+
+        final CountDownLatch idleLatch = new CountDownLatch(CLIENT_COUNT);
+
+        // 3 seconds idle time
+        server.getSessionConfig().setIdleTimeInMillis(IdleStatus.READ_IDLE, 3000);
+
+        // start the server
+        server.bind(new InetSocketAddress(0));
+
+        final int boundPort = server.getServerSocketChannel().socket().getLocalPort();
+        server.setFilters(new IdleHandler(idleLatch));
+
+        // fire the clients and let them idle
+        final Socket[] clients = new Socket[CLIENT_COUNT];
+
+        for (int i = 0; i < CLIENT_COUNT; i++) {
+            clients[i] = new Socket("127.0.0.1", boundPort);
+        }
+
+        try {
+            assertTrue("idle event missing ! ", idleLatch.await(4, TimeUnit.SECONDS));
+        } catch (final InterruptedException e) {
+            fail(e.getMessage());
+        }
+    }
+
+    private class IdleHandler extends AbstractIoFilter {
+
+        private final CountDownLatch latch;
+
+        public IdleHandler(final CountDownLatch latch) {
+            this.latch = latch;
+        }
+
+        @Override
+        public void sessionIdle(final IoSession session, final IdleStatus status) {
+            if (status == IdleStatus.READ_IDLE) {
+                // happy
+                latch.countDown();
+                session.close(true);
+            }
+        }
+    }
+}