]> git.sesse.net Git - vlc/blobdiff - src/control/event_async.c
Use var_Inherit* instead of var_CreateGet*.
[vlc] / src / control / event_async.c
index fb5c15ca17b42beca8f7f1984faa8e68bdfc84ff..5dce2a3231b203376c18a9be88ea7ecedb3bb953 100644 (file)
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
 
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <assert.h>
+
 #include <vlc/libvlc.h>
 
 #include "libvlc_internal.h"
@@ -34,10 +40,16 @@ struct queue_elmt {
 };
 
 struct libvlc_event_async_queue {
-    struct queue_elmt * elements;
+    struct queue_elmt *first_elmt, *last_elmt;
     vlc_mutex_t lock;
     vlc_cond_t signal;
     vlc_thread_t thread;
+    bool is_idle;
+    vlc_cond_t signal_idle;
+    vlc_threadvar_t is_asynch_dispatch_thread_var;
+#ifndef NDEBUG
+    long count;
+#endif
 };
 
 /*
@@ -56,51 +68,66 @@ static inline bool is_queue_initialized(libvlc_event_manager_t * p_em)
     return queue(p_em) != NULL;
 }
 
+static inline bool current_thread_is_asynch_thread(libvlc_event_manager_t * p_em)
+{
+    return vlc_threadvar_get(queue(p_em)->is_asynch_dispatch_thread_var)
+            != NULL;
+}
+
 /* Lock must be held */
-static void push(libvlc_event_manager_t * p_em, libvlc_event_listener_t * listener, libvlc_event_t * event)
+static void push(libvlc_event_manager_t * p_em,
+                 libvlc_event_listener_t * listener, libvlc_event_t * event)
 {
-#ifndef NDEBUG
-    static const long MaxQueuedItem = 300000;
-    long count = 0;
-#endif
-    
     struct queue_elmt * elmt = malloc(sizeof(struct queue_elmt));
     elmt->listener = *listener;
     elmt->event = *event;
     elmt->next = NULL;
-    
+
     /* Append to the end of the queue */
-    struct queue_elmt * iter = queue(p_em)->elements;
-    if(!iter)
-    {
-        queue(p_em)->elements = elmt;
-        return;
-    }
+    if(!queue(p_em)->first_elmt)
+        queue(p_em)->first_elmt = elmt;
+    else
+        queue(p_em)->last_elmt->next = elmt;
+    queue(p_em)->last_elmt = elmt;
 
-    while (iter->next) {
-        iter = iter->next;
 #ifndef NDEBUG
-        if(count++ > MaxQueuedItem)
-        {
-            fprintf(stderr, "Warning: libvlc event overflow.\n");
-            abort();
-        }
-#endif
+    enum { MaxQueueSize = 300000 };
+    if(queue(p_em)->count++ > MaxQueueSize)
+    {
+        fprintf(stderr, "Warning: libvlc event overflow.\n");
+        abort();
     }
-    iter->next = elmt;
+#endif
+}
+
+static inline void queue_lock(libvlc_event_manager_t * p_em)
+{
+    vlc_mutex_lock(&queue(p_em)->lock);
+}
+
+static inline void queue_unlock(libvlc_event_manager_t * p_em)
+{
+    vlc_mutex_unlock(&queue(p_em)->lock);
 }
 
 /* Lock must be held */
-static bool pop(libvlc_event_manager_t * p_em, libvlc_event_listener_t * listener, libvlc_event_t * event)
+static bool pop(libvlc_event_manager_t * p_em,
+                libvlc_event_listener_t * listener, libvlc_event_t * event)
 {
-    if(!queue(p_em)->elements)
-        return false; /* No elements */
-
-    *listener = queue(p_em)->elements->listener;
-    *event = queue(p_em)->elements->event;
-    
-    struct queue_elmt * elmt = queue(p_em)->elements;
-    queue(p_em)->elements = elmt->next;
+    if(!queue(p_em)->first_elmt)
+        return false; /* No first_elmt */
+
+    struct queue_elmt * elmt = queue(p_em)->first_elmt;
+    *listener = elmt->listener;
+    *event = elmt->event;
+
+    queue(p_em)->first_elmt = elmt->next;
+    if( !elmt->next ) queue(p_em)->last_elmt=NULL;
+
+#ifndef NDEBUG
+    queue(p_em)->count--;
+#endif
+
     free(elmt);
     return true;
 }
@@ -108,20 +135,28 @@ static bool pop(libvlc_event_manager_t * p_em, libvlc_event_listener_t * listene
 /* Lock must be held */
 static void pop_listener(libvlc_event_manager_t * p_em, libvlc_event_listener_t * listener)
 {
-    struct queue_elmt * iter = queue(p_em)->elements;
+    struct queue_elmt * iter = queue(p_em)->first_elmt;
     struct queue_elmt * prev = NULL;
     while (iter) {
         if(listeners_are_equal(&iter->listener, listener))
         {
+            struct queue_elmt * to_delete = iter;
             if(!prev)
-                queue(p_em)->elements = iter->next;
+                queue(p_em)->first_elmt = to_delete->next;
             else
-                prev->next = iter->next;
-            free(iter);
+                prev->next = to_delete->next;
+            iter = to_delete->next;
+            free(to_delete);
+#ifndef NDEBUG
+            queue(p_em)->count--;
+#endif
+        }
+        else {
+            prev = iter;
+            iter = iter->next;
         }
-        prev = iter;
-        iter = iter->next;
     }
+    queue(p_em)->last_elmt=prev;
 }
 
 /**************************************************************************
@@ -131,9 +166,15 @@ static void pop_listener(libvlc_event_manager_t * p_em, libvlc_event_listener_t
  **************************************************************************/
 void
 libvlc_event_async_fini(libvlc_event_manager_t * p_em)
-{    
+{
     if(!is_queue_initialized(p_em)) return;
-    
+
+    if(current_thread_is_asynch_thread(p_em))
+    {
+        fprintf(stderr, "*** Error: releasing the last reference of the observed object from its callback thread is not (yet!) supported\n");
+        abort();
+    }
+
     vlc_thread_t thread = queue(p_em)->thread;
     if(thread)
     {
@@ -143,14 +184,16 @@ libvlc_event_async_fini(libvlc_event_manager_t * p_em)
 
     vlc_mutex_destroy(&queue(p_em)->lock);
     vlc_cond_destroy(&queue(p_em)->signal);
+    vlc_cond_destroy(&queue(p_em)->signal_idle);
+    vlc_threadvar_delete(&queue(p_em)->is_asynch_dispatch_thread_var);
 
-    struct queue_elmt * iter = queue(p_em)->elements;
+    struct queue_elmt * iter = queue(p_em)->first_elmt;
     while (iter) {
         struct queue_elmt * elemt_to_delete = iter;
         iter = iter->next;
         free(elemt_to_delete);
     }
-    
+
     free(queue(p_em));
 }
 
@@ -164,7 +207,14 @@ libvlc_event_async_init(libvlc_event_manager_t * p_em)
 {
     p_em->async_event_queue = calloc(1, sizeof(struct libvlc_event_async_queue));
 
-    int error = vlc_clone (&queue(p_em)->thread, event_async_loop, p_em, VLC_THREAD_PRIORITY_LOW);
+    int error = vlc_threadvar_create(&queue(p_em)->is_asynch_dispatch_thread_var, NULL);
+    assert(!error);
+
+    vlc_mutex_init(&queue(p_em)->lock);
+    vlc_cond_init(&queue(p_em)->signal);
+    vlc_cond_init(&queue(p_em)->signal_idle);
+
+    error = vlc_clone (&queue(p_em)->thread, event_async_loop, p_em, VLC_THREAD_PRIORITY_LOW);
     if(error)
     {
         free(p_em->async_event_queue);
@@ -172,8 +222,6 @@ libvlc_event_async_init(libvlc_event_manager_t * p_em)
         return;
     }
 
-    vlc_mutex_init_recursive(&queue(p_em)->lock); // Beware, this is re-entrant
-    vlc_cond_init(&queue(p_em)->signal);
 }
 
 /**************************************************************************
@@ -186,9 +234,16 @@ libvlc_event_async_ensure_listener_removal(libvlc_event_manager_t * p_em, libvlc
 {
     if(!is_queue_initialized(p_em)) return;
 
-    vlc_mutex_lock(&queue(p_em)->lock);
+    queue_lock(p_em);
     pop_listener(p_em, listener);
-    vlc_mutex_unlock(&queue(p_em)->lock);
+
+    // Wait for the asynch_loop to have processed all events.
+    if(!current_thread_is_asynch_thread(p_em))
+    {
+        while(!queue(p_em)->is_idle)
+            vlc_cond_wait(&queue(p_em)->signal_idle, &queue(p_em)->lock);
+    }
+    queue_unlock(p_em);
 }
 
 /**************************************************************************
@@ -205,10 +260,10 @@ libvlc_event_async_dispatch(libvlc_event_manager_t * p_em, libvlc_event_listener
         libvlc_event_async_init(p_em);
     vlc_mutex_unlock(&p_em->object_lock);
 
-    vlc_mutex_lock(&queue(p_em)->lock);
+    queue_lock(p_em);
     push(p_em, listener, event);
     vlc_cond_signal(&queue(p_em)->signal);
-    vlc_mutex_unlock(&queue(p_em)->lock);
+    queue_unlock(p_em);
 }
 
 /**************************************************************************
@@ -222,19 +277,30 @@ static void * event_async_loop(void * arg)
     libvlc_event_listener_t listener;
     libvlc_event_t event;
 
-    vlc_mutex_lock(&queue(p_em)->lock);
+    vlc_threadvar_set(queue(p_em)->is_asynch_dispatch_thread_var, p_em);
+
+    queue_lock(p_em);
     while (true) {
         int has_listener = pop(p_em, &listener, &event);
 
-        mutex_cleanup_push(&queue(p_em)->lock);
-
         if (has_listener)
-            listener.pf_callback( &event, listener.p_user_data ); // This might edit the queue, ->lock is recursive
+        {
+            queue_unlock(p_em);
+            listener.pf_callback(&event, listener.p_user_data); // This might edit the queue
+            queue_lock(p_em);
+        }
         else
+        {
+            queue(p_em)->is_idle = true;
+
+            mutex_cleanup_push(&queue(p_em)->lock);
+            vlc_cond_broadcast(&queue(p_em)->signal_idle); // We'll be idle
             vlc_cond_wait(&queue(p_em)->signal, &queue(p_em)->lock);
+            vlc_cleanup_pop();
 
-        vlc_cleanup_pop();
+            queue(p_em)->is_idle = false;
+        }
     }
-    vlc_mutex_unlock(&queue(p_em)->lock);
+    queue_unlock(p_em);
     return NULL;
 }