summaryrefslogtreecommitdiff
path: root/epg.c
diff options
context:
space:
mode:
authorKlaus Schmidinger <vdr@tvdr.de>2015-09-01 11:14:27 +0200
committerKlaus Schmidinger <vdr@tvdr.de>2015-09-01 11:14:27 +0200
commit3cd5294d8a337ee5cd2ec894c9fbe04ad3a7690d (patch)
treeda57ce74189de9bfb27e1a747063c37cd62de501 /epg.c
parent8a7bc6a0bbf60cae8b6391a630880aad5cba3363 (diff)
downloadvdr-3cd5294d8a337ee5cd2ec894c9fbe04ad3a7690d.tar.gz
vdr-3cd5294d8a337ee5cd2ec894c9fbe04ad3a7690d.tar.bz2
Implemented strict locking of global lists
Diffstat (limited to 'epg.c')
-rw-r--r--epg.c268
1 files changed, 127 insertions, 141 deletions
diff --git a/epg.c b/epg.c
index 096b68cf..3dde2045 100644
--- a/epg.c
+++ b/epg.c
@@ -7,7 +7,7 @@
* Original version (as used in VDR before 1.3.0) written by
* Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
*
- * $Id: epg.c 3.3 2013/12/28 11:33:08 kls Exp $
+ * $Id: epg.c 4.1 2015/08/23 10:39:59 kls Exp $
*/
#include "epg.h"
@@ -15,7 +15,6 @@
#include <limits.h>
#include <time.h>
#include "libsi/si.h"
-#include "timers.h"
#define RUNNINGSTATUSTIMEOUT 30 // seconds before the running status is considered unknown
#define EPGDATAWRITEDELTA 600 // seconds between writing the epg.data file
@@ -111,9 +110,12 @@ tComponent *cComponents::GetComponent(int Index, uchar Stream, uchar Type)
// --- cEvent ----------------------------------------------------------------
+cMutex cEvent::numTimersMutex;
+
cEvent::cEvent(tEventID EventID)
{
schedule = NULL;
+ numTimers = 0;
eventID = EventID;
tableID = 0xFF; // actual table ids are 0x4E..0x60
version = 0xFF; // actual version numbers are 0..31
@@ -170,9 +172,9 @@ void cEvent::SetVersion(uchar Version)
version = Version;
}
-void cEvent::SetRunningStatus(int RunningStatus, cChannel *Channel)
+void cEvent::SetRunningStatus(int RunningStatus, const cChannel *Channel)
{
- if (Channel && runningStatus != RunningStatus && (RunningStatus > SI::RunningStatusNotRunning || runningStatus > SI::RunningStatusUndefined) && Channel->HasTimer())
+ if (Channel && runningStatus != RunningStatus && (RunningStatus > SI::RunningStatusNotRunning || runningStatus > SI::RunningStatusUndefined) && schedule && schedule->HasTimer())
isyslog("channel %d (%s) event %s status %d", Channel->Number(), Channel->Name(), *ToDescr(), RunningStatus);
runningStatus = RunningStatus;
}
@@ -243,13 +245,22 @@ cString cEvent::ToDescr(void) const
return cString::sprintf("%s %s-%s %s'%s'", *GetDateString(), *GetTimeString(), *GetEndTimeString(), vpsbuf, Title());
}
-bool cEvent::HasTimer(void) const
+void cEvent::IncNumTimers(void) const
{
- for (cTimer *t = Timers.First(); t; t = Timers.Next(t)) {
- if (t->Event() == this)
- return true;
- }
- return false;
+ numTimersMutex.Lock();
+ numTimers++;
+ if (schedule)
+ schedule->IncNumTimers();
+ numTimersMutex.Unlock();
+}
+
+void cEvent::DecNumTimers(void) const
+{
+ numTimersMutex.Lock();
+ numTimers--;
+ if (schedule)
+ schedule->DecNumTimers();
+ numTimersMutex.Unlock();
}
bool cEvent::IsRunning(bool OrAboutToStart) const
@@ -605,9 +616,9 @@ void ReportEpgBugFixStats(bool Force)
bool PrintedStats = false;
char *q = buffer;
*buffer = 0;
+ LOCK_CHANNELS_READ;
for (int c = 0; c < p->n; c++) {
- cChannel *channel = Channels.GetByChannelID(p->channelIDs[c], true);
- if (channel) {
+ if (const cChannel *Channel = Channels->GetByChannelID(p->channelIDs[c], true)) {
if (!GotHits) {
dsyslog("=====================");
dsyslog("EPG bugfix statistics");
@@ -623,7 +634,7 @@ void ReportEpgBugFixStats(bool Force)
q += snprintf(q, sizeof(buffer) - (q - buffer), "%-3d %-4d", i, p->hits);
PrintedStats = true;
}
- q += snprintf(q, sizeof(buffer) - (q - buffer), "%s%s", delim, channel->Name());
+ q += snprintf(q, sizeof(buffer) - (q - buffer), "%s%s", delim, Channel->Name());
delim = ", ";
if (q - buffer > 80) {
q += snprintf(q, sizeof(buffer) - (q - buffer), "%s...", delim);
@@ -878,14 +889,32 @@ Final:
// --- cSchedule -------------------------------------------------------------
+cMutex cSchedule::numTimersMutex;
+
cSchedule::cSchedule(tChannelID ChannelID)
{
channelID = ChannelID;
+ events.SetUseGarbageCollector();
+ numTimers = 0;
hasRunning = false;
modified = 0;
presentSeen = 0;
}
+void cSchedule::IncNumTimers(void) const
+{
+ numTimersMutex.Lock();
+ numTimers++;
+ numTimersMutex.Unlock();
+}
+
+void cSchedule::DecNumTimers(void) const
+{
+ numTimersMutex.Lock();
+ numTimers--;
+ numTimersMutex.Unlock();
+}
+
cEvent *cSchedule::AddEvent(cEvent *Event)
{
events.Add(Event);
@@ -897,8 +926,6 @@ cEvent *cSchedule::AddEvent(cEvent *Event)
void cSchedule::DelEvent(cEvent *Event)
{
if (Event->schedule == this) {
- if (hasRunning && Event->IsRunning())
- ClrRunningStatus();
UnhashEvent(Event);
events.Del(Event);
}
@@ -922,7 +949,7 @@ const cEvent *cSchedule::GetPresentEvent(void) const
{
const cEvent *pe = NULL;
time_t now = time(NULL);
- for (cEvent *p = events.First(); p; p = events.Next(p)) {
+ for (const cEvent *p = events.First(); p; p = events.Next(p)) {
if (p->StartTime() <= now)
pe = p;
else if (p->StartTime() > now + 3600)
@@ -962,7 +989,7 @@ const cEvent *cSchedule::GetEventAround(time_t Time) const
{
const cEvent *pe = NULL;
time_t delta = INT_MAX;
- for (cEvent *p = events.First(); p; p = events.Next(p)) {
+ for (const cEvent *p = events.First(); p; p = events.Next(p)) {
time_t dt = Time - p->StartTime();
if (dt >= 0 && dt < delta && p->EndTime() >= Time) {
delta = dt;
@@ -972,7 +999,7 @@ const cEvent *cSchedule::GetEventAround(time_t Time) const
return pe;
}
-void cSchedule::SetRunningStatus(cEvent *Event, int RunningStatus, cChannel *Channel)
+void cSchedule::SetRunningStatus(cEvent *Event, int RunningStatus, const cChannel *Channel)
{
hasRunning = false;
for (cEvent *p = events.First(); p; p = events.Next(p)) {
@@ -987,6 +1014,7 @@ void cSchedule::SetRunningStatus(cEvent *Event, int RunningStatus, cChannel *Cha
if (p->RunningStatus() >= SI::RunningStatusPausing)
hasRunning = true;
}
+ SetPresentSeen();
}
void cSchedule::ClrRunningStatus(cChannel *Channel)
@@ -1019,32 +1047,29 @@ void cSchedule::Sort(void)
p->SetRunningStatus(SI::RunningStatusNotRunning);
}
}
+ SetModified();
}
void cSchedule::DropOutdated(time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version)
{
if (SegmentStart > 0 && SegmentEnd > 0) {
- for (cEvent *p = events.First(); p; p = events.Next(p)) {
- if (p->EndTime() > SegmentStart) {
- if (p->StartTime() < SegmentEnd) {
- // The event overlaps with the given time segment.
- if (p->TableID() > TableID || p->TableID() == TableID && p->Version() != Version) {
- // The segment overwrites all events from tables with higher ids, and
- // within the same table id all events must have the same version.
- // We can't delete the event right here because a timer might have
- // a pointer to it, so let's set its id and start time to 0 to have it
- // "phased out":
- if (hasRunning && p->IsRunning())
- ClrRunningStatus();
- UnhashEvent(p);
- p->eventID = 0;
- p->startTime = 0;
- }
- }
- else
- break;
- }
- }
+ cEvent *p = events.First();
+ while (p) {
+ cEvent *n = events.Next(p);
+ if (p->EndTime() > SegmentStart) {
+ if (p->StartTime() < SegmentEnd) {
+ // The event overlaps with the given time segment.
+ if (p->TableID() > TableID || p->TableID() == TableID && p->Version() != Version) {
+ // The segment overwrites all events from tables with higher ids, and
+ // within the same table id all events must have the same version.
+ DelEvent(p);
+ }
+ }
+ else
+ break;
+ }
+ p = n;
+ }
}
}
@@ -1066,9 +1091,9 @@ void cSchedule::Cleanup(time_t Time)
void cSchedule::Dump(FILE *f, const char *Prefix, eDumpMode DumpMode, time_t AtTime) const
{
- cChannel *channel = Channels.GetByChannelID(channelID, true);
- if (channel) {
- fprintf(f, "%sC %s %s\n", Prefix, *channel->GetChannelID().ToString(), channel->Name());
+ LOCK_CHANNELS_READ;
+ if (const cChannel *Channel = Channels->GetByChannelID(channelID, true)) {
+ fprintf(f, "%sC %s %s\n", Prefix, *Channel->GetChannelID().ToString(), Channel->Name());
const cEvent *p;
switch (DumpMode) {
case dmAll: {
@@ -1111,12 +1136,10 @@ bool cSchedule::Read(FILE *f, cSchedules *Schedules)
if (*s) {
tChannelID channelID = tChannelID::FromString(s);
if (channelID.Valid()) {
- cSchedule *p = Schedules->AddSchedule(channelID);
- if (p) {
+ if (cSchedule *p = Schedules->AddSchedule(channelID)) {
if (!cEvent::Read(f, p))
return false;
p->Sort();
- Schedules->SetModified(p);
}
}
else {
@@ -1164,12 +1187,12 @@ void cEpgDataWriter::Perform(void)
{
cMutexLock MutexLock(&mutex); // to make sure fore- and background calls don't cause parellel dumps!
{
- cSchedulesLock SchedulesLock(true, 1000);
- cSchedules *s = (cSchedules *)cSchedules::Schedules(SchedulesLock);
- if (s) {
+ cStateKey StateKey;
+ if (cSchedules *Schedules = cSchedules::GetSchedulesWrite(StateKey, 1000)) {
time_t now = time(NULL);
- for (cSchedule *p = s->First(); p; p = s->Next(p))
+ for (cSchedule *p = Schedules->First(); p; p = Schedules->Next(p))
p->Cleanup(now);
+ StateKey.Remove();
}
}
if (dump)
@@ -1178,29 +1201,25 @@ void cEpgDataWriter::Perform(void)
static cEpgDataWriter EpgDataWriter;
-// --- cSchedulesLock --------------------------------------------------------
+// --- cSchedules ------------------------------------------------------------
-cSchedulesLock::cSchedulesLock(bool WriteLock, int TimeoutMs)
+cSchedules cSchedules::schedules;
+char *cSchedules::epgDataFileName = NULL;
+time_t cSchedules::lastDump = time(NULL);
+
+cSchedules::cSchedules(void)
+:cList<cSchedule>("Schedules")
{
- locked = cSchedules::schedules.rwlock.Lock(WriteLock, TimeoutMs);
}
-cSchedulesLock::~cSchedulesLock()
+const cSchedules *cSchedules::GetSchedulesRead(cStateKey &StateKey, int TimeoutMs)
{
- if (locked)
- cSchedules::schedules.rwlock.Unlock();
+ return schedules.Lock(StateKey, false, TimeoutMs) ? &schedules : NULL;
}
-// --- cSchedules ------------------------------------------------------------
-
-cSchedules cSchedules::schedules;
-char *cSchedules::epgDataFileName = NULL;
-time_t cSchedules::lastDump = time(NULL);
-time_t cSchedules::modified = 0;
-
-const cSchedules *cSchedules::Schedules(cSchedulesLock &SchedulesLock)
+cSchedules *cSchedules::GetSchedulesWrite(cStateKey &StateKey, int TimeoutMs)
{
- return SchedulesLock.Locked() ? &schedules : NULL;
+ return schedules.Lock(StateKey, true, TimeoutMs) ? &schedules : NULL;
}
void cSchedules::SetEpgDataFileName(const char *FileName)
@@ -1210,12 +1229,6 @@ void cSchedules::SetEpgDataFileName(const char *FileName)
EpgDataWriter.SetDump(epgDataFileName != NULL);
}
-void cSchedules::SetModified(cSchedule *Schedule)
-{
- Schedule->SetModified();
- modified = time(NULL);
-}
-
void cSchedules::Cleanup(bool Force)
{
if (Force)
@@ -1232,83 +1245,59 @@ void cSchedules::Cleanup(bool Force)
void cSchedules::ResetVersions(void)
{
- cSchedulesLock SchedulesLock(true);
- cSchedules *s = (cSchedules *)Schedules(SchedulesLock);
- if (s) {
- for (cSchedule *Schedule = s->First(); Schedule; Schedule = s->Next(Schedule))
- Schedule->ResetVersions();
- }
-}
-
-bool cSchedules::ClearAll(void)
-{
- cSchedulesLock SchedulesLock(true, 1000);
- cSchedules *s = (cSchedules *)Schedules(SchedulesLock);
- if (s) {
- for (cTimer *Timer = Timers.First(); Timer; Timer = Timers.Next(Timer))
- Timer->SetEvent(NULL);
- for (cSchedule *Schedule = s->First(); Schedule; Schedule = s->Next(Schedule))
- Schedule->Cleanup(INT_MAX);
- return true;
- }
- return false;
+ LOCK_SCHEDULES_WRITE;
+ for (cSchedule *Schedule = Schedules->First(); Schedule; Schedule = Schedules->Next(Schedule))
+ Schedule->ResetVersions();
}
bool cSchedules::Dump(FILE *f, const char *Prefix, eDumpMode DumpMode, time_t AtTime)
{
- cSchedulesLock SchedulesLock;
- cSchedules *s = (cSchedules *)Schedules(SchedulesLock);
- if (s) {
- cSafeFile *sf = NULL;
- if (!f) {
- sf = new cSafeFile(epgDataFileName);
- if (sf->Open())
- f = *sf;
- else {
- LOG_ERROR;
- delete sf;
- return false;
- }
- }
- for (cSchedule *p = s->First(); p; p = s->Next(p))
- p->Dump(f, Prefix, DumpMode, AtTime);
- if (sf) {
- sf->Close();
+ cSafeFile *sf = NULL;
+ if (!f) {
+ sf = new cSafeFile(epgDataFileName);
+ if (sf->Open())
+ f = *sf;
+ else {
+ LOG_ERROR;
delete sf;
+ return false;
}
- return true;
}
- return false;
+ LOCK_SCHEDULES_READ;
+ for (const cSchedule *p = Schedules->First(); p; p = Schedules->Next(p))
+ p->Dump(f, Prefix, DumpMode, AtTime);
+ if (sf) {
+ sf->Close();
+ delete sf;
+ }
+ return true;
}
bool cSchedules::Read(FILE *f)
{
- cSchedulesLock SchedulesLock(true, 1000);
- cSchedules *s = (cSchedules *)Schedules(SchedulesLock);
- if (s) {
- bool OwnFile = f == NULL;
- if (OwnFile) {
- if (epgDataFileName && access(epgDataFileName, R_OK) == 0) {
- dsyslog("reading EPG data from %s", epgDataFileName);
- if ((f = fopen(epgDataFileName, "r")) == NULL) {
- LOG_ERROR;
- return false;
- }
- }
- else
+ bool OwnFile = f == NULL;
+ if (OwnFile) {
+ if (epgDataFileName && access(epgDataFileName, R_OK) == 0) {
+ dsyslog("reading EPG data from %s", epgDataFileName);
+ if ((f = fopen(epgDataFileName, "r")) == NULL) {
+ LOG_ERROR;
return false;
+ }
}
- bool result = cSchedule::Read(f, s);
- if (OwnFile)
- fclose(f);
- if (result) {
- // Initialize the channels' schedule pointers, so that the first WhatsOn menu will come up faster:
- for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel))
- s->GetSchedule(Channel);
- }
- return result;
+ else
+ return false;
}
- return false;
+ LOCK_CHANNELS_WRITE;
+ LOCK_SCHEDULES_WRITE;
+ bool result = cSchedule::Read(f, Schedules);
+ if (OwnFile)
+ fclose(f);
+ if (result) {
+ // Initialize the channels' schedule pointers, so that the first WhatsOn menu will come up faster:
+ for (cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel))
+ Schedules->GetSchedule(Channel);
+ }
+ return result;
}
cSchedule *cSchedules::AddSchedule(tChannelID ChannelID)
@@ -1318,9 +1307,6 @@ cSchedule *cSchedules::AddSchedule(tChannelID ChannelID)
if (!p) {
p = new cSchedule(ChannelID);
Add(p);
- cChannel *channel = Channels.GetByChannelID(ChannelID);
- if (channel)
- channel->schedule = p;
}
return p;
}
@@ -1328,7 +1314,7 @@ cSchedule *cSchedules::AddSchedule(tChannelID ChannelID)
const cSchedule *cSchedules::GetSchedule(tChannelID ChannelID) const
{
ChannelID.ClrRid();
- for (cSchedule *p = First(); p; p = Next(p)) {
+ for (const cSchedule *p = First(); p; p = Next(p)) {
if (p->ChannelID() == ChannelID)
return p;
}
@@ -1541,18 +1527,18 @@ void cEpgHandlers::DropOutdated(cSchedule *Schedule, time_t SegmentStart, time_t
Schedule->DropOutdated(SegmentStart, SegmentEnd, TableID, Version);
}
-void cEpgHandlers::BeginSegmentTransfer(const cChannel *Channel, bool OnlyRunningStatus)
+void cEpgHandlers::BeginSegmentTransfer(const cChannel *Channel)
{
for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
- if (eh->BeginSegmentTransfer(Channel, OnlyRunningStatus))
+ if (eh->BeginSegmentTransfer(Channel, false))
return;
}
}
-void cEpgHandlers::EndSegmentTransfer(bool Modified, bool OnlyRunningStatus)
+void cEpgHandlers::EndSegmentTransfer(bool Modified)
{
for (cEpgHandler *eh = First(); eh; eh = Next(eh)) {
- if (eh->EndSegmentTransfer(Modified, OnlyRunningStatus))
+ if (eh->EndSegmentTransfer(Modified, false))
return;
}
}