public final Sink sink(final Sink sink) {
    return new Sink() {
      @Override public void write(Buffer source, long byteCount) throws IOException {
        boolean throwOnTimeout = false;
        enter();
        try {
          sink.write(source, byteCount);
          throwOnTimeout = true;
        } catch (IOException e) {
          throw exit(e);
        } finally {
          exit(throwOnTimeout);
        }
      }

      @Override public void flush() throws IOException {
        boolean throwOnTimeout = false;
        enter();
        try {
          sink.flush();
          throwOnTimeout = true;
        } catch (IOException e) {
          throw exit(e);
        } finally {
          exit(throwOnTimeout);
        }
      }

      @Override public void close() throws IOException {
        boolean throwOnTimeout = false;
        enter();
        try {
          sink.close();
          throwOnTimeout = true;
        } catch (IOException e) {
          throw exit(e);
        } finally {
          exit(throwOnTimeout);
        }
      }

      @Override public Timeout timeout() {
        return AsyncTimeout.this;
      }

      @Override public String toString() {
        return "AsyncTimeout.sink(" + sink + ")";
      }
    };
  }
@Test public void wrappedSinkTimesOut() throws Exception {
    Sink sink = new ForwardingSink(new Buffer()) {
      @Override public void write(Buffer source, long byteCount) throws IOException {
        try {
          Thread.sleep(500);
        } catch (InterruptedException e) {
          throw new AssertionError();
        }
      }
    };
    AsyncTimeout timeout = new AsyncTimeout();
    timeout.timeout(250, TimeUnit.MILLISECONDS);
    Sink timeoutSink = timeout.sink(sink);
    try {
      timeoutSink.write(null, 0);
      fail();
    } catch (InterruptedIOException expected) {
    }
  }