Beispiel #1
0
        /// <summary>
        /// Initializes the job execution context with given scheduler and bundle.
        /// </summary>
        /// <param name="sched">The scheduler.</param>
        /// <param name="firedBundle">The bundle offired triggers.</param>
        public virtual void Initialize(QuartzScheduler sched, TriggerFiredBundle firedBundle)
        {
            qs = sched;

            IJob      job;
            JobDetail jobDetail = firedBundle.JobDetail;

            try
            {
                job = sched.JobFactory.NewJob(firedBundle);
            }
            catch (SchedulerException se)
            {
                sched.NotifySchedulerListenersError(string.Format(CultureInfo.InvariantCulture, "An error occured instantiating job to be executed. job= '{0}'", jobDetail.FullName), se);
                throw;
            }
            catch (Exception e)
            {
                SchedulerException se = new SchedulerException(string.Format(CultureInfo.InvariantCulture, "Problem instantiating type '{0}'", jobDetail.JobType.FullName), e);
                sched.NotifySchedulerListenersError(string.Format(CultureInfo.InvariantCulture, "An error occured instantiating job to be executed. job= '{0}'", jobDetail.FullName), se);
                throw se;
            }

            jec = new JobExecutionContext(scheduler, firedBundle, job);
        }
Beispiel #2
0
        /// <summary>
        /// Initializes the job execution context with given scheduler and bundle.
        /// </summary>
        /// <param name="sched">The scheduler.</param>
        /// <param name="cancellationToken">The cancellation instruction.</param>
        public virtual async Task Initialize(
            QuartzScheduler sched,
            CancellationToken cancellationToken = default)
        {
            qs = sched;

            IJob       job;
            IJobDetail jobDetail = firedTriggerBundle.JobDetail;

            try
            {
                job = sched.JobFactory.NewJob(firedTriggerBundle, scheduler);
            }
            catch (SchedulerException se)
            {
                await sched.NotifySchedulerListenersError($"An error occurred instantiating job to be executed. job= '{jobDetail.Key}'", se, cancellationToken).ConfigureAwait(false);

                throw;
            }
            catch (Exception e)
            {
                SchedulerException se = new SchedulerException($"Problem instantiating type '{jobDetail.JobType.FullName}: {e.Message}'", e);
                await sched.NotifySchedulerListenersError($"An error occurred instantiating job to be executed. job= '{jobDetail.Key}, message={e.Message}'", se, cancellationToken).ConfigureAwait(false);

                throw se;
            }

            jec = new JobExecutionContextImpl(scheduler, firedTriggerBundle, job);
        }
 /// <summary>
 /// Notifies the scheduler about misfired trigger.
 /// </summary>
 /// <param name="trigger">The trigger that misfired.</param>
 public virtual async Task NotifyTriggerListenersMisfired(ITrigger trigger)
 {
     try
     {
         await sched.NotifyTriggerListenersMisfired(trigger).ConfigureAwait(false);
     }
     catch (SchedulerException se)
     {
         log.ErrorException("Error notifying listeners of trigger misfire.", se);
         await sched.NotifySchedulerListenersError("Error notifying listeners of trigger misfire.", se).ConfigureAwait(false);
     }
 }
 /// <summary>
 /// Notifies the scheduler about misfired trigger.
 /// </summary>
 /// <param name="trigger">The trigger that misfired.</param>
 public virtual void NotifyTriggerListenersMisfired(ITrigger trigger)
 {
     try
     {
         sched.NotifyTriggerListenersMisfired(trigger);
     }
     catch (SchedulerException se)
     {
         log.Error("Error notifying listeners of trigger misfire.{0}", se);
         sched.NotifySchedulerListenersError("Error notifying listeners of trigger misfire.", se);
     }
 }
 /// <summary>
 /// Notifies the scheduler about misfired trigger.
 /// </summary>
 /// <param name="trigger">The trigger that misfired.</param>
 /// <param name="cancellationToken">The cancellation instruction.</param>
 public virtual async Task NotifyTriggerListenersMisfired(
     ITrigger trigger,
     CancellationToken cancellationToken = default(CancellationToken))
 {
     try
     {
         await sched.NotifyTriggerListenersMisfired(trigger, cancellationToken).ConfigureAwait(false);
     }
     catch (SchedulerException se)
     {
         log.ErrorException("Error notifying listeners of trigger misfire.", se);
         await sched.NotifySchedulerListenersError("Error notifying listeners of trigger misfire.", se, cancellationToken).ConfigureAwait(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.DebugFormat("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;
                            continue;
                        }
                        catch (Exception e)
                        {
                            if (!lastAcquireFailed)
                            {
                                Log.Error("quartzSchedulerThreadLoop: RuntimeException " + e.Message, e);
                            }
                            lastAcquireFailed = true;
                            continue;
                        }

                        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)
                                    {
                                        qsRsrcs.JobStore.ReleaseAcquiredTrigger(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);
                                    qsRsrcs.JobStore.ReleaseAcquiredTrigger(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)
                                {
                                    qsRsrcs.JobStore.ReleaseAcquiredTrigger(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)
                                {
                                    qsRsrcs.JobStore.TriggeredJobComplete(trigger, bndle.JobDetail, SchedulerInstruction.SetAllJobTriggersError);
                                    continue;
                                }

                                if (qsRsrcs.ThreadPool.RunInThread(shell) == false)
                                {
                                    // 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);
                                }
                            }

                            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
                            {
                                // QTZ-336 A job might have been completed in the mean time and we might have
                                // missed the scheduled changed signal by not waiting for the notify() yet
                                // Check that before waiting for too long in case this very job needs to be
                                // scheduled very soon
                                if (!IsScheduleChanged())
                                {
                                    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>
        /// 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 availTreadCount = qsRsrcs.ThreadPool.BlockForAvailableThreads();
                    if (availTreadCount > 0) // will always be true, due to semantics of blockForAvailableThreads...
                    {
                        Trigger trigger = null;

                        DateTime now = DateTime.UtcNow;

                        ClearSignaledSchedulingChange();
                        try
                        {
                            trigger           = qsRsrcs.JobStore.AcquireNextTrigger(ctxt, now.Add(idleWaitTime));
                            lastAcquireFailed = false;
                        }
                        catch (JobPersistenceException jpe)
                        {
                            if (!lastAcquireFailed)
                            {
                                qs.NotifySchedulerListenersError(
                                    "An error occured 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 (trigger != null)
                        {
                            now = DateTime.UtcNow;
                            DateTime triggerTime      = trigger.GetNextFireTimeUtc().Value;
                            TimeSpan timeUntilTrigger = triggerTime - now;

                            while (timeUntilTrigger > TimeSpan.Zero)
                            {
                                lock (sigLock)
                                {
                                    try
                                    {
                                        // we could have blocked a long while
                                        // on 'synchronize', so we must recompute
                                        now = DateTime.UtcNow;
                                        timeUntilTrigger = triggerTime - now;
                                        if (timeUntilTrigger.TotalMilliseconds > 1)
                                        {
                                            Monitor.Wait(sigLock, timeUntilTrigger);
                                        }
                                    }
                                    catch (ThreadInterruptedException)
                                    {
                                    }
                                }
                                if (IsScheduleChanged())
                                {
                                    if (IsCandidateNewTimeEarlierWithinReason(triggerTime))
                                    {
                                        // above call does a clearSignaledSchedulingChange()
                                        try
                                        {
                                            qsRsrcs.JobStore.ReleaseAcquiredTrigger(ctxt, trigger);
                                        }
                                        catch (JobPersistenceException jpe)
                                        {
                                            qs.NotifySchedulerListenersError(
                                                "An error occured while releasing trigger '"
                                                + trigger.FullName + "'",
                                                jpe);
                                            // db connection must have failed... keep
                                            // retrying until it's up...
                                            ReleaseTriggerRetryLoop(trigger);
                                        }
                                        catch (Exception e)
                                        {
                                            Log.Error(
                                                "releaseTriggerRetryLoop: RuntimeException "
                                                + e.Message, e);
                                            // db connection must have failed... keep
                                            // retrying until it's up...
                                            ReleaseTriggerRetryLoop(trigger);
                                        }
                                        trigger = null;
                                        break;
                                    }
                                }
                                now = DateTime.UtcNow;
                                timeUntilTrigger = triggerTime - now;
                            }

                            if (trigger == null)
                            {
                                continue;
                            }

                            // set trigger to 'executing'
                            TriggerFiredBundle bndle = null;

                            bool goAhead = true;
                            lock (sigLock)
                            {
                                goAhead = !halted;
                            }
                            if (goAhead)
                            {
                                try
                                {
                                    bndle = qsRsrcs.JobStore.TriggerFired(ctxt,
                                                                          trigger);
                                }
                                catch (SchedulerException se)
                                {
                                    qs.NotifySchedulerListenersError(
                                        string.Format(CultureInfo.InvariantCulture,
                                                      "An error occured while firing trigger '{0}'",
                                                      trigger.FullName), se);
                                }
                                catch (Exception e)
                                {
                                    Log.Error(
                                        string.Format(CultureInfo.InvariantCulture,
                                                      "RuntimeException while firing trigger {0}", trigger.FullName),
                                        e);
                                    // db connection must have failed... keep
                                    // retrying until it's up...
                                    ReleaseTriggerRetryLoop(trigger);
                                }
                            }

                            // it's possible to get 'null' if the trigger 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(ctxt,
                                                                            trigger);
                                }
                                catch (SchedulerException se)
                                {
                                    qs.NotifySchedulerListenersError(
                                        string.Format(CultureInfo.InvariantCulture, "An error occured while releasing trigger '{0}'",
                                                      trigger.FullName), 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;
                            try
                            {
                                shell = qsRsrcs.JobRunShellFactory.BorrowJobRunShell();
                                shell.Initialize(qs, bndle);
                            }
                            catch (SchedulerException)
                            {
                                try
                                {
                                    qsRsrcs.JobStore.TriggeredJobComplete(ctxt,
                                                                          trigger, bndle.JobDetail,
                                                                          SchedulerInstruction.
                                                                          SetAllJobTriggersError);
                                }
                                catch (SchedulerException se2)
                                {
                                    qs.NotifySchedulerListenersError(
                                        string.Format(
                                            CultureInfo.InvariantCulture,
                                            "An error occured while placing job's triggers in error state '{0}'",
                                            trigger.FullName), 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(ctxt,
                                                                          trigger, bndle.JobDetail,
                                                                          SchedulerInstruction.
                                                                          SetAllJobTriggersError);
                                }
                                catch (SchedulerException se2)
                                {
                                    qs.NotifySchedulerListenersError(
                                        string.Format(CultureInfo.InvariantCulture,
                                                      "An error occured while placing job's triggers in error state '{0}'",
                                                      trigger.FullName), se2);
                                    // db connection must have failed... keep retrying
                                    // until it's up...
                                    ReleaseTriggerRetryLoop(trigger);
                                }
                            }

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

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

            // drop references to scheduler stuff to aid garbage collection...
            qs      = null;
            qsRsrcs = null;
        }
        /// <summary>
        /// The main processing loop of the <see cref="QuartzSchedulerThread" />.
        /// </summary>
        public async Task Run()
        {
            int acquiresFailed = 0;

            while (!halted)
            {
                cancellationTokenSource.Token.ThrowIfCancellationRequested();
                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)
                            {
                            }

                            // reset failure counter when paused, so that we don't
                            // wait again after unpausing
                            acquiresFailed = 0;
                        }

                        if (halted)
                        {
                            break;
                        }
                    }

                    // wait a bit, if reading from job store is consistently
                    // failing (e.g. DB is down or restarting)..
                    if (acquiresFailed > 1)
                    {
                        try
                        {
                            var delay = ComputeDelayForRepeatedErrors(qsRsrcs.JobStore, acquiresFailed);
                            await Task.Delay(delay);
                        }
                        catch
                        {
                        }
                    }

                    cancellationTokenSource.Token.ThrowIfCancellationRequested();
                    int availThreadCount = qsRsrcs.ThreadPool.BlockForAvailableThreads();
                    if (availThreadCount > 0)
                    {
                        List <IOperableTrigger> triggers;

                        DateTimeOffset now = SystemTime.UtcNow();

                        ClearSignaledSchedulingChange();
                        try
                        {
                            var noLaterThan = now + idleWaitTime;
                            var maxCount    = Math.Min(availThreadCount, qsRsrcs.MaxBatchSize);
                            triggers       = new List <IOperableTrigger>(await qsRsrcs.JobStore.AcquireNextTriggers(noLaterThan, maxCount, qsRsrcs.BatchTimeWindow, CancellationToken.None).ConfigureAwait(false));
                            acquiresFailed = 0;
                            if (Log.IsDebugEnabled())
                            {
                                Log.DebugFormat("Batch acquisition of {0} triggers", triggers?.Count ?? 0);
                            }
                        }
                        catch (JobPersistenceException jpe)
                        {
                            if (acquiresFailed == 0)
                            {
                                var msg = "An error occurred while scanning for the next trigger to fire.";
                                await qs.NotifySchedulerListenersError(msg, jpe, CancellationToken.None).ConfigureAwait(false);
                            }

                            if (acquiresFailed < int.MaxValue)
                            {
                                acquiresFailed++;
                            }

                            continue;
                        }
                        catch (Exception e)
                        {
                            if (acquiresFailed == 0)
                            {
                                Log.ErrorException("quartzSchedulerThreadLoop: RuntimeException " + e.Message, e);
                            }
                            if (acquiresFailed < int.MaxValue)
                            {
                                acquiresFailed++;
                            }
                            continue;
                        }

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

                            while (timeUntilTrigger > TimeSpan.Zero)
                            {
                                if (await ReleaseIfScheduleChangedSignificantly(triggers, triggerTime).ConfigureAwait(false))
                                {
                                    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 (await ReleaseIfScheduleChangedSignificantly(triggers, triggerTime).ConfigureAwait(false))
                                {
                                    break;
                                }
                                now = SystemTime.UtcNow();
                                timeUntilTrigger = triggerTime - now;
                            }

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

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

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

                            if (goAhead)
                            {
                                try
                                {
                                    var res = await qsRsrcs.JobStore.TriggersFired(triggers, CancellationToken.None).ConfigureAwait(false);

                                    if (res != null)
                                    {
                                        bndles = res.ToList();
                                    }
                                }
                                catch (SchedulerException se)
                                {
                                    var msg = "An error occurred while firing triggers '" + triggers + "'";
                                    await qs.NotifySchedulerListenersError(msg, se, CancellationToken.None).ConfigureAwait(false);

                                    // QTZ-179 : a problem occurred interacting with the triggers from the db
                                    // we release them and loop again
                                    foreach (IOperableTrigger t in triggers)
                                    {
                                        await qsRsrcs.JobStore.ReleaseAcquiredTrigger(t, CancellationToken.None).ConfigureAwait(false);
                                    }
                                    continue;
                                }
                            }

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

                                IOperableTrigger trigger = triggers[i];
                                // TODO SQL exception?
                                if (exception != null && (exception is DbException || exception.InnerException is DbException))
                                {
                                    Log.ErrorException("DbException while firing trigger " + trigger, exception);
                                    await qsRsrcs.JobStore.ReleaseAcquiredTrigger(trigger, CancellationToken.None).ConfigureAwait(false);

                                    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)
                                {
                                    await qsRsrcs.JobStore.ReleaseAcquiredTrigger(trigger, CancellationToken.None).ConfigureAwait(false);

                                    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;
                                try
                                {
                                    shell = qsRsrcs.JobRunShellFactory.CreateJobRunShell(bndle);
                                    await shell.Initialize(qs, CancellationToken.None).ConfigureAwait(false);
                                }
                                catch (SchedulerException)
                                {
                                    await qsRsrcs.JobStore.TriggeredJobComplete(trigger, bndle.JobDetail, SchedulerInstruction.SetAllJobTriggersError, CancellationToken.None).ConfigureAwait(false);

                                    continue;
                                }

                                var threadPoolRunResult = qsRsrcs.ThreadPool.RunInThread(() => shell.Run(CancellationToken.None));
                                if (threadPoolRunResult == false)
                                {
                                    // 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() returned false!");
                                    await qsRsrcs.JobStore.TriggeredJobComplete(trigger, bndle.JobDetail, SchedulerInstruction.SetAllJobTriggersError, CancellationToken.None).ConfigureAwait(false);
                                }
                            }

                            continue; // while (!halted)
                        }
                    }
                    else // if(availThreadCount > 0)
                    {
                        continue;
                        // while (!halted)
                    }

                    DateTimeOffset utcNow            = SystemTime.UtcNow();
                    DateTimeOffset waitTime          = utcNow.Add(GetRandomizedIdleWaitTime());
                    TimeSpan       timeUntilContinue = waitTime - utcNow;
                    lock (sigLock)
                    {
                        if (!halted)
                        {
                            try
                            {
                                // QTZ-336 A job might have been completed in the mean time and we might have
                                // missed the scheduled changed signal by not waiting for the notify() yet
                                // Check that before waiting for too long in case this very job needs to be
                                // scheduled very soon
                                if (!IsScheduleChanged())
                                {
                                    Monitor.Wait(sigLock, timeUntilContinue);
                                }
                            }
                            catch (ThreadInterruptedException)
                            {
                            }
                        }
                    }
                }
                catch (Exception re)
                {
                    Log.ErrorException("Runtime error occurred in main trigger firing loop.", re);
                }
            } // while (!halted)
        }