public TriggerFiredBundle TriggerFired(Trigger trigger) { lock (_triggerLock) { TriggerWrapper tw = _triggersDictionary[trigger.Name] as TriggerWrapper; // was the trigger deleted since being acquired? if (tw == null || tw.Trigger == null) return null; // was the trigger completed since being acquired? if (tw.State == InternalTriggerState.Complete) return null; // was the trigger paused since being acquired? if (tw.State == InternalTriggerState.Paused) return null; // was the trigger blocked since being acquired? if (tw.State == InternalTriggerState.Blocked) return null; // was the trigger paused and blocked since being acquired? if (tw.State == InternalTriggerState.PausedAndBlocked) return null; NullableDateTime prevFireTime = trigger.GetPreviousFireTimeUtc(); // in case trigger was replaced between acquiring and firering _timeTriggers.Remove(tw); trigger.Triggered(); //tw.state = TriggerWrapper.StateExecuting; tw.State = InternalTriggerState.Waiting; IScheduledJob job = RetrieveJob(trigger.JobName); TriggerFiredBundle bndle = new TriggerFiredBundle(job, trigger, false, DateTime.UtcNow, trigger.GetPreviousFireTimeUtc(), prevFireTime, trigger.GetNextFireTimeUtc()); NullableDateTime d = tw.Trigger.GetNextFireTimeUtc(); if (d.HasValue) { lock (_triggerLock) { _timeTriggers.Add(tw); } } return bndle; } }
/// <summary> /// The main processing loop. /// </summary> public void Run() { bool lastAcquireFailed = false; while (!_halted) { try { // check if we're supposed to pause... lock (_pauseLock) { while (_paused && !_halted) { try { // wait until togglePause(false) is called... Monitor.Wait(_pauseLock, 100); } catch (ThreadInterruptedException) { } } if (_halted) { break; } } int availTreadCount = _threadPool.AvailableThreads; DateTime now; int spinInterval; int numPauses; if (availTreadCount > 0) { Trigger trigger = null; now = DateTime.UtcNow; _signaled = false; try { trigger = AcquireNextTrigger(now.AddMilliseconds(_idleWaitTime)); lastAcquireFailed = false; } catch (Exception e) { if (!lastAcquireFailed) { log.Error("SchedulerThreadLoop: RuntimeException " + e.Message, e); } lastAcquireFailed = true; } if (trigger != null) { now = DateTime.UtcNow; DateTime triggerTime = trigger.GetNextFireTimeUtc().Value; long timeUntilTrigger = (long)(triggerTime - now).TotalMilliseconds; spinInterval = 10; // this looping may seem a bit silly, but it's the // current work-around // for a dead-lock that can occur if the Thread.sleep() // is replaced with // a obj.wait() that gets notified when the signal is // set... // so to be able to detect the signal change without // sleeping the entire // timeUntilTrigger, we spin here... don't worry // though, this spinning // doesn't even register 0.2% cpu usage on a pentium 4. numPauses = (int)(timeUntilTrigger / spinInterval); while (numPauses >= 0 && !_signaled) { try { Thread.Sleep(spinInterval); } catch (ThreadInterruptedException) { } now = DateTime.UtcNow; timeUntilTrigger = (long)(triggerTime - now).TotalMilliseconds; numPauses = (int)(timeUntilTrigger / spinInterval); } if (_signaled) { try { ReleaseAcquiredTrigger(trigger); } catch (Exception ex) { log.Error("ReleaseAcquiredTrigger: RuntimeException " + ex.Message, ex); } _signaled = false; continue; } // set trigger to 'executing' TriggerFiredBundle bundle = null; lock (_pauseLock) { if (!_halted) { try { bundle = TriggerFired(trigger); } catch (Exception ex) { log.Error(string.Format(CultureInfo.InvariantCulture, "RuntimeException while firing trigger {0}", trigger.Name), ex); } } // it's possible to get 'null' if the trigger was paused, // blocked, or other similar occurances that prevent it being // fired at this time... or if the scheduler was shutdown (halted) if (bundle == null) { try { ReleaseAcquiredTrigger(trigger); } catch (SchedulerException) { } continue; } _threadPool.QueueUserWorkItem(new WaitCallback(ProcessJob), bundle); } continue; } } else { // if(availTreadCount > 0) continue; // should never happen, if threadPool.blockForAvailableThreads() follows contract } // this looping may seem a bit silly, but it's the current // work-around // for a dead-lock that can occur if the Thread.sleep() is replaced // with // a obj.wait() that gets notified when the signal is set... // so to be able to detect the signal change without sleeping the // entier // getRandomizedIdleWaitTime(), we spin here... don't worry though, // the // CPU usage of this spinning can't even be measured on a pentium // 4. now = DateTime.UtcNow; DateTime waitTime = now.AddMilliseconds(GetRandomizedIdleWaitTime()); long timeUntilContinue = (long)(waitTime - now).TotalMilliseconds; spinInterval = 10; numPauses = (int)(timeUntilContinue / spinInterval); while (numPauses > 0 && !_signaled) { try { Thread.Sleep(10); } catch (ThreadInterruptedException) { } now = DateTime.UtcNow; timeUntilContinue = (long)(waitTime - now).TotalMilliseconds; numPauses = (int)(timeUntilContinue / spinInterval); } } catch (ThreadAbortException) { } catch (Exception ex) { log.Error("Runtime error occured in main trigger firing loop.", ex); } } }