@Override
  <A> Future<Void> implConnect(
      SocketAddress remote, A attachment, CompletionHandler<Void, ? super A> handler) {
    if (!isOpen()) {
      Throwable exc = new ClosedChannelException();
      if (handler == null) return CompletedFuture.withFailure(exc);
      Invoker.invoke(this, handler, attachment, null, exc);
      return null;
    }

    InetSocketAddress isa = Net.checkAddress(remote);

    // permission check
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) sm.checkConnect(isa.getAddress().getHostAddress(), isa.getPort());

    // check and update state
    // ConnectEx requires the socket to be bound to a local address
    IOException bindException = null;
    synchronized (stateLock) {
      if (state == ST_CONNECTED) throw new AlreadyConnectedException();
      if (state == ST_PENDING) throw new ConnectionPendingException();
      if (localAddress == null) {
        try {
          bind(new InetSocketAddress(0));
        } catch (IOException x) {
          bindException = x;
        }
      }
      if (bindException == null) state = ST_PENDING;
    }

    // handle bind failure
    if (bindException != null) {
      try {
        close();
      } catch (IOException ignore) {
      }
      if (handler == null) return CompletedFuture.withFailure(bindException);
      Invoker.invoke(this, handler, attachment, null, bindException);
      return null;
    }

    // setup task
    PendingFuture<Void, A> result = new PendingFuture<Void, A>(this, handler, attachment);
    ConnectTask task = new ConnectTask<A>(isa, result);
    result.setContext(task);

    // initiate I/O
    if (Iocp.supportsThreadAgnosticIo()) {
      task.run();
    } else {
      Invoker.invokeOnThreadInThreadPool(this, task);
    }
    return result;
  }
  @Override
  <V extends Number, A> Future<V> implWrite(
      boolean gatheringWrite,
      ByteBuffer src,
      ByteBuffer[] srcs,
      long timeout,
      TimeUnit unit,
      A attachment,
      CompletionHandler<V, ? super A> handler) {
    // setup task
    PendingFuture<V, A> result = new PendingFuture<V, A>(this, handler, attachment);
    ByteBuffer[] bufs;
    if (gatheringWrite) {
      bufs = srcs;
    } else {
      bufs = new ByteBuffer[1];
      bufs[0] = src;
    }
    final WriteTask writeTask = new WriteTask<V, A>(bufs, gatheringWrite, result);
    result.setContext(writeTask);

    // schedule timeout
    if (timeout > 0L) {
      Future<?> timeoutTask =
          iocp.schedule(
              new Runnable() {
                public void run() {
                  writeTask.timeout();
                }
              },
              timeout,
              unit);
      result.setTimeoutTask(timeoutTask);
    }

    // initiate I/O (can only be done from thread in thread pool)
    // initiate I/O
    if (Iocp.supportsThreadAgnosticIo()) {
      writeTask.run();
    } else {
      Invoker.invokeOnThreadInThreadPool(this, writeTask);
    }
    return result;
  }
  @Override
  <V extends Number, A> Future<V> implRead(
      boolean isScatteringRead,
      ByteBuffer dst,
      ByteBuffer[] dsts,
      long timeout,
      TimeUnit unit,
      A attachment,
      CompletionHandler<V, ? super A> handler) {
    // setup task
    PendingFuture<V, A> result = new PendingFuture<V, A>(this, handler, attachment);
    ByteBuffer[] bufs;
    if (isScatteringRead) {
      bufs = dsts;
    } else {
      bufs = new ByteBuffer[1];
      bufs[0] = dst;
    }
    final ReadTask readTask = new ReadTask<V, A>(bufs, isScatteringRead, result);
    result.setContext(readTask);

    // schedule timeout
    if (timeout > 0L) {
      Future<?> timeoutTask =
          iocp.schedule(
              new Runnable() {
                public void run() {
                  readTask.timeout();
                }
              },
              timeout,
              unit);
      result.setTimeoutTask(timeoutTask);
    }

    // initiate I/O
    if (Iocp.supportsThreadAgnosticIo()) {
      readTask.run();
    } else {
      Invoker.invokeOnThreadInThreadPool(this, readTask);
    }
    return result;
  }