@jfallows merged your pull request.. Thanks again1
 Awesome.  Thanks @jfallows!

Suspend-resume in AIO can cause a ReadPendingException 
When a read completes in AioSocketChannel and the newly read buffer is advertised via fireInboundBufferUpdated(), user code can trigger a ChannelHandlerContext.readable(false), followed by a ChannelHandlerContext.readable(true).

The ChannelHandlerContext.readable(true) causes a new AioSocketChannel.beginRead() call to occur, in this case immediately because we are on the event loop thread, then the fireInboundBufferUpdated() method returns, and the read completion handler calls AioSocketChannel.beginRead() again.

This second call to beginRead() causes the java.nio.channels.ReadPendingException.

transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java
transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java
diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java
index 817cbc0af71..16a44c6c773 100755
--- a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java
+++ b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java
@@ -54,6 +54,7 @@ private static AsynchronousSocketChannel newSocket(AsynchronousChannelGroup grou
     private boolean flushing;
 
     private final AtomicBoolean readSuspended = new AtomicBoolean();
+    private final AtomicBoolean readInProgress = new AtomicBoolean();
 
     private final Runnable readTask = new Runnable() {
         @Override
@@ -191,6 +192,11 @@ private void beginRead() {
             return;
         }
 
+        // prevent ReadPendingException
+        if (!readInProgress.compareAndSet(false, true)) {
+            return;
+        }
+
         ByteBuf byteBuf = pipeline().inboundByteBuffer();
         if (!byteBuf.readable()) {
             byteBuf.discardReadBytes();
@@ -297,6 +303,9 @@ protected void completed0(Integer result, AioSocketChannel channel) {
                     }
                 }
             } finally {
+                // see beginRead
+                channel.readInProgress.set(false);
+
                 if (read) {
                     if (!channel.readSuspended.get()) {
                         pipeline.fireInboundBufferUpdated();
