function spmd_feval_impl( bodyFcn, assignOutFcn, getOutFcn, unpackInFcn, initialOutCell, inputCell, varargin )
%SPMD_FEVAL_IMPL - support for SPMD block
    
% Copyright 2008-2020 The MathWorks, Inc.

    [resSet, isPoolRunning, pool] = spmdlang.ResourceSetMgr.chooseResourceSet( inputCell, varargin{:} );

    feval('_pct_spmdLog');
    
    attachFilesAndRetryOnError = resSet.shouldRetryIfFileNotFound();

    while true
        try
            blockExecutor = []; % pre-allocate because construction may fail.
            % the resource set knows how to execute a block.
            blockExecutor = resSet.buildBlockExecutor( bodyFcn, assignOutFcn, getOutFcn, ...
                                                       unpackInFcn, initialOutCell );
            
            blockExecutor.initiateComputation();
            
            while ~blockExecutor.isComputationComplete()
                % isComputationComplete will block for a while waiting for completion
                % In each poll tick of the polling wait, yield to any
                % events allowed to interrupt the parallel language.
                parallel.internal.pool.yield();
            end

            % Give the block executor the opportunity to throw any errors detected on
            % the labs. The block executor is free to throw such errors at any other
            % time, but this is the last opportunity.
            blockExecutor.throwBlockExceptions();
            break;
        catch E
            % If we didn't build a blockExecutor, then rethrow immediately
            if isempty( blockExecutor )
                rethrow( E );
            end

            % Dispose of the blockExecutor so we can make a new one when
            % we rerun.  Do this as early as possible so workers don't
            % think that they are being interrupted.  Note that it is OK to
            % dispose and then rethrow errors.
            try
                dispose( blockExecutor );
            catch disposeProblem %#ok<NASGU>
                % ignore dispose problems. We're going to throw something
                % else, or else try again.
            end

            [isFileNotFound, possibleSources] = parallel.internal.pool.missingSourcesFromException(E);
            if ~attachFilesAndRetryOnError || ~isFileNotFound
                rethrow(E)
            end
            
            % Get the body function and add it to the possible sources
            possibleSources{end + 1} = blockExecutor.getBodyFunctionFile(); %#ok<AGROW>

            possibleSources = unique(possibleSources);
            % Only try to do the dependency analysis once
            attachFilesAndRetryOnError = false;
            if isPoolRunning
                try
                    filesAttached = parallel.internal.pool.attachDependentFilesToPool(...
                        pool, possibleSources);
                    if ~filesAttached
                        rethrow(E);
                    end
                    dctSchedulerMessage(6, 'Rerunning spmd_feval_impl after attaching files.');
                catch attachErr
                    % We'll just throw the exception that we caught earlier if the
                    % dependency analysis goes wrong.
                    rethrow(E)
                end
            end
        end
    end
        
    dispose( blockExecutor );
    % Yield one final time to ensure all events directly generated by
    % code in the SPMD block are executed before we give control back to
    % the user.
    parallel.internal.pool.yield();
    % NB outputs assigned for caller in "delete" of blockExecutor.
end
