Ticket #11108: mythtv-11108-1.patch

File mythtv-11108-1.patch, 14.9 KB (added by gigem, 11 years ago)

Refactor, simplify and slightly enhance scheduler timing logic

  • mythtv/programs/mythbackend/scheduler.cpp

    diff --git a/mythtv/programs/mythbackend/scheduler.cpp b/mythtv/programs/mythbackend/scheduler.cpp
    index eed332a..30efca7 100644
    a b void Scheduler::run(void) 
    18061806    RecIter   startIter       = reclist.begin();
    18071807    QDateTime idleSince       = QDateTime();
    18081808    int       maxSleep        = 60000; // maximum sleep time in milliseconds
    1809     int       schedRunTime    = 30; // max scheduler run time in seconds
     1809    int       schedRunTime    = 0; // max scheduler run time in seconds
    18101810    bool      statuschanged   = false;
     1811    nextHardTime = MythDate::current().addDays(1);
     1812    nextSoftTime = MythDate::current().addMSecs(maxSleep);
    18111813
    18121814    while (doRun)
    18131815    {
     1816        if (!doRun)
     1817            break;
     1818
    18141819        reclist_changed = false;
    18151820
     1821        nextSoftTime = min(nextSoftTime, nextHardTime);
     1822        nextSoftTime.addMSecs(-nextSoftTime.time().msec());
     1823
    18161824        QDateTime curtime = MythDate::current();
    1817         int secs_to_next = (startIter != reclist.end()) ?
    1818             curtime.secsTo((*startIter)->GetRecordingStartTime()) : 60*60;
     1825        int secs_to_next = max(curtime.secsTo(nextHardTime), 0);
     1826        int sched_sleep = max(curtime.msecsTo(nextSoftTime), (qint64)0);
     1827        LOG(VB_GENERAL, LOG_INFO,
     1828            QString("### s2n: %1 ss: %2)")
     1829            .arg(secs_to_next).arg(sched_sleep));
    18191830
    18201831        // If we're about to start a recording don't do any reschedules...
    18211832        // instead sleep for a bit
    1822         if (secs_to_next < (schedRunTime + 2))
     1833        if (secs_to_next < schedRunTime + 2)
    18231834        {
    1824             int msecs = CalcTimeToNextHandleRecordingEvent(
    1825                 curtime, startIter, reclist, prerollseconds, maxSleep);
    18261835            LOG(VB_SCHEDULE, LOG_INFO,
    18271836                QString("sleeping for %1 ms (s2n: %2 sr: %3)")
    1828                     .arg(msecs).arg(secs_to_next).arg(schedRunTime));
    1829             if (msecs < 100)
    1830                 (void) ::usleep(msecs * 1000);
     1837                .arg(sched_sleep).arg(secs_to_next).arg(schedRunTime));
     1838            if (sched_sleep < 100)
     1839                (void) ::usleep(sched_sleep);
    18311840            else
    1832                 reschedWait.wait(&schedLock, msecs);
     1841            {
     1842                if (reschedWait.wait(&schedLock, sched_sleep))
     1843                    continue;
     1844            }
     1845        }
     1846        else if (!HaveQueuedRequests())
     1847        {
     1848            LOG(VB_SCHEDULE, LOG_INFO,
     1849                QString("sleeping for %1 ms (interuptable)").arg(sched_sleep));
     1850            if (reschedWait.wait(&schedLock, sched_sleep))
     1851                continue;
    18331852        }
    18341853        else
    18351854        {
    1836             if (!HaveQueuedRequests())
    1837             {
    1838                 int sched_sleep = (secs_to_next - schedRunTime - 1) * 1000;
    1839                 sched_sleep = min(sched_sleep, maxSleep);
    1840                 if (secs_to_next < prerollseconds + (maxSleep/1000))
    1841                     sched_sleep = min(sched_sleep, 5000);
    1842                 LOG(VB_SCHEDULE, LOG_INFO,
    1843                     QString("sleeping for %1 ms (interuptable)")
    1844                         .arg(sched_sleep));
    1845                 reschedWait.wait(&schedLock, sched_sleep);
    1846                 if (!doRun)
    1847                     break;
    1848             }
     1855            // The master backend is a long lived program, so
     1856            // we reload some key settings on each reschedule.
     1857            prerollseconds  =
     1858                gCoreContext->GetNumSetting("RecordPreRoll", 0);
     1859            wakeThreshold =
     1860                gCoreContext->GetNumSetting("WakeUpThreshold", 300);
     1861            idleTimeoutSecs =
     1862                gCoreContext->GetNumSetting("idleTimeoutSecs", 0);
     1863            idleWaitForRecordingTime =
     1864                gCoreContext->GetNumSetting("idleWaitForRecordingTime", 15);
     1865            tuningTimeout =
     1866                gCoreContext->GetNumSetting("tuningTimeout", 180);
    18491867
    18501868            QTime t; t.start();
    1851             if (HaveQueuedRequests() && HandleReschedule())
     1869
     1870            if (HandleReschedule())
    18521871            {
    18531872                statuschanged = true;
    18541873                startIter = reclist.begin();
    1855 
    1856                 // The master backend is a long lived program, so
    1857                 // we reload some key settings on each reschedule.
    1858                 prerollseconds  =
    1859                     gCoreContext->GetNumSetting("RecordPreRoll", 0);
    1860                 wakeThreshold =
    1861                     gCoreContext->GetNumSetting("WakeUpThreshold", 300);
    1862                 idleTimeoutSecs =
    1863                     gCoreContext->GetNumSetting("idleTimeoutSecs", 0);
    1864                 idleWaitForRecordingTime =
    1865                     gCoreContext->GetNumSetting("idleWaitForRecordingTime", 15);
    1866                 tuningTimeout =
    1867                     gCoreContext->GetNumSetting("tuningTimeout", 180);
    18681874            }
    18691875
    1870             int e = t.elapsed();
    1871             if (e > 0)
    1872             {
    1873                 schedRunTime = (firstRun) ? 0 : schedRunTime;
    1874                 schedRunTime =
    1875                     max((int)(((e + 999) / 1000) * 1.5f), schedRunTime);
    1876             }
     1876            schedRunTime = max((int)(((t.elapsed() + 999) / 1000) * 1.5f),
     1877                               schedRunTime);
    18771878
    18781879            if (firstRun)
    18791880            {
     1881                firstRun = false;
    18801882                blockShutdown &= HandleRunSchedulerStartup(
    18811883                    prerollseconds, idleWaitForRecordingTime);
    1882                 firstRun = false;
    18831884
    18841885                // HandleRunSchedulerStartup releases the schedLock so the
    18851886                // reclist may have changed. If it has go to top of loop
    18861887                // and update secs_to_next...
    18871888                if (reclist_changed)
     1889                {
     1890                    nextHardTime = MythDate::current();
    18881891                    continue;
     1892                }
    18891893            }
    18901894
    18911895            // Unless a recording is about to start, check for slaves
    void Scheduler::run(void) 
    19021906            }
    19031907        }
    19041908
     1909        nextHardTime = MythDate::current().addDays(1);
     1910        nextSoftTime = MythDate::current().addMSecs(maxSleep);
     1911
    19051912        // Skip past recordings that are already history
    19061913        // (i.e. AddHistory() has been called setting oldrecstatus)
    19071914        for ( ; startIter != reclist.end(); ++startIter)
    void Scheduler::run(void) 
    19271934        // schedLock.  If anything changed, reclist iterators could be
    19281935        // invalidated so start over.
    19291936        if (reclist_changed)
     1937        {
     1938            // Re-run immediately as there might be other recordings
     1939            // that need to start now.
     1940            nextHardTime = MythDate::current();
    19301941            continue;
     1942        }
    19311943
    19321944        /// Wake any slave backends that need waking
    19331945        curtime = MythDate::current();
    void Scheduler::run(void) 
    19621974    RunEpilog();
    19631975}
    19641976
    1965 int Scheduler::CalcTimeToNextHandleRecordingEvent(
    1966     const QDateTime &curtime,
    1967     RecConstIter startIter, const RecList &reclist,
    1968     int prerollseconds, int max_sleep /*ms*/)
    1969 {
    1970     if (startIter == reclist.end())
    1971         return max_sleep;
    1972 
    1973     int msecs = max_sleep;
    1974     for (RecConstIter i = startIter; i != reclist.end() && (msecs > 0); ++i)
    1975     {
    1976         // Check on recordings that we've told to start, but have
    1977         // not yet started every second or so.
    1978         if ((*i)->GetRecordingStatus() == rsTuning)
    1979         {
    1980             msecs = min(msecs, 1000);
    1981             continue;
    1982         }
    1983 
    1984         // These recordings have already been handled..
    1985         if ((*i)->GetRecordingStatus() == (*i)->oldrecstatus)
    1986             continue;
    1987 
    1988         int secs_to_next = curtime.secsTo((*i)->GetRecordingStartTime());
    1989 
    1990         if ((*i)->GetRecordingStatus() == rsWillRecord &&
    1991             !recPendingList.contains((*i)->MakeUniqueSchedulerKey()))
    1992             secs_to_next -= 30;
    1993 
    1994         if (secs_to_next < 0)
    1995         {
    1996             msecs = 0;
    1997             break;
    1998         }
    1999 
    2000         // This is what normally breaks us out of the loop...
    2001         if (secs_to_next > max_sleep)
    2002         {
    2003             msecs = min(msecs, max_sleep);
    2004             break;
    2005         }
    2006 
    2007         if (secs_to_next > 31)
    2008         {
    2009             msecs = min(msecs, 30 * 1000);
    2010             continue;
    2011         }
    2012 
    2013         if ((secs_to_next-1) * 1000 > msecs)
    2014             continue;
    2015 
    2016         if (secs_to_next < 15)
    2017         {
    2018             QTime st = (*i)->GetRecordingStartTime().time();
    2019             int tmp = curtime.time().msecsTo(st);
    2020             tmp = (tmp < 0) ? tmp + 86400000 : tmp;
    2021             msecs = (tmp > 15*1000) ? 0 : min(msecs, tmp);
    2022         }
    2023         else
    2024         {
    2025             msecs = min(msecs, (secs_to_next-1) * 1000);
    2026         }
    2027     }
    2028 
    2029     return min(msecs, max_sleep);
    2030 }
    2031 
    20321977void Scheduler::ResetDuplicates(uint recordid, uint findid,
    20331978                                const QString &title, const QString &subtitle,
    20341979                                const QString &descrip,
    bool Scheduler::HandleReschedule(void) 
    21322077        if (request.size() < 1 || tokens.size() < 1)
    21332078        {
    21342079            LOG(VB_GENERAL, LOG_ERR, "Empty Reschedule request received");
    2135             return false;
     2080            continue;
    21362081        }
    21372082
    21382083        LOG(VB_GENERAL, LOG_INFO, QString("Reschedule requested for %1")
    bool Scheduler::HandleRecording( 
    24482393
    24492394    if (ri.GetRecordingStatus() != rsWillRecord)
    24502395    {
    2451         if (ri.GetRecordingStatus() != ri.oldrecstatus &&
    2452             ri.GetRecordingStartTime() <= MythDate::current())
     2396        if (ri.GetRecordingStatus() != ri.oldrecstatus)
    24532397        {
    2454             ri.AddHistory(false);
     2398            if (ri.GetRecordingStartTime() <= MythDate::current())
     2399                ri.AddHistory(false);
     2400            else
     2401                nextSoftTime = min(nextSoftTime, ri.GetRecordingStartTime());
    24552402        }
    24562403        return false;
    24572404    }
    bool Scheduler::HandleRecording( 
    24632410
    24642411    // This check needs to be shorter than the related one in
    24652412    // SchedLiveTV().
    2466     if (secsleft - prerollseconds < 60)
     2413    if (secsleft - prerollseconds > 60)
    24672414    {
    2468         if (!recPendingList.contains(schedid))
    2469         {
    2470             recPendingList[schedid] = false;
    2471             // If we haven't rescheduled in a while, do so now to
    2472             // accomodate LiveTV.
    2473             if (schedTime.secsTo(curtime) > 30)
    2474                 EnqueuePlace("PrepareToRecord");
    2475         }
     2415        nextHardTime = min(nextHardTime, ri.GetRecordingStartTime());
     2416        nextSoftTime = min(nextSoftTime, curtime
     2417                           .addSecs(secsleft - prerollseconds - 60));
     2418        return true;
     2419    }
     2420
     2421    if (!recPendingList.contains(schedid))
     2422    {
     2423        recPendingList[schedid] = false;
     2424        // If we haven't rescheduled in a while, do so now to
     2425        // accomodate LiveTV.
     2426        if (schedTime.secsTo(curtime) > 30)
     2427            EnqueuePlace("PrepareToRecord");
    24762428    }
    24772429
    24782430    if (secsleft - prerollseconds > 35)
    2479         return true;
     2431    {
     2432        nextHardTime = min(nextHardTime, ri.GetRecordingStartTime());
     2433        nextSoftTime = min(nextSoftTime, curtime
     2434                           .addSecs(secsleft - prerollseconds - 35));
     2435        return false;
     2436    }
    24802437
    24812438    QMap<int, EncoderLink*>::iterator tvit = m_tvList->find(ri.GetCardID());
    24822439    if (tvit == m_tvList->end())
    bool Scheduler::HandleRecording( 
    25152472    // changes.
    25162473    RecordingInfo tempri(ri);
    25172474
    2518     schedLock.unlock();
    2519     bool isBusyRecording = IsBusyRecording(&tempri);
    2520     schedLock.lock();
    2521     if (reclist_changed)
    2522         return reclist_changed;
    2523 
    2524     if (prerollseconds > 0 && !isBusyRecording)
     2475    if (prerollseconds > 0)
    25252476    {
    2526         // Will use pre-roll settings only if no other
    2527         // program is currently being recorded
    2528         secsleft -= prerollseconds;
     2477        schedLock.unlock();
     2478        bool isBusyRecording = IsBusyRecording(&tempri);
     2479        schedLock.lock();
     2480        if (reclist_changed)
     2481            return reclist_changed;
     2482
     2483        if (isBusyRecording)
     2484        {
     2485            // Don't wait any longer than 5 seconds before checking
     2486            // again.  The recorder might free up and let us still use
     2487            // some of the preroll.
     2488            nextSoftTime = min(nextSoftTime, curtime.addSecs(5));
     2489        }
     2490        else
     2491        {
     2492            // Will use pre-roll settings only if no other
     2493            // program is currently being recorded
     2494            secsleft -= prerollseconds;
     2495        }
    25292496    }
    25302497
    25312498#if 0
    bool Scheduler::HandleRecording( 
    25342501#endif
    25352502
    25362503    if (secsleft > 30)
     2504    {
     2505        nextHardTime = min(nextHardTime, ri.GetRecordingStartTime());
     2506        nextSoftTime = min(nextSoftTime, curtime.addSecs(secsleft - 30));
    25372507        return false;
     2508    }
    25382509
    25392510    if (nexttv->IsWaking())
    25402511    {
    bool Scheduler::HandleRecording( 
    25682539            EnqueuePlace("SlaveNotAwake");
    25692540        }
    25702541
     2542        nextHardTime = min(nextHardTime, ri.GetRecordingStartTime());
     2543        nextSoftTime = min(nextSoftTime, curtime.addSecs(1));
    25712544        return false;
    25722545    }
    25732546
    bool Scheduler::HandleRecording( 
    25982571    }
    25992572
    26002573    if (secsleft > 0)
     2574    {
     2575        nextHardTime = min(nextHardTime, ri.GetRecordingStartTime());
     2576        nextSoftTime = min(nextSoftTime, curtime.addSecs(secsleft));
    26012577        return false;
     2578    }
    26022579
    26032580    QDateTime recstartts = MythDate::current(true).addSecs(30);
    26042581    recstartts = QDateTime(
    bool Scheduler::HandleRecording( 
    26222599            recStatus = nexttv->StartRecording(&tempri);
    26232600            schedLock.lock();
    26242601
     2602            if (recStatus == rsTuning)
     2603                nextSoftTime = min(nextSoftTime, curtime.addSecs(1));
     2604
    26252605            // activate auto expirer
    26262606            if (m_expirer)
    26272607                m_expirer->Update(ri.GetCardID(), fsID, true);
    void Scheduler::HandleTuning( 
    27092689                            "%1 seconds, has been exceeded.")
    27102690                    .arg(tuningTimeout));
    27112691            }
     2692            else
     2693            {
     2694                nextSoftTime = min(nextSoftTime, curtime.addSecs(1));
     2695            }
    27122696        }
    27132697    }
    27142698
  • mythtv/programs/mythbackend/scheduler.h

    diff --git a/mythtv/programs/mythbackend/scheduler.h b/mythtv/programs/mythbackend/scheduler.h
    index fedab8f..fd38a1f 100644
    a b class Scheduler : public MThread, public MythScheduler 
    159159                         const RecList &reclist);
    160160    void FillDirectoryInfoCache(bool force = false);
    161161
    162     int CalcTimeToNextHandleRecordingEvent(
    163         const QDateTime &curtime,
    164         RecConstIter startIter, const RecList &reclist,
    165         int prerollseconds, int max_sleep /*ms*/);
    166162    void OldRecordedFixups(void);
    167163    void ResetDuplicates(uint recordid, uint findid, const QString &title,
    168164                         const QString &subtitle, const QString &descrip,
    class Scheduler : public MThread, public MythScheduler 
    217213    InputGroupMap igrp;
    218214
    219215    QDateTime schedTime;
     216    QDateTime nextHardTime;
     217    QDateTime nextSoftTime;
    220218    bool reclist_changed;
    221219
    222220    bool specsched;