package com.dtflys.forest.backend.okhttp3.response;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * @author gongjun[jun.gong@thebeastshop.com]
 * @since 2018-02-28 18:10
 */
public class OkHttp3ResponseFuture implements Future<Object> {

    private volatile boolean cancelled;
    private volatile boolean completed;
    private volatile Object result;
    private volatile Exception ex;


    @Override
    public boolean isCancelled() {
        return cancelled;
    }

    @Override
    public boolean isDone() {
        return completed;
    }

    private Object getResult() throws ExecutionException {
        if (this.ex != null) {
            throw new ExecutionException(this.ex);
        }
        return this.result;
    }


    @Override
    public synchronized Object get() throws InterruptedException, ExecutionException {
        while (!this.completed) {
            wait();
        }
        return getResult();
    }

    @Override
    public Object get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
        final long msecs = unit.toMillis(timeout);
        final long startTime = (msecs <= 0) ? 0 : System.currentTimeMillis();
        long waitTime = msecs;
        if (this.completed) {
            return getResult();
        } else if (waitTime <= 0) {
            throw new TimeoutException();
        } else {
            for (;;) {
                wait(waitTime);
                if (this.completed) {
                    return getResult();
                } else {
                    waitTime = msecs - (System.currentTimeMillis() - startTime);
                    if (waitTime <= 0) {
                        throw new TimeoutException();
                    }
                }
            }
        }

    }

    public boolean completed(final Object response) {
        synchronized(this) {
            if (this.completed) {
                return false;
            }
            this.completed = true;
            this.result = response;
            notifyAll();
        }
        return true;
    }

    public boolean failed(final Exception exception) {
        synchronized(this) {
            if (this.completed) {
                return false;
            }
            this.completed = true;
            this.ex = exception;
            notifyAll();
        }
        return true;
    }

    public boolean cancel(final boolean mayInterruptIfRunning) {
        synchronized(this) {
            if (this.completed) {
                return false;
            }
            this.completed = true;
            this.cancelled = true;
            notifyAll();
        }
        return true;
    }

    public boolean cancel() {
        return cancel(true);
    }

}

@@ -1528,24 +1528,66 @@
  for (;;) %7B%0A
+                synchronized (this) %7B%0A    
             
@@ -1598,32 +1598,36 @@
 wait(waitTime);%0A
+    
                 
@@ -1660,32 +1660,36 @@
                 
+    
 return getResult
@@ -1700,32 +1700,36 @@
                 
+    
 %7D else %7B%0A       
@@ -1713,32 +1713,36 @@
        %7D else %7B%0A
+    
                 
@@ -1822,24 +1822,28 @@
             
+    
 if (waitTime
@@ -1843,32 +1843,36 @@
 aitTime %3C= 0) %7B%0A
+    
                 
@@ -1901,32 +1901,58 @@
 outException();%0A
+                        %7D%0A
                 

Move this call to "wait()" into a synchronized block to be sure the monitor on "this" is held.