@Override
  public final void processNextInbound() throws Exception {

    int preBuffered = _inPreBuffered;
    Message msg;

    _inPreBuffered = 0; // reset the preBuffer .. incase of exception
    final int startPos = _inHdrLen + preBuffered;
    _inByteBuffer.limit(_inByteBuffer.capacity());
    _inByteBuffer.position(startPos);

    final int bytesRead = initialChannelRead(preBuffered, MIN_BYTES);
    if (bytesRead == 0) {
      _inPreBuffered = preBuffered;
      return;
    }

    // time starts from when we have read the full message off the socket
    if (_logStats) _decoder.setReceived(Utils.nanoTime());

    _inByteBuffer.position(_inHdrLen);

    final int maxIdx = _inHdrLen + bytesRead;
    _inByteBuffer.limit(maxIdx);

    if (_stopping) return;

    _inLogBuf.setLength(maxIdx);

    msg = decode(_inHdrLen, bytesRead);

    final int msgLen = _decoder.getLength();
    int extraBytes = bytesRead;

    if (msg != null) {

      extraBytes = bytesRead - msgLen;

      logInEvent(null);
      logInEventPojo(msg);

      // message decoded, shift left any extra bytes before invoke controller as that can throw
      // exception .. avoids extra try-finally
      invokeController(msg);
    } else {
      // no partial messages in UDP ... could also be duplicate in any case discard
      extraBytes = 0;
    }

    if (extraBytes > 0) {
      _inPreBuffered = extraBytes;
      final int nextMsgStart = _inHdrLen + msgLen;
      shiftInBufferLeft(extraBytes, nextMsgStart);
    }
  }