/// <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); }
// executes the delegate immediately, on the calling thread, without scheduling public bool TryExecute(SynchronizationLock synchronizationObject, Action action, DateTime startTime) { if (this.forcedShutdownRequested) { 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 (!TryGetExclusiveLock(synchronizationObject)) { return(false); } this.ExecuteAndRelease(synchronizationObject, action); this.counters?.Increment(SchedulerCounters.ImmediateWorkitemsPerSecond); return(true); }
/// <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(); } // 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 }); }
private void ExecuteAndRelease(SynchronizationLock synchronizationObject, Action action) { try { action(); this.counters?.Increment(SchedulerCounters.WorkitemsPerSecond); } catch (Exception e) when(this.errorHandler(e)) { } finally { ReleaseExclusiveLock(synchronizationObject); } }
/// <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> /// <returns>Success flag.</returns> public bool TryExecute <T>(SynchronizationLock synchronizationObject, Action <T> action, T argument, 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 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(); action(argument); this.counters?.Increment(SchedulerCounters.WorkitemsPerSecond); this.counters?.Increment(SchedulerCounters.ImmediateWorkitemsPerSecond); } catch (Exception e) when(this.errorHandler(e)) { } finally { synchronizationObject.Release(); context.Exit(); } return(true); }
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(); } }
/// <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> /// <returns>Success flag</returns> public bool TryExecute <T>(SynchronizationLock synchronizationObject, Action <T> action, T argument, DateTime startTime) { if (this.forcedShutdownRequested || startTime > this.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 on the sync context // however, if this thread already has the lock, we have to give up to keep the no-reentrancy guarantee if (!TryGetExclusiveLock(synchronizationObject)) { return(false); } try { action(argument); this.counters?.Increment(SchedulerCounters.WorkitemsPerSecond); this.counters?.Increment(SchedulerCounters.ImmediateWorkitemsPerSecond); } catch (Exception e) when(this.errorHandler(e)) { } finally { ReleaseExclusiveLock(synchronizationObject); } return(true); }
public PriorityQueueNode(int id) { this.id = id; this.simpleLock = new SynchronizationLock(this, false); }
/// <summary> /// Allow further scheduling. /// </summary> /// <param name="synchronizationObject">Synchronization object.</param> public void Thaw(SynchronizationLock synchronizationObject) { synchronizationObject.Release(); }
/// <summary> /// Prevent further scheduling. /// </summary> /// <param name="synchronizationObject">Synchronization object.</param> public void Freeze(SynchronizationLock synchronizationObject) { synchronizationObject.Hold(); }
private static void ReleaseExclusiveLock(SynchronizationLock synchronizationObject) { synchronizationObject.Release(); }
private static bool TryGetExclusiveLock(SynchronizationLock syncLock) { return(syncLock.TryLock()); }
// enqueues a workitem and, if possible, kicks off a worker thread to pick it up public void Schedule(SynchronizationLock synchronizationObject, Action action, DateTime startTime, bool asContinuation = true) { if (this.forcedShutdownRequested) { return; } if (synchronizationObject == null || action == null) { throw new ArgumentNullException(); } WorkItem wi = new WorkItem() { SyncLock = synchronizationObject, Callback = action, StartTime = startTime }; // if the workitem not yet due, add it to the future workitem queue if (startTime > this.clock.GetCurrentTime() && this.delayFutureWorkitemsUntilDue) { this.futureWorkitems.Enqueue(wi); this.futureAdded.Set(); return; } // try to kick off another thread now, without going through the global queue if (TryGetExclusiveLock(synchronizationObject)) { if (this.threadSemaphore.TryEnter()) { // there are threads available, which means the items in the global queue, if any, are locked // start a thread to do the work this.counters?.Increment(SchedulerCounters.ImmediateWorkitemsPerSecond); ThreadPool.QueueUserWorkItem(this.Run, wi); return; } ReleaseExclusiveLock(synchronizationObject); } // try to schedule on the local thread, to be executed once the current operation finishes, as long as the new workitem time is the same or less as the current one if (asContinuation && this.isSchedulerThread.Value && this.nextWorkitem.Value == null && wi.StartTime <= this.currentWorkitemTime.Value) { // we own the thread, so schedule the work in the local queue this.nextWorkitem.Value = wi; this.counters?.Increment(SchedulerCounters.LocalQueueCount); return; } // last resort, add the workitem to the global queue this.globalWorkitems.Enqueue(wi); // if a thread became available, it might have missed the workitem being enqueued, so retry to make sure if (this.threadSemaphore.TryEnter()) { if (this.globalWorkitems.TryDequeue(out wi)) { this.counters?.Increment(SchedulerCounters.GlobalWorkitemsPerSecond); ThreadPool.QueueUserWorkItem(this.Run, wi); } else { this.threadSemaphore.Exit(); } } }
/// <summary> /// Initializes a new instance of the <see cref="SchedulerContext"/> class. /// </summary> public SchedulerContext() { this.syncLock = new SynchronizationLock(this); }