internal void AddPendingEvent(PendingEvent ev) { if (IsShuttingDown) // don't care about adding anything if we're shutting down { return; } lock (_lockHeap) { _eventHeap.Push(ev); } }
// returns the index of the added item private int Add(PendingEvent ev) { // check if we need to resize if (_events.Length == Count) { var bigger = new PendingEvent[_events.Length * 2]; Array.Copy(_events, bigger, Count); _events = bigger; } _events[Count] = ev; return(Count++); // postfix is intentional }
public void Push(PendingEvent ev) { Guard.NotNull(() => ev, ev); var ei = Add(ev); while (ei > 0) { var pi = ParentIndex(ei); if (ev.IsEarlierThan(_events[pi])) { Swap(ei, pi); ei = pi; } else { break; } } }
internal bool IsEarlierThan(PendingEvent ev) { return(ScheduledTime < ev.ScheduledTime); }
internal async Task RunPendingEventAsync(PendingEvent ev) { var eventTime = ev.ScheduledTime; var execLockTaken = false; try { lock (_scheduleLock) { if (ev.RunId != _runId) { return; } // take execution lock execLockTaken = Interlocked.CompareExchange(ref _execLocked, 1, 0) == 0; if (execLockTaken) { PrevEvent = eventTime; // set this here while we're still in the schedule lock } } if (execLockTaken) { try { #if NETFULL var result = _expressionToRun != null ? await _queue.SendAsync(this, eventTime, _expressionToRun).ConfigureAwait(false) : await _queue.SendAsync(this, eventTime, _actionToRun, RawExpression).ConfigureAwait(false); #else var result = await _queue.SendAsync(this, eventTime, _actionToRun, RawExpression).ConfigureAwait(false); #endif if (result.Status == JobQueuedStatus.Success || result.Status == JobQueuedStatus.RequeuedDueToErrorStatus) { RaiseEnQueue(result); _queue.Logger.LogDebug($"job {this} queued"); } else if (result.Status == JobQueuedStatus.AlreadyQueuedWaiting || result.Status == JobQueuedStatus.AlreadyQueuedProcessing || result.Status == JobQueuedStatus.AlreadyProcessed) { _queue.Logger.LogWarning($"Failed to enqueue job {this}, the status is {result.Status}"); RaiseNonFatalFailureEnQueue(result); } else if (result.SendingException != null) { _queue.Logger.LogError($"An error has occurred adding job {this} into the queue{System.Environment.NewLine}{result.SendingException}"); RaiseException(result.SendingException); } } catch (Exception ex) { _queue.Logger.LogError($"A fatal error has occurred trying to add job {this} into the queue{System.Environment.NewLine}{ex}"); RaiseException(ex); } } } finally { if (execLockTaken) { _execLocked = 0; // release exec lock } } // figure out the next time to run the schedule lock (_scheduleLock) { if (ev.RunId != _runId) { return; } try { var next = Schedule.Next(); if (next <= eventTime) { next = Schedule.Next(eventTime); } NextEvent = next; QueueNextEvent(); } catch (Exception ex) { _runId++; IsScheduleRunning = false; RaiseException(new DotNetWorkQueueException("Schedule has been terminated because the next valid time could not be found.", ex)); } } }
internal async Task RunPendingEvent(PendingEvent ev) { var eventTime = ev.ScheduledTime; var execLockTaken = false; try { lock (_scheduleLock) { if (ev.RunId != _runId) return; // take execution lock execLockTaken = Interlocked.CompareExchange(ref _execLocked, 1, 0) == 0; if (execLockTaken) PrevEvent = eventTime; // set this here while we're still in the schedule lock } if (execLockTaken) { try { var result = _expressionToRun != null ? await _queue.SendAsync(this, eventTime, _expressionToRun).ConfigureAwait(false) : await _queue.SendAsync(this, eventTime, _actionToRun).ConfigureAwait(false); if (result.Status == JobQueuedStatus.Success || result.Status == JobQueuedStatus.RequeuedDueToErrorStatus) { RaiseEnQueue(result); _queue.Logger.Log(LogLevel.Debug, () => $"job {this} has been queued"); } else if (result.Status == JobQueuedStatus.AlreadyQueuedWaiting || result.Status == JobQueuedStatus.AlreadyQueuedProcessing || result.Status == JobQueuedStatus.AlreadyProcessed) { _queue.Logger.Log(LogLevel.Warn, () => $"Failed to enqueue job {this}, the status is {result.Status}"); RaiseNonFatalFailtureEnQueue(result); } else if (result.SendingException != null) { _queue.Logger.ErrorException($"An error has occurred adding job {this} into the queue", result.SendingException); RaiseException(result.SendingException); } } catch (Exception ex) { _queue.Logger.ErrorException($"A fatal error has occurred trying to add job {this} into the queue", ex); RaiseException(ex); } } } finally { if (execLockTaken) _execLocked = 0; // release exec lock } // figure out the next time to run the schedule lock (_scheduleLock) { if (ev.RunId != _runId) return; try { var next = Schedule.Next(); if (next <= eventTime) next = Schedule.Next(eventTime); NextEvent = next; QueueNextEvent(); } catch (Exception ex) { _runId++; IsScheduleRunning = false; RaiseException(new DotNetWorkQueueException("Schedule has been terminated because the next valid time could not be found.", ex)); } } }
internal bool IsEarlierThan(PendingEvent ev) { return ScheduledTime < ev.ScheduledTime; }