Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
epg: more robust 32-bit object ID handling
Changes:
  - store the last id number to the epgdb file
    (the purpose is to reuse the id numbers as last as possible)
  - use RB tree to track id numbers (faster lookups)
  - skip invalid zero IDs (++_epg_object_idx)
  - skip used IDs (rare, but possible)

The goal is to handle correctly the 32-bit ID wrapping (++_epg_object_idx).
  • Loading branch information
perexg committed Oct 5, 2014
1 parent 54de3db commit 1fbbbb8
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 21 deletions.
57 changes: 48 additions & 9 deletions src/epg.c
Expand Up @@ -39,24 +39,39 @@
#define EPG_HASH_WIDTH 1024
#define EPG_HASH_MASK (EPG_HASH_WIDTH - 1)

/* Objects tree */
epg_object_tree_t epg_objects[EPG_HASH_WIDTH];

/* URI lists */
epg_object_tree_t epg_brands;
epg_object_tree_t epg_seasons;
epg_object_tree_t epg_episodes;
epg_object_tree_t epg_serieslinks;

/* Other special case lists */
epg_object_list_t epg_objects[EPG_HASH_WIDTH];
epg_object_list_t epg_object_unref;
epg_object_list_t epg_object_updated;

/* Global counter */
static uint32_t _epg_object_idx = 0;

/*
*
*/
static inline epg_object_tree_t *epg_id_tree( epg_object_t *eo )
{
return &epg_objects[eo->id & EPG_HASH_MASK];
}

/* **************************************************************************
* Comparators / Ordering
* *************************************************************************/

static int _id_cmp ( const void *a, const void *b )
{
return ((epg_object_t*)a)->id - ((epg_object_t*)b)->id;
}

static int _uri_cmp ( const void *a, const void *b )
{
return strcmp(((epg_object_t*)a)->uri, ((epg_object_t*)b)->uri);
Expand Down Expand Up @@ -132,7 +147,7 @@ static void _epg_object_destroy
if (eo->uri) free(eo->uri);
if (tree) RB_REMOVE(tree, eo, uri_link);
if (eo->_updated) LIST_REMOVE(eo, up_link);
LIST_REMOVE(eo, id_link);
RB_REMOVE(epg_id_tree(eo), eo, id_link);
}

static void _epg_object_getref ( void *o )
Expand Down Expand Up @@ -169,15 +184,25 @@ static void _epg_object_set_updated ( void *o )
static void _epg_object_create ( void *o )
{
epg_object_t *eo = o;
uint32_t id = eo->id;
if (!id) eo->id = ++_epg_object_idx;
if (!eo->id) eo->id = ++_epg_object_idx;
else if (eo->id > _epg_object_idx) _epg_object_idx = eo->id;
if (!eo->getref) eo->getref = _epg_object_getref;
if (!eo->putref) eo->putref = _epg_object_putref;
tvhtrace("epg", "eo [%p, %u, %d, %s] created",
eo, eo->id, eo->type, eo->uri);
_epg_object_set_updated(eo);
LIST_INSERT_HEAD(&epg_object_unref, eo, un_link);
LIST_INSERT_HEAD(&epg_objects[eo->id & EPG_HASH_MASK], eo, id_link);
while (1) {
if (!RB_INSERT_SORTED(epg_id_tree(eo), eo, id_link, _id_cmp))
break;
if (id) {
tvherror("epg", "fatal error, duplicate EPG ID");
abort();
}
eo->id = ++_epg_object_idx;
if (!eo->id) eo->id = ++_epg_object_idx;
}
}

static epg_object_t *_epg_object_find_by_uri
Expand Down Expand Up @@ -211,11 +236,11 @@ static epg_object_t *_epg_object_find_by_uri

epg_object_t *epg_object_find_by_id ( uint32_t id, epg_object_type_t type )
{
epg_object_t *eo;
LIST_FOREACH(eo, &epg_objects[id & EPG_HASH_MASK], id_link) {
if (eo->id == id)
return ((type == EPG_UNDEF) || (eo->type == type)) ? eo : NULL;
}
epg_object_t *eo, temp;
temp.id = id;
eo = RB_FIND(epg_id_tree(&temp), &temp, id_link, _id_cmp);
if (eo && eo->type == type)
return eo;
return NULL;
}

Expand Down Expand Up @@ -2603,6 +2628,20 @@ void epg_query_free(epg_query_t *eq)
* Miscellaneous
* *************************************************************************/

htsmsg_t *epg_config_serialize( void )
{
htsmsg_t *m = htsmsg_create_map();
htsmsg_add_u32(m, "last_id", _epg_object_idx);
return m;
}

int epg_config_deserialize( htsmsg_t *m )
{
if (htsmsg_get_u32(m, "last_id", &_epg_object_idx))
return 0;
return 1; /* ok */
}

void epg_skel_done(void)
{
epg_object_t **skel;
Expand Down
8 changes: 7 additions & 1 deletion src/epg.h
Expand Up @@ -104,7 +104,7 @@ typedef enum epg_object_type
struct epg_object
{
RB_ENTRY(epg_object) uri_link; ///< Global URI link
LIST_ENTRY(epg_object) id_link; ///< Global (ID) link
RB_ENTRY(epg_object) id_link; ///< Global (ID) link
LIST_ENTRY(epg_object) un_link; ///< Global unref'd link
LIST_ENTRY(epg_object) up_link; ///< Global updated link

Expand Down Expand Up @@ -527,6 +527,12 @@ epg_broadcast_t *epg_broadcast_deserialize
/* Unlink */
void epg_channel_unlink ( struct channel *ch );

/* ************************************************************************
* Global config
* ***********************************************************************/
htsmsg_t *epg_config_serialize ( void );
int epg_config_deserialize ( htsmsg_t *m );

/* ************************************************************************
* Querying
* ***********************************************************************/
Expand Down
39 changes: 28 additions & 11 deletions src/epgdb.c
Expand Up @@ -131,6 +131,10 @@ _epgdb_v2_process( char **sect, htsmsg_t *m, epggrab_stats_t *stats )
} else if ( !strcmp(*sect, "broadcasts") ) {
if (epg_broadcast_deserialize(m, 1, &save)) stats->broadcasts.total++;

/* Global config */
} else if ( !strcmp(*sect, "config") ) {
if (epg_config_deserialize(m)) stats->config.total++;

/* Unknown */
} else {
tvhlog(LOG_DEBUG, "epgdb", "malformed database section [%s]", *sect);
Expand Down Expand Up @@ -220,8 +224,17 @@ void epg_init ( void )

free(sect);

if (!stats.config.total) {
htsmsg_t *m = htsmsg_create_map();
/* it's not correct, but at least something */
htsmsg_add_u32(m, "last_id", 64 * 1024 * 1024);
if (!epg_config_deserialize(m))
assert(0);
}

/* Stats */
tvhlog(LOG_INFO, "epgdb", "loaded v%d", ver);
tvhlog(LOG_INFO, "epgdb", " config %d", stats.config.total);
tvhlog(LOG_INFO, "epgdb", " channels %d", stats.channels.total);
tvhlog(LOG_INFO, "epgdb", " brands %d", stats.brands.total);
tvhlog(LOG_INFO, "epgdb", " seasons %d", stats.seasons.total);
Expand Down Expand Up @@ -300,31 +313,32 @@ void epg_save ( void )
return;

memset(&stats, 0, sizeof(stats));
if ( _epg_write_sect(fd, "brands") ) return;
if ( _epg_write_sect(fd, "config") ) goto fin;
if (_epg_write(fd, epg_config_serialize())) goto fin;
if ( _epg_write_sect(fd, "brands") ) goto fin;
RB_FOREACH(eo, &epg_brands, uri_link) {
if (_epg_write(fd, epg_brand_serialize((epg_brand_t*)eo))) return;
if (_epg_write(fd, epg_brand_serialize((epg_brand_t*)eo))) goto fin;
stats.brands.total++;
}
if ( _epg_write_sect(fd, "seasons") ) return;
if ( _epg_write_sect(fd, "seasons") ) goto fin;
RB_FOREACH(eo, &epg_seasons, uri_link) {
if (_epg_write(fd, epg_season_serialize((epg_season_t*)eo))) return;
if (_epg_write(fd, epg_season_serialize((epg_season_t*)eo))) goto fin;
stats.seasons.total++;
}
if ( _epg_write_sect(fd, "episodes") ) return;
if ( _epg_write_sect(fd, "episodes") ) goto fin;
RB_FOREACH(eo, &epg_episodes, uri_link) {
if (_epg_write(fd, epg_episode_serialize((epg_episode_t*)eo))) return;
if (_epg_write(fd, epg_episode_serialize((epg_episode_t*)eo))) goto fin;
stats.episodes.total++;
}
if ( _epg_write_sect(fd, "serieslinks") ) return;
if ( _epg_write_sect(fd, "serieslinks") ) goto fin;
RB_FOREACH(eo, &epg_serieslinks, uri_link) {
if (_epg_write(fd, epg_serieslink_serialize((epg_serieslink_t*)eo)))
return;
if (_epg_write(fd, epg_serieslink_serialize((epg_serieslink_t*)eo))) goto fin;
stats.seasons.total++;
}
if ( _epg_write_sect(fd, "broadcasts") ) return;
if ( _epg_write_sect(fd, "broadcasts") ) goto fin;
CHANNEL_FOREACH(ch) {
RB_FOREACH(ebc, &ch->ch_epg_schedule, sched_link) {
if (_epg_write(fd, epg_broadcast_serialize(ebc))) return;
if (_epg_write(fd, epg_broadcast_serialize(ebc))) goto fin;
stats.broadcasts.total++;
}
}
Expand All @@ -335,4 +349,7 @@ void epg_save ( void )
tvhlog(LOG_INFO, "epgdb", " seasons %d", stats.seasons.total);
tvhlog(LOG_INFO, "epgdb", " episodes %d", stats.episodes.total);
tvhlog(LOG_INFO, "epgdb", " broadcasts %d", stats.broadcasts.total);

fin:
close(fd);
}
1 change: 1 addition & 0 deletions src/epggrab.h
Expand Up @@ -58,6 +58,7 @@ typedef struct epggrab_stats
epggrab_stats_part_t seasons;
epggrab_stats_part_t episodes;
epggrab_stats_part_t broadcasts;
epggrab_stats_part_t config;
} epggrab_stats_t;

/* **************************************************************************
Expand Down

4 comments on commit 1fbbbb8

@ksooo
Copy link
Contributor

@ksooo ksooo commented on 1fbbbb8 Oct 6, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change seems to completely break recordings. All actually finished recordings are shown as failed (time missed) in webui. Upcoming recordings will not be started...

@perexg
Copy link
Contributor Author

@perexg perexg commented on 1fbbbb8 Oct 6, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tt's probably caused by 67f5e84 . but new recordings should be fine.. I cannot reproduce here...

@ProfYaffle
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure which commit broke the recordings, but I had it as well - not all of them, though, some remained scheduled while some upcoming recordings moved to 'Time Missed'. The good news is that it's recoverable: delete the failed recording record, and re-schedule (manually or disable/enable the relevant rule) and they're back again. At least, that seemed to work for me, and I've had two recordings run this morning as scheduled...

@perexg
Copy link
Contributor Author

@perexg perexg commented on 1fbbbb8 Oct 6, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, if you delete/readd the recordings, all should be fine.. If you like to move the logs from failed to finished, just change errors=0 in appropriate files in the /dvr/logs directory..

Please sign in to comment.