function value = getJavaFutureInterruptibly(future, shouldInterruptFcn, processEvents, timeoutSeconds)
%getJavaFutureInterruptibly calls 'get' on a java future until it completes
%   VALUE = getJavaFutureInterruptibly(FUTURE) calls FUTURE.get(1, SECOND)
%   until such time that FUTURE yields a result. This is to allow users
%   to hit CTRL-C to abort. This function always cancels FUTURE on exit.
%
%   In the event that future.get() yields an ExecutionException, we attempt to
%   unpick that and throw the MessageID and LocalizedMessage if they exist.

% Copyright 2013-2020 The MathWorks, Inc.

if nargin < 2
    shouldInterruptFcn = @false;
end

if nargin < 3
    processEvents = false;
end

if nargin < 4
    timeoutSeconds = Inf;
end

dctSchedulerMessage(5, '>> parallel.internal.getJavaFutureInterruptibly: timeout %d seconds', timeoutSeconds);

cancelFuture = onCleanup(@() future.cancel(true));
done = false;
value = [];
startTime = tic();
timeoutExpired = false;
while ~done && ~shouldInterruptFcn() && ~timeoutExpired
    if processEvents
        drawnow;
    end
    [done, value] = parallel.internal.getJavaFutureResult(...
        future, 1, java.util.concurrent.TimeUnit.SECONDS);
    if timeoutSeconds ~= Inf
        elapsedTime = toc(startTime);
        timeoutExpired = elapsedTime > timeoutSeconds;
        
        if timeoutExpired
            % Obtain stack traces to find out what the java code is doing:
            dctSchedulerMessage(5, '-- parallel.internal.getJavaFutureInterruptibly timeout expired: %d java stackdump\n << timeout expired', elapsedTime);
            iGetStackTrace('ServiceAccessor');
        end
    end
end
dctSchedulerMessage(5, '<< parallel.internal.getJavaFutureInterruptibly done: %d shouldInterruptFcn %d timeoutExpired %d', ...
    done, shouldInterruptFcn(), timeoutExpired);
end

function iGetStackTrace(threadNamePrefix)
dctSchedulerMessage(5, '>> iGetStackTrace: prefix: %s', threadNamePrefix);
try
    threads = java.lang.Thread.getAllStackTraces().keySet().toArray();
catch ex
    dctSchedulerMessage(5, '-- iGetStackTrace java.lang.Thread.getAllStackTraces failed ', ex);
end
for ii = 1:numel(threads)
    try
        currentThread = threads(ii);
        if startsWith(currentThread.getName(), threadNamePrefix)
            dctSchedulerMessage(5, '-- iGetStackTrace >>');
            dctSchedulerMessage(5, 'Thread: %s | %s | %s | %d', ...
                currentThread.getName(), currentThread.getThreadGroup().getName(), ...
                currentThread.getState(), currentThread.getId());
            stackTrace = currentThread.getStackTrace();
            for jj = 1:numel(stackTrace)
                dctSchedulerMessage(5, '\t@ %s', char(stackTrace(jj).toString()));
            end
            dctSchedulerMessage(5, '-- iGetStackTrace <<');
        end
    catch ex
        dctSchedulerMessage(5, '-- iGetStackTrace failed', ex);
    end
end
dctSchedulerMessage(5, '<< iGetStackTrace');
end
