% SubmissionChecks - a series of pre-submission checks

% Copyright 2011-2019 The MathWorks, Inc.

classdef ( Hidden ) SubmissionChecks
    methods ( Static )

        function warnIfPoolOpenOnThisCluster( cluster, minWorkersNeeded )
            % warnIfPoolOpenOnThisCluster - issue a warning if submitting a job and
            % a parallel pool is open and consuming all resources. This check is
            % specifically intended to protect users from the problem of opening a
            % pool and then submitting batch jobs.
            
            aPool = gcp('nocreate');
            [isOnCluster, poolSize] = parallel.internal.pool.isPoolRunningOnEquivalentCluster(aPool, cluster);
            if isOnCluster
                % We're trying to submit to the same place
                freeWorkers = cluster.hGetMaxNumWorkers() - poolSize;
                if minWorkersNeeded > freeWorkers
                    parallel.internal.warningNoBackTrace( ...
                        message( 'parallel:job:ClusterAlreadyRunningPool' ) );
                end
            end
        end

        function [tasks, numTasks] = checkIndependentTasksBeforeSubmission( job )
        % checkIndependentTasksBeforeSubmission - check there's at least
        % one non-cancelled task
            validateattributes( job, {'parallel.IndependentJob'}, {'scalar'} );
            
            % Only want tasks in the Pending state - on cancellation, a
            % task is given the state Finished
            taskStates = get( job.Tasks, 'StateEnum');
            % If there's only one task, need to pack into a cell array
            if ~iscell(taskStates)
                taskStates = {taskStates};
            end
            tasks = job.Tasks([taskStates{:}] == parallel.internal.types.States.Pending);
            numTasks = numel( tasks );
            if numTasks < 1
                error(message('parallel:job:JobSubmissionWithNoTasks'));
            end
        end

        function [tasks, numTasks] = checkSimultaneousTasksBeforeSubmission( cjob )
        % checkSimultaneousTasksBeforeSubmission - must be exactly 1 task
            validateattributes( cjob, {'parallel.CommunicatingJob', ...
                                'parallel.ConcurrentJob' }, {'scalar'} );
            tasks    = cjob.Tasks;
            numTasks = numel( tasks );
            if numTasks ~= 1
                error(message('parallel:job:CommunicatingJobNeedsOneTask'));
            end
        end

        function maxW = checkWorkerLimsForSimultaneousJob( cluster, cjob )
        % checkMinMaxWorkersForSimultaneousJob - checks that NumWorkersRange
        % are ok for this cluster.

            validateattributes( cluster, {'parallel.Cluster'}, {'scalar'} );
            validateattributes( cjob, {'parallel.CommunicatingJob', ...
                                'parallel.ConcurrentJob' }, {'scalar'} );

            workerLims = cjob.NumWorkersRange;
            minW = workerLims(1);
            maxW = workerLims(2);

            % The following cases should never be hit due to the constraints on
            % NumWorkersRange:
            if maxW < 1
                error(message('parallel:job:UnexpectedBadNumWorkersRange'));
            end

            % Return the number of workers that we think should be requested
            maxW = min( maxW, cluster.NumWorkers );

            if minW > cluster.NumWorkers
                if isempty( cluster.Profile )
                    % TODO:doc review
                    error(message('parallel:job:TooManyWorkersRequested', minW, cluster.NumWorkers));
                else
                    error(message('parallel:job:TooManyWorkersRequestedForProfile', minW, cluster.Profile, cluster.NumWorkers));
                end
            end
        end
    end
end
