Beispiel #1
0
        /// <summary>
        /// Pause scheduler until all scheduled work items on the given context have completed.
        /// </summary>
        /// <param name="context">The scheduler context on which to wait.</param>
        internal void PauseForQuiescence(SchedulerContext context)
        {
            if (context.Empty.WaitOne(0) || !this.started || this.completed)
            {
                return;
            }

            // Pulse the future queue to process any work items that are either due or
            // non-reachable due to having a start time past the finalization time.
            this.futureQueuePulse.Set();

            if (!this.isSchedulerThread.Value && !this.allowSchedulingOnExternalThreads)
            {
                // this is not a scheduler thread, so just wait for the context to empty and return
                context.Empty.WaitOne();
                return;
            }

            // continue executing work items on this scheduler thread while waiting for the context to empty
            while (!this.forcedShutdownRequested && !context.Empty.WaitOne(0))
            {
                WorkItem wi;

                if (this.nextWorkitem.Value != null)
                {
                    // if there is a queued local work item, try to run it first
                    wi = (WorkItem)this.nextWorkitem.Value;
                    this.nextWorkitem.Value = null;
                    this.counters?.Decrement(SchedulerCounters.LocalQueueCount);
                    if (wi.SyncLock.TryLock())
                    {
                        this.counters?.Increment(SchedulerCounters.LocalWorkitemsPerSecond);
                        goto ExecuteWorkItem;
                    }
                    else
                    {
                        // it's locked, so let someone else have it
                        this.Schedule(wi, false);
                        this.counters?.Increment(SchedulerCounters.LocalToGlobalPromotions);
                    }
                }

                // try to get another item from the global queue
                if (!this.globalWorkitems.TryDequeue(out wi))
                {
                    // no work left
                    continue;
                }

                this.counters?.Increment(SchedulerCounters.GlobalWorkitemsPerSecond);

ExecuteWorkItem:
                this.currentWorkitemTime.Value = wi.StartTime;
                this.ExecuteAndRelease(wi.SyncLock, wi.Callback, wi.SchedulerContext);
            }
        }
Beispiel #2
0
 private void ExecuteAndRelease(SynchronizationLock synchronizationObject, Action action, SchedulerContext context)
 {
     try
     {
         action();
         this.counters?.Increment(SchedulerCounters.WorkitemsPerSecond);
     }
     catch (Exception e) when(this.errorHandler(e))
     {
     }
     finally
     {
         synchronizationObject.Release();
         context.Exit();
     }
 }
Beispiel #3
0
 /// <summary>
 /// Stops scheduling further work items on the given context.
 /// </summary>
 /// <param name="context">The context on which scheduling should cease.</param>
 public void StopScheduling(SchedulerContext context)
 {
     // wait for already scheduled work to finish
     this.PauseForQuiescence(context);
     context.Stop();
 }
Beispiel #4
0
 /// <summary>
 /// Starts scheduling work on the given context.
 /// </summary>
 /// <param name="context">The context on which scheduling should start.</param>
 public void StartScheduling(SchedulerContext context)
 {
     context.Start(this.Clock);
 }
Beispiel #5
0
        /// <summary>
        /// Enqueues a workitem and, if possible, kicks off a worker thread to pick it up.
        /// </summary>
        /// <param name="synchronizationObject">Synchronization object.</param>
        /// <param name="action">Action to execute.</param>
        /// <param name="startTime">Scheduled start time.</param>
        /// <param name="context">The scheduler context on which to schedule the action.</param>
        /// <param name="asContinuation">Flag whether to execute once current operation completes.</param>
        /// <param name="allowSchedulingPastFinalization">Allow scheduling past finalization time.</param>
        public void Schedule(SynchronizationLock synchronizationObject, Action action, DateTime startTime, SchedulerContext context, bool asContinuation = true, bool allowSchedulingPastFinalization = false)
        {
            if (synchronizationObject == null || action == null || context == null)
            {
                throw new ArgumentNullException();
            }

            if (!allowSchedulingPastFinalization && startTime > context.FinalizeTime)
            {
                return;
            }

            // Enter the context to track this new work item. The context will be exited
            // only after the work item has been successfully executed (or dropped).
            context.Enter();
            this.Schedule(new WorkItem()
            {
                SyncLock = synchronizationObject, Callback = action, StartTime = startTime, SchedulerContext = context
            }, asContinuation);
        }
Beispiel #6
0
        /// <summary>
        /// Attempts to execute the delegate immediately, on the calling thread, without scheduling.
        /// </summary>
        /// <param name="synchronizationObject">Synchronization object.</param>
        /// <param name="action">Action to execute.</param>
        /// <param name="startTime">Scheduled start time.</param>
        /// <param name="context">The scheduler context on which to execute the action.</param>
        /// <returns>Success flag.</returns>
        public bool TryExecute(SynchronizationLock synchronizationObject, Action action, DateTime startTime, SchedulerContext context)
        {
            if (this.forcedShutdownRequested || startTime > context.FinalizeTime)
            {
                return(true);
            }

            if (startTime > this.clock.GetCurrentTime() && this.delayFutureWorkitemsUntilDue)
            {
                return(false);
            }

            if (!this.isSchedulerThread.Value && !this.allowSchedulingOnExternalThreads)
            {
                // this thread is not ours, so return
                return(false);
            }

            // try to acquire a lock
            // however, if this thread already has the lock, we have to give up to keep the no-reentrancy guarantee
            if (!synchronizationObject.TryLock())
            {
                return(false);
            }

            // ExecuteAndRelease assumes that the context has already been entered, so we must explicitly
            // enter it here. The context will be exited just prior to returning from ExecuteAndRelease.
            context.Enter();
            this.ExecuteAndRelease(synchronizationObject, action, context);
            this.counters?.Increment(SchedulerCounters.ImmediateWorkitemsPerSecond);
            return(true);
        }
Beispiel #7
0
        /// <summary>
        /// Attempts to execute the delegate immediately, on the calling thread, without scheduling.
        /// </summary>
        /// <typeparam name="T">Action argument type.</typeparam>
        /// <param name="synchronizationObject">Synchronization object.</param>
        /// <param name="action">Action to execute.</param>
        /// <param name="argument">Action argument.</param>
        /// <param name="startTime">Scheduled start time.</param>
        /// <param name="context">The scheduler context on which to execute the action.</param>
        /// <param name="outputActionTiming">Indicates whether to output action timing information.</param>
        /// <param name="actionStartTime">The time the runtime started executing the action.</param>
        /// <param name="actionEndTime">The time the runtime ended executing the action.</param>
        /// <returns>Success flag.</returns>
        public bool TryExecute <T>(
            SynchronizationLock synchronizationObject,
            Action <T> action,
            T argument,
            DateTime startTime,
            SchedulerContext context,
            bool outputActionTiming,
            out DateTime actionStartTime,
            out DateTime actionEndTime)
        {
            actionStartTime = DateTime.MinValue;
            actionEndTime   = DateTime.MinValue;

            if (this.forcedShutdownRequested || startTime > context.FinalizeTime)
            {
                actionStartTime = actionEndTime = this.clock.GetCurrentTime();
                return(true);
            }

            if (startTime > this.clock.GetCurrentTime() && this.delayFutureWorkitemsUntilDue)
            {
                return(false);
            }

            if (!this.isSchedulerThread.Value && !this.allowSchedulingOnExternalThreads)
            {
                // this thread is not ours, so return
                return(false);
            }

            // try to acquire a lock on the sync context
            // however, if this thread already has the lock, we have to give up to keep the no-reentrancy guarantee
            if (!synchronizationObject.TryLock())
            {
                return(false);
            }

            try
            {
                // Unlike ExecuteAndRelease, which assumes that the context has already been entered (e.g.
                // when the work item was first created), we need to explicitly enter the context prior to
                // running the action. The context will be exited in the finally clause.
                context.Enter();

                this.counters?.Increment(SchedulerCounters.WorkitemsPerSecond);
                this.counters?.Increment(SchedulerCounters.ImmediateWorkitemsPerSecond);

                if (outputActionTiming)
                {
                    actionStartTime = this.clock.GetCurrentTime();
                }

                action(argument);
            }
            catch (Exception e) when(this.errorHandler(e))
            {
            }
            finally
            {
                if (outputActionTiming)
                {
                    actionEndTime = this.clock.GetCurrentTime();
                }

                synchronizationObject.Release();
                context.Exit();
            }

            return(true);
        }