/* * Decompiled with CFR 0.152. */ package okio; import java.io.IOException; import java.io.InterruptedIOException; import okio.Buffer; import okio.Sink; import okio.Source; import okio.Timeout; public class AsyncTimeout extends Timeout { private static AsyncTimeout head; private boolean inQueue; private AsyncTimeout next; private long timeoutAt; public final void enter() { if (this.inQueue) { throw new IllegalStateException("Unbalanced enter/exit"); } long timeoutNanos = this.timeoutNanos(); boolean hasDeadline = this.hasDeadline(); if (timeoutNanos == 0L && !hasDeadline) { return; } this.inQueue = true; AsyncTimeout.scheduleTimeout(this, timeoutNanos, hasDeadline); } private static synchronized void scheduleTimeout(AsyncTimeout node, long timeoutNanos, boolean hasDeadline) { if (head == null) { head = new AsyncTimeout(); new Watchdog().start(); } long now = System.nanoTime(); if (timeoutNanos != 0L && hasDeadline) { node.timeoutAt = now + Math.min(timeoutNanos, node.deadlineNanoTime() - now); } else if (timeoutNanos != 0L) { node.timeoutAt = now + timeoutNanos; } else if (hasDeadline) { node.timeoutAt = node.deadlineNanoTime(); } else { throw new AssertionError(); } long remainingNanos = node.remainingNanos(now); AsyncTimeout prev = head; while (true) { if (prev.next == null || remainingNanos < prev.next.remainingNanos(now)) { node.next = prev.next; prev.next = node; if (prev != head) break; AsyncTimeout.class.notify(); break; } prev = prev.next; } } public final boolean exit() { if (!this.inQueue) { return false; } this.inQueue = false; return AsyncTimeout.cancelScheduledTimeout(this); } private static synchronized boolean cancelScheduledTimeout(AsyncTimeout node) { AsyncTimeout prev = head; while (prev != null) { if (prev.next == node) { prev.next = node.next; node.next = null; return false; } prev = prev.next; } return true; } private long remainingNanos(long now) { return this.timeoutAt - now; } protected void timedOut() { } public final Sink sink(final Sink sink) { return new Sink(){ @Override public void write(Buffer source, long byteCount) throws IOException { boolean throwOnTimeout = false; AsyncTimeout.this.enter(); try { sink.write(source, byteCount); throwOnTimeout = true; } catch (IOException e) { throw AsyncTimeout.this.exit(e); } finally { AsyncTimeout.this.exit(throwOnTimeout); } } @Override public void flush() throws IOException { boolean throwOnTimeout = false; AsyncTimeout.this.enter(); try { sink.flush(); throwOnTimeout = true; } catch (IOException e) { throw AsyncTimeout.this.exit(e); } finally { AsyncTimeout.this.exit(throwOnTimeout); } } @Override public void close() throws IOException { boolean throwOnTimeout = false; AsyncTimeout.this.enter(); try { sink.close(); throwOnTimeout = true; } catch (IOException e) { throw AsyncTimeout.this.exit(e); } finally { AsyncTimeout.this.exit(throwOnTimeout); } } @Override public Timeout timeout() { return AsyncTimeout.this; } public String toString() { return "AsyncTimeout.sink(" + sink + ")"; } }; } public final Source source(final Source source) { return new Source(){ @Override public long read(Buffer sink, long byteCount) throws IOException { boolean throwOnTimeout = false; AsyncTimeout.this.enter(); try { long result = source.read(sink, byteCount); throwOnTimeout = true; long l = result; return l; } catch (IOException e) { throw AsyncTimeout.this.exit(e); } finally { AsyncTimeout.this.exit(throwOnTimeout); } } @Override public void close() throws IOException { boolean throwOnTimeout = false; try { source.close(); throwOnTimeout = true; } catch (IOException e) { throw AsyncTimeout.this.exit(e); } finally { AsyncTimeout.this.exit(throwOnTimeout); } } @Override public Timeout timeout() { return AsyncTimeout.this; } public String toString() { return "AsyncTimeout.source(" + source + ")"; } }; } final void exit(boolean throwOnTimeout) throws IOException { boolean timedOut = this.exit(); if (timedOut && throwOnTimeout) { throw this.newTimeoutException(null); } } final IOException exit(IOException cause) throws IOException { if (!this.exit()) { return cause; } return this.newTimeoutException(cause); } protected IOException newTimeoutException(IOException cause) { InterruptedIOException e = new InterruptedIOException("timeout"); if (cause != null) { e.initCause(cause); } return e; } private static synchronized AsyncTimeout awaitTimeout() throws InterruptedException { AsyncTimeout node = AsyncTimeout.head.next; if (node == null) { AsyncTimeout.class.wait(); return null; } long waitNanos = node.remainingNanos(System.nanoTime()); if (waitNanos > 0L) { long waitMillis = waitNanos / 1000000L; AsyncTimeout.class.wait(waitMillis, (int)(waitNanos -= waitMillis * 1000000L)); return null; } AsyncTimeout.head.next = node.next; node.next = null; return node; } private static final class Watchdog extends Thread { public Watchdog() { super("Okio Watchdog"); this.setDaemon(true); } @Override public void run() { while (true) { try { while (true) { AsyncTimeout timedOut; if ((timedOut = AsyncTimeout.awaitTimeout()) == null) { continue; } timedOut.timedOut(); } } catch (InterruptedException interruptedException) { continue; } break; } } } }