Before: changes were submitted, but if this happened in the selector thread, then they were executed immediately.
This lead to recursion: the run of a change submitted another change, which was run, which submitted a change, etc.
To avoid StackOverFlowException, a ForkInvoker was used, breaking the stack after 4 recursive calls.
The reason for this was to avoid to queue a change that could have been run in place, but costs probably it costs
more than what it saves.
Current: changes are now always queued. This avoids recursion and the need for a ForkInvoker, making the code simpler.
Instead of recursing we now iterate over the queue of changes.
Read interest and write interest can be setting concurrently, and they may cancel each other.
Replaced _interestOps with an AtomicInteger and checking whether the update succeeds,
otherwise it is reattempted.
DecryptedEndPoint does not perform any scheduling for idle timeouts, because those are done
by SelectChannelEndPoint that is the usual underlying EndPoint of DecryptedEndPoint.
In case of more nested EndPoint/Connection chains, EndPoints that delegate (such as
DecryptedEndPoint) should delegate the idle timeout to the underlying EndPoint.
When the output is shutdown, field _cannotAcceptMoreAppDataToFlush is set to true,
indicating that no more flush() calls are accepted.
However, a further call to flush() was reading _cannotAcceptMoreAppDataToFlush and
just returning false, leaving writes in pending state but without chance to be completed.
Now, if we detect _cannotAcceptMoreAppDataToFlush==true in flush(), we check
whether the output was shutdown; if it is the case, we throw an EofException to signal
to the application that it cannot write because the connection is already closed.
SslConnection should remain as passive as possible, with the next connection driving the activity.
If client connections need to drive the SSL handshake with an empty write, they should do it, and
not SslConnection.