--- java/org/apache/catalina/tribes/transport/nio/NioReceiver.java (revision 1496923) +++ java/org/apache/catalina/tribes/transport/nio/NioReceiver.java (working copy) @@ -27,6 +27,7 @@ import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; +import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.LinkedList; import java.util.Set; @@ -378,19 +379,49 @@ Selector selector = this.selector.getAndSet(null); if (selector==null) return; try { - Iterator it = selector.keys().iterator(); - // look at each key in the selected set - while (it.hasNext()) { - SelectionKey key = it.next(); - key.channel().close(); - key.attach(null); - key.cancel(); - } - }catch ( IOException ignore ){ - if (log.isWarnEnabled()) { - log.warn("Unable to cleanup on selector close.",ignore); - } - }catch ( ClosedSelectorException ignore){} + int closeAttempt = 1; + int maxCloseAttempts = 3; + boolean goodClose = false; + do { + try { + // attempt to close known keys/channels + Iterator it = selector.keys().iterator(); + + // in order to see a ConcurrentModificationException thrown + // it is helpful to sleep after obtaining the key set iterator + //try { + // System.err.println("sleeping..."); + // Thread.sleep(2000); + //} catch (InterruptedException ignore) {} + + while (it.hasNext()) { + SelectionKey key = it.next(); + try { + key.channel().close(); + } catch (IOException ignore) { + if (log.isWarnEnabled()) { + log.warn("Unable to cleanup on selector close.",ignore); + } + } + key.attach(null); + key.cancel(); + } + goodClose = true; + } catch (ConcurrentModificationException concurrentException) { + // This will occur when another thread is processing closed + // connections because when connections are closed, the + // associated channel is closed and the key is canceled. + // See concurrency notes for Selector. + if (log.isDebugEnabled()) { + log.debug("Caught exception during close attempt " + closeAttempt + " of " + + maxCloseAttempts + ". Retrying.", concurrentException); + } + try { Thread.sleep(100 * closeAttempt); } catch(InterruptedException ignore) {} + } + } while (!goodClose && closeAttempt++ < maxCloseAttempts); + }catch ( ClosedSelectorException ignore){ + ignore.printStackTrace(); + } selector.close(); }