%% Query and Cancel parfeval Futures
% When you use parfeval or parfevalOnAll to run computations in the background, you create objects called futures. You can use the State property of a future to find out whether it is running, queued or finished. You can also use the FevalQueue property of a parallel pool to access running and queued futures. To cancel futures, you can use the cancel function. In this example, you:
% Use cancel to cancel futures directly.
% Check completion errors on completed futures.
% Use the FevalQueue property to access futures.
%% Add Work to Queue
% Create a parallel pool p with two workers.

p = parpool(2);

% When you use parfeval to run computations in the background, the function creates and adds a future for each computation to the pool queue. Tasks remain in the queue until a worker becomes idle. When a worker becomes idle, it starts to compute a task if the queue is not empty. When a worker completes a task, the task is removed from the queue and the worker becomes idle.
% Use parfeval to create an array of futures f by instructing workers to execute the function pause. Use an argument of 1 for the third future, and an argument of Inf for all other futures.

for n = 1:5
    if n == 3
        f(n) = parfeval(@pause,0,1);
    else
        f(n) = parfeval(@pause,0,Inf);
    end
end

% Each use of parfeval returns a future object that represents the execution of a function on a worker. Except for the third future, every future will take an infinite amount of time to compute. The future created by parfeval(@pause,0,Inf) is an extreme case of a future which can slow down a queue.
%
%% Cancel Futures Directly
% You can use the State property to obtain the status of futures. Construct a cell array of the state of each future in f.

{f.State}

% Every task except for the third pauses forever.
% Cancel the second future directly with cancel.

cancel(f(2));
{f.State}

% After you cancel the second future, the third future runs. Wait until the third future completes, then examine the states again.

wait(f(3));
{f.State}

% The third future now has the state 'finished'.
%
%% Check Completion Errors
% When a future completes, its State property becomes 'finished'. To distinguish between futures which are cancelled and complete normally, use the Error property.

fprintf("f(2): %s\n", f(2).Error.message)
fprintf("f(3): %s\n", f(3).Error.message)

% The code cancels the second future, as the message property indicates. The second future was cancelled, as stated in the message property. The third future completes without error, and therefore does not have an error message.
%
%% Cancel Futures in Pool Queue
% You can use the FevalQueue property to access the futures in the pool queue.

p.FevalQueue

% The queue has two properties: RunningFutures and QueuedFutures. The RunningFutures property is an array of futures corresponding to tasks that are currently running.

disp(p.FevalQueue.RunningFutures)

% The QueuedFutures property is an array of futures corresponding to tasks that are currently queued and not running.

disp(p.FevalQueue.QueuedFutures)

% You can cancel a single future or an array of futures. Cancel all the futures in QueuedFutures.

cancel(p.FevalQueue.QueuedFutures);
{f.State}

% RunningFutures and QueuedFutures are sorted from newest to oldest, regardless of whether f is in order from newest to oldest. Each future has a unique ID property for the lifetime of the client. Check the ID property of each of the futures in f.

disp(f)

% Compare the result against the ID property of each of the RunningFutures.

for j = 1:length(p.FevalQueue.RunningFutures)
    rf = p.FevalQueue.RunningFutures(j);
    fprintf("p.FevalQueue.RunningFutures(%i): ID = %i\n", j, rf.ID)
end

% Here, RunningFutures is an array containing f(1) and f(4). If you cancel RunningFutures(2), you cancel the fourth future f(4).
% Sometimes, futures are not available in the workspace, for example, if you execute the same piece of code twice before it finishes, or if you use parfeval in a function. You can cancel futures that are not available in the workspace.
% Clear f from the workspace.

clear f

% You can use RunningFutures and QueuedFutures to access futures that have not yet completed. Use RunningFutures to cancel f(4).

rf2 = p.FevalQueue.RunningFutures(2);
cancel(rf2)
rf2.State

% To cancel all the futures still in the queue, use the following code.

cancel([p.FevalQueue.RunningFutures p.FevalQueue.QueuedFutures])

% Copyright 2020 The MathWorks, Inc.