Esempio n. 1
0
        private void OnCancelInternal(uint id, bool timedOut, Exception exception = null)
        {
            var onCancel = RegisteredJobs[id].JobCancellationHandler;

            RegisteredJobs.Remove(id);

            if (onCancel != null)
            {
                onCancel(id, timedOut, exception); // Call the caller's 'cleanup' code if it was provided
            }
        }
Esempio n. 2
0
        // This is the WRAPPER.  This is what is actually executing as a Unity coroutine.
        // It is structured to use a given timeslice share (expressed as number of stopwatch ticks)

        private IEnumerator RunJob(uint id, IEnumerator job, bool startImmediately)
        {
            bool jobDone = false;

            var jobName = RegisteredJobs[id].Name;

            var timeoutStopwatch = new Stopwatch();

            timeoutStopwatch.Start();

            var wasWaitingForDependentJobs = RegisteredJobs[id].DependentJobs.Count > 0;

            if (wasWaitingForDependentJobs)
            {
                RegisteredJobs[id].State = JobState.Waiting;
            }

            while (RegisteredJobs[id].DependentJobs.Count > 0)
            {
                RegisteredJobs[id].DependentJobs.RemoveAll(item => !RegisteredJobs.ContainsKey(item));

                if (RegisteredJobs[id].DependentJobs.Count > 0)
                {
                    yield return(null); // Yield, as we are still waiting for at least one dependent job to complete

                    if (timeoutStopwatch.ElapsedTicks >= RegisteredJobs[id].TimeoutTimeInTicks)
                    {
                        Debug.Log("JobManager:  \"" + jobName + "\" has timed out (before it even started) and is therefore being cancelled");
                        OnCancelInternal(id, true);
                        yield break;    // And we're completely done
                    }
                }
            }

            // Note:  We put this here because if the job has been waiting for dependent jobs, and the last dependent job has finished, and in the same frame,
            // we're now starting this job, the budgeting can effectively give more time than it should to this job execution, because the basic per-job
            // budget is calculated once per frame and is based on the number of active jobs.
            if (!startImmediately || wasWaitingForDependentJobs)
            {
                yield return(null);
            }

            if (RegisteredJobs[id].State != JobState.Paused)
            {
                RegisteredJobs[id].State = JobState.Running;
            }

            //Debug.Log("JobManager:  Started \"" + jobName + "\"" + (RegisteredJobs[id].State == JobState.Paused ? " but in paused state" : ""));

            var sw = new Stopwatch();

            var       timedOut          = false;
            var       exceptionOccurred = false;
            Exception exception         = null;

            var savedCurrentlyRunningJobId = CurrentlyRunningJobId;

            CurrentlyRunningJobId = id;

            do
            {
                if (RegisteredJobs[id].FinishJobNow)
                {
                    Debug.Log("JobManager:  \"" + jobName + "\" request to complete entire job now...");

                    try
                    {
                        while (job.MoveNext())
                        {
                            ;
                        }
                    }
                    catch (Exception e)
                    {
                        DumpJobExceptionMessage(id, e);
                        exceptionOccurred = true;
                        exception         = e;
                    }

                    break;  // Break out of the outer do loop
                }

                var budgetedTimeInTicks = JobBudget - LastJobOverTicks;
                // Note that at this point, budgetedTimeInTicks can be negative.
                // In that case the job will execute exactly one iteration below, EXCEPT if CanSkipFrames is set to true, in which case we don't execute it at all

                //Debug.Log("budgetedTimeInTicks:" + budgetedTimeInTicks + " for job " + jobName);

                if (budgetedTimeInTicks < 0 && RegisteredJobs[id].CanSkipFrames)
                {
                    //Debug.Log("JobManager:  \"" + RegisteredJobs[id].Name + "\" is being skipped this frame");
                    LastJobOverTicks      = 0 - budgetedTimeInTicks;
                    CurrentlyRunningJobId = savedCurrentlyRunningJobId;

                    yield return(null);

                    savedCurrentlyRunningJobId = CurrentlyRunningJobId;
                    CurrentlyRunningJobId      = id;
                }
                else
                {
                    long elapsedTicks = 0;

                    if (RegisteredJobs[id].State != JobState.Paused)
                    {
                        sw.Start();
                        var executionCount = 0;
                        do
                        {
                            try
                            {
                                if (!job.MoveNext()) // Call the actual code to be executed; returns false if entire job is done
                                {
                                    jobDone      = true;
                                    elapsedTicks = sw.ElapsedTicks;
                                    break;
                                }
                            }
                            catch (Exception e)
                            {
                                DumpJobExceptionMessage(id, e);
                                exceptionOccurred = true;
                                exception         = e;
                                jobDone           = true;
                                elapsedTicks      = sw.ElapsedTicks;
                                break;
                            }

                            elapsedTicks = sw.ElapsedTicks;

                            if (++executionCount >= RegisteredJobs[id].MaxExecutionsPerFrame)
                            {
                                break;
                            }

                            if (RegisteredJobs[id].State == JobState.Paused)
                            {
                                break;
                            }
                        } while (elapsedTicks < budgetedTimeInTicks);

                        sw.Reset();
                    }

                    LastJobOverTicks      = elapsedTicks - budgetedTimeInTicks; // Note that this can be negative
                    TimeSpentInJobsTicks += elapsedTicks;
                    //if (elapsedTicks < budgetedTimeInTicks)
                    //    Debug.Log( "JobManager:  Job slice finished earlier than budget!" );
                }

                if (!jobDone)
                {
                    if (timeoutStopwatch.ElapsedTicks < RegisteredJobs[id].TimeoutTimeInTicks)
                    {
                        CurrentlyRunningJobId = savedCurrentlyRunningJobId;

                        yield return(null);

                        savedCurrentlyRunningJobId = CurrentlyRunningJobId;
                        CurrentlyRunningJobId      = id;
                    }
                    else
                    {
                        Debug.Log("JobManager:  \"" + jobName + "\" has timed out and is therefore being cancelled");
                        OnCancelInternal(id, true);
                        jobDone  = true;
                        timedOut = true;
                    }
                }
            } while (!jobDone);


            if (exceptionOccurred)
            {
                var spawningJobId = RegisteredJobs[id].JobIdToUnPauseWhenDone;

                // Cancel this job, and all jobs spawned by this job
                CancelJob(id, true, exception);

                // Cancel all job(s) that spawned this job
                while (spawningJobId != UInt32.MaxValue)
                {
                    var nextSpawningJobId = RegisteredJobs[spawningJobId].JobIdToUnPauseWhenDone;

                    CancelJob(spawningJobId);

                    if (savedCurrentlyRunningJobId == spawningJobId)
                    {
                        savedCurrentlyRunningJobId = UInt32.MaxValue;
                    }

                    spawningJobId = nextSpawningJobId;
                }
            }
            else if (!timedOut)
            {
                var onCompletion           = RegisteredJobs[id].JobCompletionHandler;
                var jobIdToUnPauseWhenDone = RegisteredJobs[id].JobIdToUnPauseWhenDone;

                RegisteredJobs.Remove(id);

                //Debug.Log("JobManager:  \"" + jobName + "\" completed");

                if (jobIdToUnPauseWhenDone != UInt32.MaxValue)
                {
                    if (!UnPauseJob(jobIdToUnPauseWhenDone))
                    {
                        Debug.Log("JobManager:  Warning: \"" + jobName + "\" tried to unpause job id " + jobIdToUnPauseWhenDone + " but wasn't able to");
                    }
                }

                if (onCompletion != null)
                {
                    onCompletion(id); // Call the caller's 'on completion' code if it was provided
                }
            }

            CurrentlyRunningJobId = savedCurrentlyRunningJobId;
        }