X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fcontrol%2Fevent_async.c;h=5dce2a3231b203376c18a9be88ea7ecedb3bb953;hb=e756f1322837217ab392d627346986434555f6ac;hp=fb5c15ca17b42beca8f7f1984faa8e68bdfc84ff;hpb=ebc50a983a40e0d22f0d5102c3d70364e857ed0b;p=vlc diff --git a/src/control/event_async.c b/src/control/event_async.c index fb5c15ca17..5dce2a3231 100644 --- a/src/control/event_async.c +++ b/src/control/event_async.c @@ -17,11 +17,17 @@ * 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 + #include #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; }