Hi ! I tried to inject the benchmark we have in MINA 2 into MINA3, using the NioTcpClient Julien just added. It was easy to migrate the code, but now, I have some issues...
Basically I get a NPE when trying to manipulate the session selectionKey, which may become null in some case. This is typically a race condition, where this SelectionKey is deregistered from one selector (the Connect one) and registered on the IoLoop selector. Let me explain what's going on... I have a client that tries to write some data when teh session get opened : public void sessionOpened(IoSession session) { sendMessage(session, data); } There is nothing wrong here : it's perfectly elicit to write some data as soon as we are informed that the session is now connected. The problem is that, in the middle, we have *moved* the channel from one selector to the other, and in the middle of this process, we set the SelectionKey to null : In NioTcpSession : public void ready(final boolean accept, boolean connect, final boolean read, final ByteBuffer readBuffer, final boolean write) { if (connect) { // cancel current registration for connection selectionKey.cancel(); selectionKey = null; <<<<----------------------------- Here ! // Register for reading selectorLoop.register(false, false, true, false, this, channel, null); // and inform the IoHandler that the session is connected setConnected(); The setConnected() call will itself call the processSessionOpen(). If it goes faster than the processing of the selectorLoop.register() processing, then we will have a null selectionKey when we process the NioSelectorLoop.modifyRegistration() method : public void modifyRegistration(final boolean accept, final boolean read, final boolean write, final SelectorListener listener, final SelectableChannel channel) { final SelectionKey key = channel.keyFor(selector); if (key == null) { logger.error("Trying to modify the registration of a not registered channel"); The stack trace from the setConnected() call is : Daemon Thread [SelectorWorker connect-0] (Suspended) NioSelectorLoop.modifyRegistration(boolean, boolean, boolean, SelectorListener, SelectableChannel) line: 142 NioTcpSession.flushWriteQueue() line: 225 NioTcpSession(AbstractIoSession).enqueueWriteRequest(Object) line: 553 NioTcpSession(AbstractIoSession).enqueueFinalWriteMessage(Object) line: 868 NioTcpSession(AbstractIoSession).processMessageWriting(Object, IoFuture<Void>) line: 793 NioTcpSession(AbstractIoSession).doWriteWithFuture(Object, IoFuture<Void>) line: 523 NioTcpSession(AbstractIoSession).write(Object) line: 501 Mina3BenchmarkClient$1.sendMessage(IoSession, byte[]) line: 50 Mina3BenchmarkClient$1.sessionOpened(IoSession) line: 55 NioTcpSession(AbstractIoSession).processSessionOpen() line: 666 NioTcpSession.setConnected() line: 202 NioTcpSession.ready(boolean, boolean, boolean, ByteBuffer, boolean) line: 398 NioSelectorLoop$SelectorWorker.run() line: 209 Again, this is random and not too frequent, but I can assure that it actually *happens*. How could we avoid such a behavior ? I think we should wait for the selection key to be set to its new value here, which means we have to add some kind of synchronization between the two threads (yuk :/), or transfer the setConnected() call to the second thread (which is, IMO, the best thing to do). What about adding a call back that does that in the ready() call ? Like in : public void ready(final boolean accept, boolean connect, final boolean read, final ByteBuffer readBuffer, final boolean write) { if (connect) { // cancel current registration for connection selectionKey.cancel(); selectionKey = null; <<<<----------------------------- Here ! // Register for reading selectorLoop.register(false, false, true, false, this, channel, new RegistrationCallback() { @Override public void done(SelectionKey selectionKey) { setConnected(); } } ); Wdyt ? PS : I will commit the benchmark module shortly, so that anyone can play with the code and see what's going on, if it's not clear. Thanks ! -- Regards, Cordialement, Emmanuel Lécharny www.iktek.com