Permalink
Browse files

Divide inputs into sets for detecting conflicts during scheduling.

Before input group and multirec support were added, the scheduler
split recording candidates into separate, conflict lists by cardid.
That avoided wasting time checking for conflicts that could never
occur.  It didn't alter the O(n^2) nature of conflict detection, but
it helped to keep n low.  After input group and multirec support were
added, all recording candidates were effectively placed on a single
list because recordings on different cards could now potentially
conflict.

This change restores the practice of putting recording candidates that
can't conflict on separate lists.  Instead of using cardids, however,
the scheduler uses cardids and input groups to split inputs into
mutually exclusive sets where any recording in one set can never
conflict with any recording in another set.
  • Loading branch information...
gigem committed Jun 16, 2013
1 parent ba5cc8c commit 7e250bc1b1385bbaba45c71ce7db9e1173180707
Showing with 114 additions and 3 deletions.
  1. +110 −2 mythtv/programs/mythbackend/scheduler.cpp
  2. +4 −1 mythtv/programs/mythbackend/scheduler.h
@@ -92,6 +92,8 @@ Scheduler::Scheduler(bool runthread, QMap<int, EncoderLink *> *tvList,
return;
}
CreateConflictLists();
fsInfoCacheFillTime = MythDate::current().addSecs(-1000);
if (doRun)
@@ -131,6 +133,12 @@ Scheduler::~Scheduler()
worklist.pop_back();
}
while (!conflictlists.empty())
{
delete conflictlists.back();
conflictlists.pop_back();
}
locker.unlock();
wait();
}
@@ -961,6 +969,8 @@ void Scheduler::PruneOverlaps(void)
void Scheduler::BuildListMaps(void)
{
QMap<uint, uint> badinputs;
RecIter i = worklist.begin();
for ( ; i != worklist.end(); ++i)
{
@@ -970,16 +980,31 @@ void Scheduler::BuildListMaps(void)
p->GetRecordingStatus() == rsWillRecord ||
p->GetRecordingStatus() == rsUnknown)
{
conflictlist.push_back(p);
RecList *conflictlist = conflictlistmap[p->GetInputID()];
if (!conflictlist)
{
++badinputs[p->GetInputID()];
continue;
}
conflictlist->push_back(p);
titlelistmap[p->GetTitle().toLower()].push_back(p);
recordidlistmap[p->GetRecordingRuleID()].push_back(p);
}
}
QMap<uint, uint>::iterator it;
for (it = badinputs.begin(); it != badinputs.end(); ++it)
{
LOG(VB_GENERAL, LOG_WARNING, LOC_WARN +
QString("Ignored %1 entries for invalid input %2")
.arg(badinputs[it.value()]).arg(it.key()));
}
}
void Scheduler::ClearListMaps(void)
{
conflictlist.clear();
for (uint i = 0; i < conflictlists.size(); ++i)
conflictlists[i]->clear();
titlelistmap.clear();
recordidlistmap.clear();
cache_is_same_program.clear();
@@ -1087,6 +1112,7 @@ const RecordingInfo *Scheduler::FindConflict(
const RecordingInfo *p,
int openend) const
{
RecList &conflictlist = *conflictlistmap[p->GetInputID()];
RecConstIter k = conflictlist.begin();
if (FindNextConflict(conflictlist, p, k, openend))
return *k;
@@ -1334,6 +1360,7 @@ void Scheduler::MoveHigherRecords(bool livetv)
p->SetRecordingStatus(rsWillRecord);
MarkOtherShowings(p);
RecList &conflictlist = *conflictlistmap[p->GetInputID()];
RecConstIter k = conflictlist.begin();
for ( ; FindNextConflict(conflictlist, p, k); ++k)
{
@@ -1366,6 +1393,7 @@ void Scheduler::MoveHigherRecords(bool livetv)
if (!livetv)
MarkOtherShowings(p);
RecList &conflictlist = *conflictlistmap[p->GetInputID()];
RecConstIter k = conflictlist.begin();
for ( ; FindNextConflict(conflictlist, p, k); ++k)
{
@@ -5113,4 +5141,84 @@ bool Scheduler::WasStartedAutomatically()
return autoStart;
}
void Scheduler::CreateConflictLists(void)
{
// For each input, build a set of inputs that it can conflict
// with, including itself.
QStringList queryStrs;
queryStrs <<
QString("SELECT ci1.cardinputid, ci2.cardinputid "
"FROM cardinput ci1, cardinput ci2 "
"WHERE ci1.cardid = ci2.cardid AND "
" ci1.cardinputid <= ci2.cardinputid "
"ORDER BY ci1.cardinputid, ci2.cardinputid");
queryStrs <<
QString("SELECT DISTINCT ci1.cardinputid, ci2.cardinputid "
"FROM cardinput ci1, cardinput ci2, "
" inputgroup ig1, inputgroup ig2 "
"WHERE ci1.cardinputid = ig1.cardinputid AND "
" ci2.cardinputid = ig2.cardinputid AND"
" ig1.inputgroupid = ig2.inputgroupid AND "
" ci1.cardinputid < ci2.cardinputid "
"ORDER BY ci1.cardinputid, ci2.cardinputid");
MSqlQuery query(MSqlQuery::InitCon());
QMap<uint, QSet<uint> > inputSets;
// For each input, create a set containing all of the inputs
// (including itself) that are grouped with it. Inputs can either
// be implicitly group by belonging to the same card or explicitly
// grouped by belonging to the same input group.
for (int i = 0; i < queryStrs.size(); ++i)
{
query.prepare(queryStrs[i]);
if (!query.exec())
{
MythDB::DBError(QString("BuildConflictLists%1").arg(i), query);
return;
}
while (query.next())
{
uint id0 = query.value(0).toUInt();
uint id1 = query.value(1).toUInt();
inputSets[id0].insert(id1);
inputSets[id1].insert(id0);
}
}
QMap<uint, QSet<uint> >::iterator mit;
for (mit = inputSets.begin(); mit != inputSets.end(); ++mit)
{
uint inputid = mit.key();
if (conflictlistmap.contains(inputid))
continue;
// Find the union of all inputs grouped with those already in
// the set. Keep doing so until no new inputs get added.
// This might not be the most efficient way, but it's simple
// and more than fast enough for our needs.
QSet<uint> fullset = mit.value();
QSet<uint> checkset;
QSet<uint>::const_iterator sit;
while (checkset != fullset)
{
checkset = fullset;
for (sit = checkset.begin(); sit != checkset.end(); ++sit)
fullset += inputSets[*sit];
}
// Create a new conflict list for the resulting set of inputs
// and point each inputs list at it.
RecList *conflictlist = new RecList();
conflictlists.push_back(conflictlist);
for (sit = checkset.begin(); sit != checkset.end(); ++sit)
{
LOG(VB_SCHEDULE, LOG_INFO,
QString("### Assigning input %1 to conflict set %2")
.arg(*sit).arg(conflictlists.size()));
conflictlistmap[*sit] = conflictlists.back();
}
}
}
/* vim: set expandtab tabstop=4 shiftwidth=4: */
@@ -194,14 +194,17 @@ class Scheduler : public MThread, public MythScheduler
void ClearRequestQueue(void)
{ reschedQueue.clear(); };
void CreateConflictLists(void);
MythDeque<QStringList> reschedQueue;
mutable QMutex schedLock;
QMutex recordmatchLock;
QWaitCondition reschedWait;
RecList reclist;
RecList worklist;
RecList retrylist;
RecList conflictlist;
vector<RecList *> conflictlists;
QMap<uint, RecList *> conflictlistmap;
QMap<uint, RecList> recordidlistmap;
QMap<QString, RecList> titlelistmap;
InputGroupMap igrp;

0 comments on commit 7e250bc

Please sign in to comment.