%

% Copyright 2012-2020 The MathWorks, Inc.
classdef ( ConstructOnLoad = true, Hidden ) CJSConcurrentJob < parallel.ConcurrentJob & ...
        parallel.internal.cluster.CJSJobMixin
    properties ( PCTGetSet, Transient, Hidden )
        JobSchedulerData
        EnvironmentCredentials
    end

    methods ( Hidden )
        function obj = CJSConcurrentJob( parent, jobid, jobsid, support )
            parallel.Job.loadCheck( nargin );
            obj@parallel.ConcurrentJob( parent, jobid );
            obj@parallel.internal.cluster.CJSJobMixin( support, jobsid );
        end
    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % abstract job methods
    methods ( Access = protected )
        function t = createTaskOnOneJob( job, varargin )
            import parallel.internal.cluster.CJSJobMethods

            if isempty( job.Tasks )
                allowMultipleTasks = false;
                t = CJSJobMethods.createTask( job, job.Support, job.SupportID, ...
                                              allowMultipleTasks, varargin{:} );
            else
                error(message('parallel:job:ConcurrentJobOneTask'));
            end
        end
        function submitOneJob( job )
            job.submitOneJobCommon(@hSubmitConcurrentJob);
        end

        function newJob = recreateOneJob( oldJob, varargin )
            cluster = oldJob.Parent;
            jobPVPairs = extractPVPairsFromExistingObject( oldJob, [ {'NumWorkersRange'}, getCommonJobPropsForJobRecreation() ] );
            newJob = cluster.createConcurrentJob( jobPVPairs{:} );
            jobHiddenPVPairs = extractPVPairsFromExistingObject( oldJob, getCommonHiddenJobPropsForRecreation() ); 
            for idx = 1:2:numel(jobHiddenPVPairs)
                newJob.hSetProperty( jobHiddenPVPairs{idx}, jobHiddenPVPairs{idx+1} );
            end
            %Concurrent jobs only have one task
            taskPVPairs = extractCreationArgsFromExistingTask( oldJob.Tasks(1), getCommonTaskPropsForJobRecreation() );
            newJob.createTask( taskPVPairs{:} );                        
        end        
        
        function destroyJob( jobOrJobs )
            import parallel.internal.cluster.CJSJobMethods;
            arrayfun( @(job) CJSJobMethods.destroyOneJob( job.Parent, job, ...
                job.Support, job.SupportID ), jobOrJobs );
        end
        function cancelJob( jobOrJobs, cancelException )
            import parallel.internal.cluster.CJSJobMethods;
            arrayfun( @(job, ce) CJSJobMethods.cancelOneJob( job.Parent, job, ...
                job.Support, ce ), jobOrJobs, cancelException );
        end
        function varargout = getTasksByState( obj )
            [varargout{1:nargout}] = obj.getTasksByStateImpl();
        end
        function attachFileDataToJob( obj, filedata )
            obj.Support.attachFileDataToJob( obj.SupportID, obj.Variant, filedata );
        end
    end
    methods ( Access = private )
        function trimExcessTasks( job, numberOfLabs )
        % Called prior to execution on lab 1 to remove any excess tasks that
        % might have been created.
            try
                tasks = job.Tasks;
                for ii = numberOfLabs+1:numel(tasks)
                    % We cannot use the full 'delete' here, as this causes the
                    % task (and hence job) to be cancelled. So, we use a secret
                    % method to rid ourselves of the troublesome tasks.
                    hRemoveOneExcessTask( tasks(ii) );
                end
            catch E %#ok<NASGU> swallow errors in this method
            end
        end
        function tf = canModifyJobOnWorker( ~, taskId )
        % On the worker only, can we modify the job?
            tf = ( taskId == 1 );
        end
    end

    % API1 compatibility
    methods ( Hidden )
        function pPreJobEvaluate( job, task )
            if mpiInitialized
                % MPI is not required for concurrent jobs, so finalize MPI right now and ensure
                % it cannot be used.
                mpiParallelSessionEnding;
                mpiFinalize;
            end
            canModifyJob  = job.canModifyJobOnWorker( task.ID );
            if canModifyJob
                % The following line of code is not permitted to throw:
                job.trimExcessTasks();
            end
            idForRandInit = task.ID;
            setupGPUAllocations = false;
            preCJSJob( job, canModifyJob, idForRandInit, setupGPUAllocations );
        end
        function pPostJobEvaluate( ~ )
        % Essentially the same as CJSIndependentJob.pPostJobEvaluate.
        end
    end
    
    methods ( Hidden )
        function tf = hWorkerCrashedDuringExecution( job )
        % See if we've got any crash dump files.
            tf = ~isempty( job.hGetCrashDumpFiles() );
        end
    end
end
%#ok<*ATUNK> custom attributes
