]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
epg: more robust 32-bit object ID handling
authorJaroslav Kysela <perex@perex.cz>
Sun, 5 Oct 2014 18:51:13 +0000 (20:51 +0200)
committerJaroslav Kysela <perex@perex.cz>
Sun, 5 Oct 2014 18:51:13 +0000 (20:51 +0200)
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).

src/epg.c
src/epg.h
src/epgdb.c
src/epggrab.h

index ad1a5edd898c7b7d165a8a014bc8c2e9505a6506..38d637977620629ddb1eb7f8e3bf57081900f65a 100644 (file)
--- a/src/epg.c
+++ b/src/epg.c
@@ -39,6 +39,9 @@
 #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;
@@ -46,17 +49,29 @@ 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);
@@ -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 )
@@ -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 
@@ -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;
 }
 
@@ -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;
index fd17e5da7875af4cc7cf145d12a8f47fed659a63..340a23f44b64feeee3d6e2b161a468f76d115dd6 100644 (file)
--- a/src/epg.h
+++ b/src/epg.h
@@ -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
  
@@ -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
  * ***********************************************************************/
index 32d9adb1995918deefcda1f896d2a606afaaeb8e..1ba29ac604914b96ed02b4710cc3581b5e3d069e 100644 (file)
@@ -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);
@@ -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);
@@ -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++;
     }
   }
@@ -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);
 }
index 793583040f7b7630f532d3c2328743b20887b35d..518dcb4ca5ef39aa19dc7609f0bd8ec64e94c84c 100644 (file)
@@ -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;
 
 /* **************************************************************************