]> git.sesse.net Git - vlc/blobdiff - src/control/event_async.c
Remove libvlc_get_vlc_instance hack from libvlc
[vlc] / src / control / event_async.c
index fb5c15ca17b42beca8f7f1984faa8e68bdfc84ff..ecb6fa8890e8259c0b9fa94dcc3c1f71efd9b322 100644 (file)
  * 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"
@@ -38,6 +44,9 @@ struct libvlc_event_async_queue {
     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;
 };
 
 /*
@@ -56,6 +65,11 @@ 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);
+}
+
 /* Lock must be held */
 static void push(libvlc_event_manager_t * p_em, libvlc_event_listener_t * listener, libvlc_event_t * event)
 {
@@ -63,12 +77,12 @@ static void push(libvlc_event_manager_t * p_em, libvlc_event_listener_t * listen
     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)
@@ -90,6 +104,16 @@ static void push(libvlc_event_manager_t * p_em, libvlc_event_listener_t * listen
     iter->next = elmt;
 }
 
+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)
 {
@@ -98,7 +122,7 @@ static bool pop(libvlc_event_manager_t * p_em, libvlc_event_listener_t * listene
 
     *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;
     free(elmt);
@@ -113,14 +137,18 @@ static void pop_listener(libvlc_event_manager_t * p_em, libvlc_event_listener_t
     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)->elements = to_delete->next;
             else
-                prev->next = iter->next;
-            free(iter);
+                prev->next = to_delete->next;
+            iter = to_delete->next;
+            free(to_delete);
+        }
+        else {
+            prev = iter;
+            iter = iter->next;
         }
-        prev = iter;
-        iter = iter->next;
     }
 }
 
@@ -131,9 +159,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,6 +177,8 @@ 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;
     while (iter) {
@@ -150,7 +186,7 @@ libvlc_event_async_fini(libvlc_event_manager_t * p_em)
         iter = iter->next;
         free(elemt_to_delete);
     }
-    
+
     free(queue(p_em));
 }
 
@@ -164,7 +200,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 +215,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 +227,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 +253,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 +270,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;
 }