// Path: xvfb-manager/src/main/java/com/github/mike10004/xvfbmanager/Poller.java
// public static class PollOutcome<E> {
// 
//     /**
//      * Reason polling stopped.
//      */
//     public final StopReason reason;
// 
//     /**
//      * An object that represents the resolved state of the poll.
//      */
//     public final @Nullable E content;
// 
//     /**
//      * Gets the polling duration. This may not be exact.
//      */
//     public final Duration duration;
// 
//     private final int numAttempts;
// 
//     private PollOutcome(StopReason reason, @Nullable E content, Duration duration, int numAttempts) {
//         this.reason = checkNotNull(reason);
//         this.content = content;
//         this.duration = checkNotNull(duration);
//         this.numAttempts = numAttempts;
//     }
// 
//     @Override
//     public String toString() {
//         return "PollOutcome{" +
//                 "reason=" + reason +
//                 ", content=" + content +
//                 ", duration=" + duration +
//                 ", attempts=" + numAttempts +
//                 '}';
//     }
// 
//     /**
//      * Gets the number of times the poll was attempted. This is the numbef of
//      * times the {@link #check(int) check()} function is invoked.
//      * @return count of attempts
//      */
//     public int getNumAttempts() {
//         return numAttempts;
//     }
// 
//     @Override
//     public boolean equals(Object o) {
//         if (this == o) return true;
//         if (o == null || getClass() != o.getClass()) return false;
// 
//         PollOutcome<?> that = (PollOutcome<?>) o;
// 
//         if (numAttempts != that.numAttempts) return false;
//         if (reason != that.reason) return false;
//         if (content != null ? !content.equals(that.content) : that.content != null) return false;
//         return duration.equals(that.duration);
//     }
// 
//     @Override
//     public int hashCode() {
//         int result = reason.hashCode();
//         result = 31 * result + (content != null ? content.hashCode() : 0);
//         result = 31 * result + duration.hashCode();
//         result = 31 * result + numAttempts;
//         return result;
//     }
// }
// 
// Path: xvfb-manager/src/main/java/com/github/mike10004/xvfbmanager/Poller.java
// public enum StopReason {
// 
//     /**
//      * State was resolved to the poller's satisfaction.
//      */
//     RESOLVED,
// 
//     /**
//      * State was not resolved to the poller's satisfaction,
//      * but polling must cease anyway.
//      */
//     ABORTED,
// 
//     /**
//      * The poller's iterator of intervals was exhausted
//      * prior to resolution or abortion of polling.
//      */
//     TIMEOUT
// }


import com.github.mike10004.nativehelper.Whicher;
import com.google.common.util.concurrent.JdkFutureAdapters;
import io.github.mike10004.subprocess.ProcessMonitor;
import io.github.mike10004.subprocess.ProcessResult;
import io.github.mike10004.subprocess.ProcessTracker;
import io.github.mike10004.subprocess.Subprocess;
import com.github.mike10004.xvfbmanager.Poller.PollOutcome;
import com.github.mike10004.xvfbmanager.Poller.StopReason;
import com.google.common.base.Suppliers;
import com.google.common.collect.Iterables;
import com.google.common.io.CharSource;
import com.google.common.io.Files;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.MoreExecutors;
import io.github.mike10004.subprocess.SubprocessLaunchSupport;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.requireNonNull;
    private static final int AUTO_DISPLAY_POLLS_MAX = 20;

    protected int pollForDisplayNumber(final CharSource cs) {
        Poller<Integer> poller = new Poller<Integer>() {
            @Override
            protected PollAnswer<Integer> check(int pollAttemptsSoFar) {
                @Nullable String lastLine = null;
                try {
                    lastLine = Iterables.getFirst(cs.readLines().reverse(), null);
                } catch (IOException e) {
                    log.info("failed to read from {}", cs);
                }
                if (lastLine != null) {
                    lastLine = lastLine.trim();
                    if (lastLine.matches("\\d+")) {
                        int displayNumber = Integer.parseInt(lastLine);
                        return resolve(displayNumber);
                    } else {
                        log.debug("last line of xvfb output is not an integer: {}", StringUtils.abbreviate(lastLine, 128));
                    }
                }
                return continuePolling();
            }
        };
        PollOutcome<Integer> pollOutcome;
        try {
            pollOutcome = poller.poll(AUTO_DISPLAY_POLL_INTERVAL_MS, AUTO_DISPLAY_POLLS_MAX);
        } catch (InterruptedException e) {
            throw new XvfbException("interrupted while polling for display number", e);
        }
// Path: xvfb-manager/src/main/java/com/github/mike10004/xvfbmanager/Poller.java
// public static class PollOutcome<E> {
// 
//     /**
//      * Reason polling stopped.
//      */
//     public final StopReason reason;
// 
//     /**
//      * An object that represents the resolved state of the poll.
//      */
//     public final @Nullable E content;
// 
//     /**
//      * Gets the polling duration. This may not be exact.
//      */
//     public final Duration duration;
// 
//     private final int numAttempts;
// 
//     private PollOutcome(StopReason reason, @Nullable E content, Duration duration, int numAttempts) {
//         this.reason = checkNotNull(reason);
//         this.content = content;
//         this.duration = checkNotNull(duration);
//         this.numAttempts = numAttempts;
//     }
// 
//     @Override
//     public String toString() {
//         return "PollOutcome{" +
//                 "reason=" + reason +
//                 ", content=" + content +
//                 ", duration=" + duration +
//                 ", attempts=" + numAttempts +
//                 '}';
//     }
// 
//     /**
//      * Gets the number of times the poll was attempted. This is the numbef of
//      * times the {@link #check(int) check()} function is invoked.
//      * @return count of attempts
//      */
//     public int getNumAttempts() {
//         return numAttempts;
//     }
// 
//     @Override
//     public boolean equals(Object o) {
//         if (this == o) return true;
//         if (o == null || getClass() != o.getClass()) return false;
// 
//         PollOutcome<?> that = (PollOutcome<?>) o;
// 
//         if (numAttempts != that.numAttempts) return false;
//         if (reason != that.reason) return false;
//         if (content != null ? !content.equals(that.content) : that.content != null) return false;
//         return duration.equals(that.duration);
//     }
// 
//     @Override
//     public int hashCode() {
//         int result = reason.hashCode();
//         result = 31 * result + (content != null ? content.hashCode() : 0);
//         result = 31 * result + duration.hashCode();
//         result = 31 * result + numAttempts;
//         return result;
//     }
// }
// 
// Path: xvfb-manager/src/main/java/com/github/mike10004/xvfbmanager/Poller.java
// public enum StopReason {
// 
//     /**
//      * State was resolved to the poller's satisfaction.
//      */
//     RESOLVED,
// 
//     /**
//      * State was not resolved to the poller's satisfaction,
//      * but polling must cease anyway.
//      */
//     ABORTED,
// 
//     /**
//      * The poller's iterator of intervals was exhausted
//      * prior to resolution or abortion of polling.
//      */
//     TIMEOUT
// }


// Path: xvfb-manager/src/main/java/com/github/mike10004/xvfbmanager/XvfbManager.java
import com.github.mike10004.nativehelper.Whicher;
import com.google.common.util.concurrent.JdkFutureAdapters;
import io.github.mike10004.subprocess.ProcessMonitor;
import io.github.mike10004.subprocess.ProcessResult;
import io.github.mike10004.subprocess.ProcessTracker;
import io.github.mike10004.subprocess.Subprocess;
import com.github.mike10004.xvfbmanager.Poller.PollOutcome;
import com.github.mike10004.xvfbmanager.Poller.StopReason;
import com.google.common.base.Suppliers;
import com.google.common.collect.Iterables;
import com.google.common.io.CharSource;
import com.google.common.io.Files;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.MoreExecutors;
import io.github.mike10004.subprocess.SubprocessLaunchSupport;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.requireNonNull;

    private static final int AUTO_DISPLAY_POLLS_MAX = 20;

    protected int pollForDisplayNumber(final CharSource cs) {
        Poller<Integer> poller = new Poller<Integer>() {
            @Override
            protected PollAnswer<Integer> check(int pollAttemptsSoFar) {
                @Nullable String lastLine = null;
                try {
                    lastLine = Iterables.getFirst(cs.readLines().reverse(), null);
                } catch (IOException e) {
                    log.info("failed to read from {}", cs);
                }
                if (lastLine != null) {
                    lastLine = lastLine.trim();
                    if (lastLine.matches("\\d+")) {
                        int displayNumber = Integer.parseInt(lastLine);
                        return resolve(displayNumber);
                    } else {
                        log.debug("last line of xvfb output is not an integer: {}", StringUtils.abbreviate(lastLine, 128));
                    }
                }
                return continuePolling();
            }
        };
        PollOutcome<Integer> pollOutcome;
        try {
            pollOutcome = poller.poll(AUTO_DISPLAY_POLL_INTERVAL_MS, AUTO_DISPLAY_POLLS_MAX);
        } catch (InterruptedException e) {
            throw new XvfbException("interrupted while polling for display number", e);
        }
        if (pollOutcome.reason == StopReason.RESOLVED) {