예제 #1
0
        protected virtual void Reschedule()
        {
            if (IsDisposed)
            {
                return;
            }

            if (SchedulingItems.Count == 0)
            {
                NextExecution = null;
                Timer.Change(Timeout.Infinite, Timeout.Infinite);
                LastTimerInterval = Timeout.Infinite;
            }
            else
            {
                var            executionTime = SchedulingItems[0].NextExecution;
                DateTimeOffset now           = SchedulingTimeHelpers.Now();
                TimeSpan       delay         = executionTime.Value.Subtract(now);
                long           dueTime       = System.Math.Max(MinJobInterval, (long)delay.TotalMilliseconds);
                dueTime       = System.Math.Min(dueTime, SelfTestInterval);
                NextExecution = SchedulingTimeHelpers.Now().AddMilliseconds(dueTime);
                Timer.Change(dueTime, Timeout.Infinite);
                LastTimerInterval = dueTime;
            }

            LastTimerRun = SchedulingTimeHelpers.Now();
        }
예제 #2
0
        /// <summary>
        /// Updates the internal state after an execution, and updates the
        /// <see cref="LastJobEvaluation"/> and <see cref="NextExecution"/>
        /// values. If the job is not supposed to run anymore, the
        /// <see cref="NextExecution"/> property is set to null.
        /// </summary>
        protected virtual void UpdateState()
        {
            LastJobEvaluation = SchedulingTimeHelpers.Now();

            lock (ManagedJob.SynchronizationToken)
            {
                if (ManagedJob.SchedulingState == SchedulingItemStateType.Aborted)
                {
                    NextExecution = null;
                    return;
                }


                if (RemainingExecutions.HasValue)
                {
                    //only decrease the loop counter if the job is not paused
                    if (ManagedJob.SchedulingState == SchedulingItemStateType.Running)
                    {
                        RemainingExecutions--;
                    }

                    //we're done if we peformend the last loop
                    if (RemainingExecutions == 0)
                    {
                        //we have a loop, and completed it
                        ManagedJob.SchedulingState = SchedulingItemStateType.Done;
                        NextExecution = null;
                        return;
                    }
                }


                //if there is no reoccurrence interval, we cannot calculate a new run
                //-> cancel
                if (!ManagedJob.Interval.HasValue)
                {
                    ManagedJob.SchedulingState = SchedulingItemStateType.Aborted;
                    NextExecution = null;
                    return;
                }

                //schedule next execution - even if this is beyond expiration
                //(we don't cancel as long as expiration happened)
                NextExecution = (NextExecution.Value).Add(ManagedJob.Interval.Value);

                //if the next job is beyond expiration, reset again
                if (ManagedJob.ExpirationTime.HasValue && LastJobEvaluation.Value > ManagedJob.ExpirationTime)
                {
                    ManagedJob.SchedulingState = SchedulingItemStateType.Done;
                    NextExecution = null;
                }
            }
        }
예제 #3
0
        protected virtual void RunPendingJobs()
        {
            var now     = SchedulingTimeHelpers.Now();
            var dueJobs = new List <SchedulingItemContext>();
            SchedulingItemContext currentJob = null;

            for (int i = 0; i < SchedulingItems.Count; i++)
            {
                var job = SchedulingItems[i];
                if (job.NextExecution.Value > now)
                {
                    break;
                }
                dueJobs.Add(job);
            }

            try
            {
                for (var i = dueJobs.Count - 1; i >= 0; i--)
                {
                    currentJob = dueJobs[i];
                    currentJob.ExecuteAsync(this);
                    if (currentJob.NextExecution.HasValue)
                    {
                        IsSorted = false;
                    }
                    else
                    {
                        SchedulingItems.RemoveAt(i);
                    }
                }
            }
            catch (Exception e)
            {
                if (!SubmitJobException(currentJob.ManagedJob, e))
                {
                    throw;
                }
            }
        }
예제 #4
0
        /// <summary>
        /// Initializes a new instance of the <see cref="T:System.Object"/> class.
        /// </summary>
        /// <exception cref="ArgumentNullException">If one of the parameters is
        /// is a null reference.</exception>
        /// <exception cref="InvalidOperationException">If the configuration does not allow
        /// proper scheduling, e.g. because several loops were specified, but the inverval is
        /// missing.</exception>
        public SchedulingItemContext(SchedulingItem managedJob, Action <SchedulingItem> callbackAction)
        {
            if (managedJob == null)
            {
                throw new ArgumentNullException("managedJob");
            }
            if (callbackAction == null)
            {
                throw new ArgumentNullException("callbackAction");
            }

            //make sure we have an interval if we have more than one loop
            TimeSpan?interval = managedJob.Interval;

            if (interval == null)
            {
                if (managedJob.Loops == null || managedJob.Loops.Value > 1)
                {
                    string msg = "Job [{0}] is invalid: Specifiy either a single run, or a loop interval.";
                    msg = String.Format(msg, managedJob.Id);
                    throw new InvalidOperationException(msg);
                }
            }

            ManagedJob     = managedJob;
            CallbackAction = callbackAction;
            NextExecution  = managedJob.StartTime;

            //adjust starting time if the initial time was in the past,
            //but rather start immediately
            var now = SchedulingTimeHelpers.Now();

            if (NextExecution.Value < now)
            {
                NextExecution = now;
            }

            RemainingExecutions = managedJob.Loops;
        }
예제 #5
0
        public virtual void Submit(SchedulingItem schedulingItem, Action <SchedulingItem> callback)
        {
            if (schedulingItem == null)
            {
                throw new ArgumentNullException("schedulingItem");
            }
            if (callback == null)
            {
                throw new ArgumentNullException("callback");
            }
            var interval = schedulingItem.Interval;

            if (interval.HasValue && interval.Value.TotalMilliseconds < MinJobInterval)
            {
                string msg = "Interval of {0} ms is too small - a minimum interval of {1} ms is accepted.";
                msg = String.Format(msg, interval.Value.TotalMilliseconds, MinJobInterval);
                throw new InvalidOperationException(msg);
            }
            var context = new SchedulingItemContext(schedulingItem, callback);

            lock (SyncRoot)
            {
                if (NextExecution == null || context.NextExecution <= NextExecution.Value)
                {
                    SchedulingItems.Insert(0, context);
                    if (NextExecution == null || NextExecution.Value.Subtract(SchedulingTimeHelpers.Now()).TotalMilliseconds > MinJobInterval)
                    {
                        Reschedule();
                    }
                }
                else
                {
                    SchedulingItems.Add(context);
                    IsSorted = false;
                }
            }
        }
예제 #6
0
        protected virtual void VerifySystemTime()
        {
            if (LastTimerInterval == Timeout.Infinite)
            {
                return;
            }
            var now            = SchedulingTimeHelpers.Now();
            var pauseDuration  = now.Subtract(LastTimerRun);
            var timeDivergence = pauseDuration.TotalMilliseconds - LastTimerInterval;

            if (timeDivergence > 1000 || timeDivergence < 1000)
            {
                bool changeExpirationTime = ReschedulingType ==
                                            ReschedulingType.RescheduleNextExecutionAndExpirationTime;
                SchedulingItems.ForEach(jc =>
                {
                    jc.NextExecution = jc.NextExecution.Value.AddMilliseconds(timeDivergence);
                    if (changeExpirationTime && jc.ManagedJob.ExpirationTime.HasValue)
                    {
                        jc.ManagedJob.ExpirationTime = jc.ManagedJob.ExpirationTime.Value.AddMilliseconds(timeDivergence);
                    }
                });
            }
        }
예제 #7
0
        /// <summary>
        /// Invokes the managed job's <see cref="CallbackAction"/> through
        /// the thread pool, and updates the job's internal state.
        /// </summary>
        public virtual void ExecuteAsync(SchedulingService scheduler)
        {
            //only execute if the job is neither aborted, expired, or paused
            if (ManagedJob.SchedulingState == SchedulingItemStateType.Running && (ManagedJob.ExpirationTime == null || ManagedJob.ExpirationTime >= SchedulingTimeHelpers.Now()))
            {
                ThreadPool.QueueUserWorkItem(s =>
                {
                    try
                    {
                        CallbackAction(ManagedJob);
                    }
                    catch (Exception e)
                    {
                        //do not reference the scheduler instance to avoid closure
                        SchedulingService sch = (SchedulingService)s;
                        if (!sch.SubmitJobException(ManagedJob, e))
                        {
                            throw;
                        }
                    }
                }, scheduler);
            }

            UpdateState();
        }