function pctRunOnAll(varargin)
%pctRunOnAll run a command on client and all workers in a parallel pool
%  pctRunOnAll allows you to specify commands to run on the client and all
%  the workers in a parallel pool. This is useful if there are setup or
%  configuration changes that you want to do on both the client and all of
%  the workers.
%  
%
%  SYNTAX
%
%  pctRunOnAll COMMAND runs the specified command on the client and all the
%  workers and prints any command line output back to this command window.
%  The requested COMMAND runs in the base workspace of the workers and does
%  not have any return variables.
%
%  EXAMPLES
%
%  Clear all loaded functions on client and all workers
%    pctRunOnAll clear functions
%
%  Change directory on client and all workers to the project directory
%    pctRunOnAll cd /opt/projects/c1456
%
%  Add a few directories to the paths of the client and all workers
%    pctRunOnAll addpath({'/usr/local/path1' '/usr/local/path2'})
%
%   See also : parpool 

%   Copyright 2007-2019 The MathWorks, Inc.

% Convert any string inputs to character vectors
[varargin{:}] = convertStringsToChars(varargin{:});

command = iParseRunOnAllArgs(varargin{:});

pool = gcp('nocreate');
if isempty(pool) || ~pool.Connected
    error(message('parallel:convenience:RunOnAllPoolNotRunning', command));
end
l = pool.hGetSession().getLabs();
if isempty( l )
    error(message('parallel:convenience:RunOnAllNotOnClient'));
end

% Use Drainable Output here rather than System.out.print.
o = com.mathworks.toolbox.distcomp.pmode.RunOnAllCompletionObserver(true);
% Labs.eval includes the deadlock detection
l.eval(command, o);
err = '';
try
    evalin('base', command);
catch err
end

complete = false;
output = o.getDrainableOutput;
% Function to display the Strings in a Java String array.
dispStringArray = @(msgs) cellfun(@(msg) disp(char(msg)), cell(msgs));

while ~complete
    complete = o.waitForCompletion(1, java.util.concurrent.TimeUnit.SECONDS);
    % It's possible that when we originally asked for the output, it hadn't been
    % created yet - so try to get it again.
    if isempty(output)
        output = o.getDrainableOutput;
    end
    % If we have the output object now, display the output.
    if ~isempty(output)
        dispStringArray(output.drainOutput());
    end
    % Only test to see if the session is failing if we didn't get a
    % results from the queue
    if ~complete && ~pool.Connected
        error(message('parallel:convenience:RunOnAllPoolShutdown'));
    end
end

if ~isempty(err)
    rethrow(err)
end

end
% -------------------------------------------------------------------------
%
% -------------------------------------------------------------------------
function command = iParseRunOnAllArgs(varargin)
% iParseRunOnAllArgs checks that we can create a single string from
% varargin
if numel(varargin) == 0
    command = '';
    return
end
% Ensure that all elements of varargin are strings 1xN
if (all(cellfun(@ischar, varargin) &  ...
        cellfun(@(C) size(C, 2), varargin) == cellfun(@numel, varargin)))
    command = sprintf('%s ', varargin{:});
else
    error(message('parallel:convenience:RunOnAllInvalidInput'))
end
end
