]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
descrambler/cwc: try to avoid dead-lock (descrambler_table_callback)
authorJaroslav Kysela <perex@perex.cz>
Mon, 19 Jun 2017 18:40:21 +0000 (20:40 +0200)
committerJaroslav Kysela <perex@perex.cz>
Mon, 19 Jun 2017 18:40:21 +0000 (20:40 +0200)
src/descrambler.h
src/descrambler/descrambler.c

index e45d7f2684f7a74442634b976761960973d6af6a..61de6007cdda3862b70c7eedeb33ae713708a763 100644 (file)
@@ -115,9 +115,15 @@ typedef void (*descrambler_section_callback_t)
  */
 typedef struct descrambler_ecmsec {
   LIST_ENTRY(descrambler_ecmsec) link;
+  LIST_ENTRY(descrambler_ecmsec) active_link;
+  int       refcnt;
+  uint8_t   changed;
   uint8_t   number;
+  uint8_t   quick_ecm_called;
   uint8_t  *last_data;
   int       last_data_len;
+  descrambler_section_callback_t callback;
+  void     *opaque;
 } descrambler_ecmsec_t;
 
 typedef struct descrambler_section {
@@ -125,7 +131,6 @@ typedef struct descrambler_section {
   descrambler_section_callback_t callback;
   void     *opaque;
   LIST_HEAD(, descrambler_ecmsec) ecmsecs;
-  uint8_t   quick_ecm_called;
 } descrambler_section_t;
 
 typedef struct descrambler_table {
index 8345c06f217c9a78569490aa4d942a2f4b8d835e..72d58766ae7fb816ad69f87dd2f1a3ad2be4588d 100644 (file)
@@ -709,8 +709,11 @@ cont:
                td->td_nicename,
                dr->dr_key_const ? " (const)" : "");
       descrambler_change_keystate(td, DS_IDLE, 0);
-      if (td->td_ecm_idle)
+      if (td->td_ecm_idle) {
+        pthread_mutex_unlock(&t->s_stream_mutex);
         td->td_ecm_idle(td);
+        pthread_mutex_lock(&t->s_stream_mutex);
+      }
       goto fin;
     }
 
@@ -858,7 +861,8 @@ descrambler_flush_table_data( service_t *t )
       while ((des = LIST_FIRST(&ds->ecmsecs)) != NULL) {
         LIST_REMOVE(des, link);
         free(des->last_data);
-        free(des);
+        if (atomic_dec(&des->refcnt, 1) == 0)
+          free(des);
       }
   }
   pthread_mutex_unlock(&mux->mm_descrambler_lock);
@@ -1212,6 +1216,7 @@ descrambler_table_callback
   descrambler_ecmsec_t *des;
   th_descrambler_runtime_t *dr;
   th_descrambler_key_t *tk;
+  LIST_HEAD(,descrambler_ecmsec) sections;
   int emm = (mt->mt_flags & MT_FAST) == 0;
   mpegts_service_t *t;
   int64_t clk;
@@ -1220,6 +1225,7 @@ descrambler_table_callback
 
   if (len < 6)
     return 0;
+  LIST_INIT(&sections);
   pthread_mutex_lock(&mt->mt_mux->mm_descrambler_lock);
   TAILQ_FOREACH(ds, &dt->sections, link) {
     if (!emm) {
@@ -1244,14 +1250,28 @@ descrambler_table_callback
       } else {
         des->last_data_len = 0;
       }
-      ds->callback(ds->opaque, mt->mt_pid, ptr, len, emm);
+      des->changed = 2;
+    } else {
+      des->changed = des->last_data != NULL ? 1 : 0;
+    }
+    atomic_add(&des->refcnt, 1);
+    des->callback = ds->callback;
+    des->opaque = ds->opaque;
+    LIST_INSERT_HEAD(&sections, des, active_link);
+  }
+  pthread_mutex_unlock(&mt->mt_mux->mm_descrambler_lock);
+
+  LIST_FOREACH(des, &sections, active_link) {
+    if (des->changed == 2) {
+      des->callback(des->opaque, mt->mt_pid, ptr, len, emm);
       if (!emm) { /* ECM */
         if ((t = mt->mt_service) != NULL) {
+          pthread_mutex_lock(&t->s_stream_mutex);
           /* The keys are requested from this moment */
           dr = t->s_descramble;
           if (dr) {
-            if (!dr->dr_quick_ecm && !ds->quick_ecm_called) {
-              ds->quick_ecm_called = 1;
+            if (!dr->dr_quick_ecm && !des->quick_ecm_called) {
+              des->quick_ecm_called = 1;
               dr->dr_quick_ecm = descrambler_quick_ecm(mt->mt_service, mt->mt_pid);
               if (dr->dr_quick_ecm)
                 tvhdebug(LS_DESCRAMBLER, "quick ECM enabled for service '%s'",
@@ -1271,6 +1291,7 @@ descrambler_table_callback
             tvhtrace(LS_DESCRAMBLER, "ECM message %02x (section %d, len %d, pid %d) for service \"%s\"",
                      ptr[0], des->number, len, mt->mt_pid, t->s_dvb_svcname);
           }
+          pthread_mutex_unlock(&t->s_stream_mutex);
         } else
           tvhtrace(LS_DESCRAMBLER, "Unknown fast table message %02x (section %d, len %d, pid %d)",
                    ptr[0], des->number, len, mt->mt_pid);
@@ -1291,8 +1312,9 @@ descrambler_table_callback
           tvhtrace(LS_DESCRAMBLER_EMM, "%s message %02x:{%02x:%02x}:%02x (len %d, pid %d)",
                    s, ptr[0], ptr[1], ptr[2], ptr[3], len, mt->mt_pid);
       }
-    } else if (des->last_data && !emm) {
+    } else if (des->changed == 1 && !emm) {
       if ((t = mt->mt_service) != NULL) {
+        pthread_mutex_lock(&t->s_stream_mutex);
         if ((dr = t->s_descramble) != NULL) {
           clk = mclk();
           for (i = 0; i < DESCRAMBLER_MAX_KEYS; i++) {
@@ -1308,10 +1330,16 @@ descrambler_table_callback
             }
           }
         }
+        pthread_mutex_unlock(&t->s_stream_mutex);
       }
     }
   }
-  pthread_mutex_unlock(&mt->mt_mux->mm_descrambler_lock);
+
+  while ((des = LIST_FIRST(&sections)) != NULL) {
+    LIST_REMOVE(des, active_link);
+    if (atomic_dec(&des->refcnt, 1) == 0)
+      free(des);
+  }
   return 0;
 }
 
@@ -1394,7 +1422,8 @@ descrambler_close_pid_( mpegts_mux_t *mux, void *opaque, int pid )
         while ((des = LIST_FIRST(&ds->ecmsecs)) != NULL) {
           LIST_REMOVE(des, link);
           free(des->last_data);
-          free(des);
+          if (atomic_dec(&des->refcnt, 1) == 0)
+            free(des);
         }
         if (TAILQ_FIRST(&dt->sections) == NULL) {
           TAILQ_REMOVE(&mux->mm_descrambler_tables, dt, link);
@@ -1442,7 +1471,8 @@ descrambler_flush_tables( mpegts_mux_t *mux )
       while ((des = LIST_FIRST(&ds->ecmsecs)) != NULL) {
         LIST_REMOVE(des, link);
         free(des->last_data);
-        free(des);
+        if (atomic_dec(&des->refcnt, 1) == 0)
+          free(des);
       }
       free(ds);
     }