/// <summary>
        /// Construct a new <see cref="QuartzSchedulerThread" /> for the given
        /// <see cref="QuartzScheduler" /> as a <see cref="Thread" /> with the given
        /// attributes.
        /// </summary>
        internal QuartzSchedulerThread(QuartzScheduler qs, QuartzSchedulerResources qsRsrcs,
                                       bool setDaemon, int threadPrio)
            : base(qsRsrcs.ThreadName)
        {
            log = LogManager.GetLogger(GetType().FullName);
            //ThreadGroup generatedAux = qs.SchedulerThreadGroup;
            this.qs      = qs;
            this.qsRsrcs = qsRsrcs;
            IsBackground = setDaemon;
            Priority     = (ThreadPriority)threadPrio;

            // start the underlying thread, but put this object into the 'paused'
            // state
            // so processing doesn't start yet...
            paused = true;
            halted = false;
        }
        /// <summary>
        /// The main processing loop of the <see cref="QuartzSchedulerThread" />.
        /// </summary>
        public override void Run()
        {
            bool lastAcquireFailed = false;

            while (!halted)
            {
                try
                {
                    // check if we're supposed to pause...
                    lock (sigLock)
                    {
                        while (paused && !halted)
                        {
                            try
                            {
                                // wait until togglePause(false) is called...
                                Monitor.Wait(sigLock, 1000);
                            }
                            catch (ThreadInterruptedException)
                            {
                            }
                        }

                        if (halted)
                        {
                            break;
                        }
                    }

                    int availThreadCount = qsRsrcs.ThreadPool.BlockForAvailableThreads();
                    if (availThreadCount > 0) // will always be true, due to semantics of blockForAvailableThreads...
                    {
                        IList <IOperableTrigger> triggers = null;

                        DateTimeOffset now = SystemTime.UtcNow();

                        ClearSignaledSchedulingChange();
                        try
                        {
                            triggers = qsRsrcs.JobStore.AcquireNextTriggers(
                                now + idleWaitTime, Math.Min(availThreadCount, qsRsrcs.MaxBatchSize), qsRsrcs.BatchTimeWindow);
                            lastAcquireFailed = false;
                            if (log.IsDebugEnabled)
                            {
                                log.Debug("Batch acquisition of {0} triggers", (triggers == null ? 0 : triggers.Count));
                            }
                        }
                        catch (JobPersistenceException jpe)
                        {
                            if (!lastAcquireFailed)
                            {
                                qs.NotifySchedulerListenersError(
                                    "An error occurred while scanning for the next trigger to fire.",
                                    jpe);
                            }
                            lastAcquireFailed = true;
                        }
                        catch (Exception e)
                        {
                            if (!lastAcquireFailed)
                            {
                                Log.Error("quartzSchedulerThreadLoop: RuntimeException "
                                          + e.Message, e);
                            }
                            lastAcquireFailed = true;
                        }

                        if (triggers != null && triggers.Count > 0)
                        {
                            now = SystemTime.UtcNow();
                            DateTimeOffset triggerTime      = triggers[0].GetNextFireTimeUtc().Value;
                            TimeSpan       timeUntilTrigger = triggerTime - now;

                            while (timeUntilTrigger > TimeSpan.FromMilliseconds(2))
                            {
                                if (ReleaseIfScheduleChangedSignificantly(triggers, triggerTime))
                                {
                                    break;
                                }
                                lock (sigLock)
                                {
                                    if (halted)
                                    {
                                        break;
                                    }
                                    if (!IsCandidateNewTimeEarlierWithinReason(triggerTime, false))
                                    {
                                        try
                                        {
                                            // we could have blocked a long while
                                            // on 'synchronize', so we must recompute
                                            now = SystemTime.UtcNow();
                                            timeUntilTrigger = triggerTime - now;
                                            if (timeUntilTrigger > TimeSpan.Zero)
                                            {
                                                Monitor.Wait(sigLock, timeUntilTrigger);
                                            }
                                        }
                                        catch (ThreadInterruptedException)
                                        {
                                        }
                                    }
                                }
                                if (ReleaseIfScheduleChangedSignificantly(triggers, triggerTime))
                                {
                                    break;
                                }
                                now = SystemTime.UtcNow();
                                timeUntilTrigger = triggerTime - now;
                            }

                            // this happens if releaseIfScheduleChangedSignificantly decided to release triggers
                            if (triggers.Count == 0)
                            {
                                continue;
                            }

                            // set triggers to 'executing'
                            IList <TriggerFiredResult> bndles = new List <TriggerFiredResult>();

                            bool goAhead = true;
                            lock (sigLock)
                            {
                                goAhead = !halted;
                            }

                            if (goAhead)
                            {
                                try
                                {
                                    IList <TriggerFiredResult> res = qsRsrcs.JobStore.TriggersFired(triggers);
                                    if (res != null)
                                    {
                                        bndles = res;
                                    }
                                }
                                catch (SchedulerException se)
                                {
                                    qs.NotifySchedulerListenersError("An error occurred while firing triggers '" + triggers + "'", se);
                                    // QTZ-179 : a problem occurred interacting with the triggers from the db
                                    // we release them and loop again
                                    foreach (IOperableTrigger t in triggers)
                                    {
                                        ReleaseTriggerRetryLoop(t);
                                    }
                                    continue;
                                }
                            }

                            for (int i = 0; i < bndles.Count; i++)
                            {
                                TriggerFiredResult result    = bndles[i];
                                TriggerFiredBundle bndle     = result.TriggerFiredBundle;
                                Exception          exception = result.Exception;

                                IOperableTrigger trigger = triggers[i];
                                // TODO SQL exception?
                                if (exception != null && (exception is DbException || exception.InnerException is DbException))
                                {
                                    Log.Error("DbException while firing trigger " + trigger, exception);
                                    // db connection must have failed... keep
                                    // retrying until it's up...
                                    ReleaseTriggerRetryLoop(trigger);
                                    continue;
                                }

                                // it's possible to get 'null' if the triggers was paused,
                                // blocked, or other similar occurrences that prevent it being
                                // fired at this time...  or if the scheduler was shutdown (halted)
                                if (bndle == null)
                                {
                                    try
                                    {
                                        qsRsrcs.JobStore.ReleaseAcquiredTrigger(trigger);
                                    }
                                    catch (SchedulerException se)
                                    {
                                        qs.NotifySchedulerListenersError(
                                            "An error occurred while releasing triggers '" + trigger.Key + "'", se);
                                        // db connection must have failed... keep retrying
                                        // until it's up...
                                        ReleaseTriggerRetryLoop(trigger);
                                    }
                                    continue;
                                }

                                // TODO: improvements:
                                //
                                // 2- make sure we can get a job runshell before firing trigger, or
                                //   don't let that throw an exception (right now it never does,
                                //   but the signature says it can).
                                // 3- acquire more triggers at a time (based on num threads available?)

                                JobRunShell shell = null;
                                try
                                {
                                    shell = qsRsrcs.JobRunShellFactory.CreateJobRunShell(bndle);
                                    shell.Initialize(qs);
                                }
                                catch (SchedulerException)
                                {
                                    try
                                    {
                                        qsRsrcs.JobStore.TriggeredJobComplete(trigger, bndle.JobDetail,
                                                                              SchedulerInstruction.SetAllJobTriggersError);
                                    }
                                    catch (SchedulerException se2)
                                    {
                                        qs.NotifySchedulerListenersError(
                                            "An error occurred while placing job's triggers in error state '" +
                                            trigger.Key + "'", se2);
                                        // db connection must have failed... keep retrying
                                        // until it's up...
                                        ErrorTriggerRetryLoop(bndle);
                                    }
                                    continue;
                                }

                                if (qsRsrcs.ThreadPool.RunInThread(shell) == false)
                                {
                                    try
                                    {
                                        // this case should never happen, as it is indicative of the
                                        // scheduler being shutdown or a bug in the thread pool or
                                        // a thread pool being used concurrently - which the docs
                                        // say not to do...
                                        Log.Error("ThreadPool.runInThread() return false!");
                                        qsRsrcs.JobStore.TriggeredJobComplete(trigger, bndle.JobDetail,
                                                                              SchedulerInstruction.
                                                                              SetAllJobTriggersError);
                                    }
                                    catch (SchedulerException se2)
                                    {
                                        qs.NotifySchedulerListenersError(
                                            string.Format(CultureInfo.InvariantCulture,
                                                          "An error occurred while placing job's triggers in error state '{0}'",
                                                          trigger.Key), se2);
                                        // db connection must have failed... keep retrying
                                        // until it's up...
                                        ReleaseTriggerRetryLoop(trigger);
                                    }
                                }
                            }

                            continue; // while (!halted)
                        }
                    }
                    else // if(availThreadCount > 0)
                    {
                        // should never happen, if threadPool.blockForAvailableThreads() follows contract
                        continue;
                        // while (!halted)
                    }

                    DateTimeOffset utcNow            = SystemTime.UtcNow();
                    DateTimeOffset waitTime          = utcNow.Add(GetRandomizedIdleWaitTime());
                    TimeSpan       timeUntilContinue = waitTime - utcNow;
                    lock (sigLock)
                    {
                        if (!halted)
                        {
                            try
                            {
                                Monitor.Wait(sigLock, timeUntilContinue);
                            }
                            catch (ThreadInterruptedException)
                            {
                            }
                        }
                    }
                }
                catch (Exception re)
                {
                    if (Log != null)
                    {
                        Log.Error("Runtime error occurred in main trigger firing loop.", re);
                    }
                }
            } // while (!halted)

            // drop references to scheduler stuff to aid garbage collection...
            qs      = null;
            qsRsrcs = null;
        }
 /// <summary>
 /// Construct a new <see cref="QuartzSchedulerThread" /> for the given
 /// <see cref="QuartzScheduler" /> as a non-daemon <see cref="Thread" />
 /// with normal priority.
 /// </summary>
 internal QuartzSchedulerThread(QuartzScheduler qs, QuartzSchedulerResources qsRsrcs)
     : this(qs, qsRsrcs, qsRsrcs.MakeSchedulerThreadDaemon, (int)ThreadPriority.Normal)
 {
 }