CancelToken.java

// SPDX-License-Identifier: Apache-2.0
package com.shengzhiai.yugu.stcompat.internal;

import java.io.Closeable;
import java.io.IOException;

/**
 * Cancellation for one logical call: aborts the HTTP attempt in flight (by closing its
 * connection) and wakes up retry back-off sleeps.
 */
public final class CancelToken {
    private final Object lock = new Object();
    private boolean cancelled;
    private Closeable active;

    public boolean isCancelled() {
        synchronized (lock) {
            return cancelled;
        }
    }

    public void cancel() {
        Closeable c;
        synchronized (lock) {
            if (cancelled) {
                return;
            }
            cancelled = true;
            c = active;
            active = null;
            lock.notifyAll();
        }
        closeQuietly(c);
    }

    /** Registers the resource of the attempt in flight. Closes it at once when already cancelled. */
    public boolean attach(Closeable c) {
        synchronized (lock) {
            if (!cancelled) {
                active = c;
                return true;
            }
        }
        closeQuietly(c);
        return false;
    }

    public void detach(Closeable c) {
        synchronized (lock) {
            if (active == c) {
                active = null;
            }
        }
    }

    /** Sleeps up to {@code ms}; returns false when cancelled before or during the sleep. */
    public boolean sleep(long ms) {
        long end = System.nanoTime() + ms * 1000000L;
        synchronized (lock) {
            while (!cancelled) {
                long left = (end - System.nanoTime()) / 1000000L;
                if (left <= 0) {
                    return true;
                }
                try {
                    lock.wait(left);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    return false;
                }
            }
            return false;
        }
    }

    private static void closeQuietly(Closeable c) {
        if (c != null) {
            try {
                c.close();
            } catch (IOException ignored) {
                // closing to abort, nothing to report
            } catch (RuntimeException ignored) {
                // HttpURLConnection.disconnect may throw on some devices
            }
        }
    }
}