docs: IAsyncFuture multiIO(ByteBuffer[] buffers, long position, boolean isRead, boolean forceQueue, long bytesRequested, boolean useJITBuffer, VirtualConnection vci,
                           boolean asyncIO) {
  
          if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled()) {
              Tr.entry(tc, "multiIO(.," + position + "," + isRead + "," + forceQueue + "," + bytesRequested + "," + useJITBuffer + ",.," + asyncIO);
          }
  
          // Sanity check on the arguments
          if (buffers == null) {
              throw new IllegalArgumentException();
          }
  
          // Allocate a future representing this operation
          AsyncFuture theFuture = null;
          CompletionKey iocb = null;
  
          // set it everytime, faster than doing and "if (channelVCI == null)"
          this.channelVCI = vci;
  
          if (isRead) {
              theFuture = this.readFuture;
              iocb = this.readIOCB;
              if (asyncIO) {
                  iocb.setCallIdentifier(this.channelIndex | READ_INDEX);
              } else {
                  iocb.setCallIdentifier(this.channelIndex | SYNC_READ_INDEX);
              }
          } else {
              theFuture = this.writeFuture;
              iocb = this.writeIOCB;
              if (asyncIO) {
                  iocb.setCallIdentifier(this.channelIndex | WRITE_INDEX);
              } else {
                  iocb.setCallIdentifier(this.channelIndex | SYNC_WRITE_INDEX);
              }
          }
          theFuture.resetFuture();
          theFuture.setBuffers(buffers);
          if (!isOpen()) {
              theFuture.completed(new ClosedChannelException());
              if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled()) {
                  Tr.exit(tc, "multiIO", theFuture);
              }
              return theFuture;
          }
  
          // Prepare the buffer arrays
          int count = 0;
          for (int i = 0; i < buffers.length; i++) {
              // If the buffer array entry is null, stop processing the array
              if (buffers[i] == null)
                  break;
              count++;
          }
          if (count == 0) {
              // Should always have at least one buffer, if not, its an error.
              if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                  Tr.debug(tc, "no buffers passed on I/O call");
              }
              AsyncException exception = new AsyncException("no buffers passed on I/O call");
              FFDCFilter.processException(exception, getClass().getName(), "384", this);
              // We were asked to do no work
              theFuture.completed(exception);
              if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled()) {
                  Tr.exit(tc, "multiIO", theFuture);
              }
              return theFuture;
          } else if (count > iocb.getBufferCount()) {
              // too many buffers to fit in Completion Key, so expand it
              iocb.expandBuffers(count);
          }
  
          for (int i = 0; i < count; i++) {
              // Set the start address of the data in each buffer
              iocb.setBuffer((getBufAddress(buffers[i]) + buffers[i].position()),
                             buffers[i].remaining(), i);
          }
  
          // Make the OS call and see if it completes immediately or asyncronously.
          boolean pending = false;
          iocb.setBytesAffected(0);
          try {
              if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                  Tr.debug(tc, "Requesting IO from native for "
                               + " call id: " + Long.toHexString(iocb.getCallIdentifier())
                               + " channel id: " + iocb.getChannelIdentifier());
              }
  
              iocb.preNativePrep();
              if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                  Tr.debug(tc, "CompleteKey in AbstractAsyncChannel.multiIO before preNativePrep() call", iocb);
              }
              pending = provider.multiIO3(iocb.getAddress(), position, count, isRead, forceQueue, bytesRequested, useJITBuffer);
              iocb.postNativePrep();
              if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                  Tr.debug(tc, "CompleteKey in AbstractAsyncChannel.multiIO after postNativePrep() call", iocb);
              }
  
              // For pending operations, ensure that someone is listening...
              if (pending) {
                  if (vci.isInputStateTrackingOperational()) {
                      synchronized (vci.getLockObject()) {
                          if (asyncIO) {
                              // only allow closes of outstanding async reads/writes
                              if (isRead) {
                                  vci.setReadStatetoCloseAllowedNoSync();
                              } else {
                                  vci.setWriteStatetoCloseAllowedNoSync();
                              }
                          }
  
                          if (vci.getCloseWaiting()) {
                              vci.getLockObject().notify();
                          }
                      } // end-sync
                  }
              } else {
                  // For calls that complete immediately, complete the future
                  // according to the returned data
                  int rc = iocb.getReturnCode();
                  if (rc == 0) {
                      // Defend against erroneous combination of values returned from native
                      if (iocb.getBytesAffected() == 0 && bytesRequested > 0) {
                          IOException ioe = new IOException("Async IO operation failed, internal error");
                          if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                              Tr.debug(tc, "Error processing multiIO request, exception: " + ioe.getMessage());
                          }
                          theFuture.completed(ioe);
                      } else {
                          // The IO operation succeeded, set the buffer position to reflect the number of bytes read/written.
                          theFuture.completed(iocb.getBytesAffected());
                      }
                  } else {
                      // The operation completed immediately with an IO error.
                      theFuture.completed(AsyncLibrary.getIOException("Async IO operation failed (1), reason: ", rc));
                  }
              }
          } catch (AsyncException exception) {
              // If a problem occurs invoking the async call, then the operation
              // completes immediately, with an Exception.
              if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                  Tr.debug(tc, "Error processing multiIO request, exception: " + exception.getMessage());
              }
              FFDCFilter.processException(exception, getClass().getName(), "420", this);
              theFuture.completed(exception);
          }
          if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled()) {
              Tr.exit(tc, "multiIO", theFuture);
          }
          return theFuture;
      }