event_async.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:9k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * event.c: New libvlc event control API
  3.  *****************************************************************************
  4.  * Copyright (C) 2007 the VideoLAN team
  5.  * $Id $
  6.  *
  7.  * Authors: Filippo Carone <filippo@carone.org>
  8.  *          Pierre d'Herbemont <pdherbemont # videolan.org>
  9.  *
  10.  * This program is free software; you can redistribute it and/or modify
  11.  * it under the terms of the GNU General Public License as published by
  12.  * the Free Software Foundation; either version 2 of the License, or
  13.  * (at your option) any later version.
  14.  *
  15.  * This program is distributed in the hope that it will be useful,
  16.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.  * GNU General Public License for more details.
  19.  *
  20.  * You should have received a copy of the GNU General Public License
  21.  * along with this program; if not, write to the Free Software
  22.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  23.  *****************************************************************************/
  24. #include <assert.h>
  25. #include <vlc/libvlc.h>
  26. #include "libvlc_internal.h"
  27. #include "event_internal.h"
  28. struct queue_elmt {
  29.     libvlc_event_listener_t listener;
  30.     libvlc_event_t event;
  31.     struct queue_elmt * next;
  32. };
  33. struct libvlc_event_async_queue {
  34.     struct queue_elmt * elements;
  35.     vlc_mutex_t lock;
  36.     vlc_cond_t signal;
  37.     vlc_thread_t thread;
  38.     bool is_idle;
  39.     vlc_cond_t signal_idle;
  40.     vlc_threadvar_t is_asynch_dispatch_thread_var;
  41. };
  42. /*
  43.  * Utilities
  44.  */
  45. static void*  event_async_loop(void * arg);
  46. static inline struct libvlc_event_async_queue * queue(libvlc_event_manager_t * p_em)
  47. {
  48.     return p_em->async_event_queue;
  49. }
  50. static inline bool is_queue_initialized(libvlc_event_manager_t * p_em)
  51. {
  52.     return queue(p_em) != NULL;
  53. }
  54. static inline bool current_thread_is_asynch_thread(libvlc_event_manager_t * p_em)
  55. {
  56.     return vlc_threadvar_get(queue(p_em)->is_asynch_dispatch_thread_var);
  57. }
  58. /* Lock must be held */
  59. static void push(libvlc_event_manager_t * p_em, libvlc_event_listener_t * listener, libvlc_event_t * event)
  60. {
  61. #ifndef NDEBUG
  62.     static const long MaxQueuedItem = 300000;
  63.     long count = 0;
  64. #endif
  65.     
  66.     struct queue_elmt * elmt = malloc(sizeof(struct queue_elmt));
  67.     elmt->listener = *listener;
  68.     elmt->event = *event;
  69.     elmt->next = NULL;
  70.     
  71.     /* Append to the end of the queue */
  72.     struct queue_elmt * iter = queue(p_em)->elements;
  73.     if(!iter)
  74.     {
  75.         queue(p_em)->elements = elmt;
  76.         return;
  77.     }
  78.     while (iter->next) {
  79.         iter = iter->next;
  80. #ifndef NDEBUG
  81.         if(count++ > MaxQueuedItem)
  82.         {
  83.             fprintf(stderr, "Warning: libvlc event overflow.n");
  84.             abort();
  85.         }
  86. #endif
  87.     }
  88.     iter->next = elmt;
  89. }
  90. static inline void queue_lock(libvlc_event_manager_t * p_em)
  91. {
  92.     vlc_mutex_lock(&queue(p_em)->lock);
  93. }
  94. static inline void queue_unlock(libvlc_event_manager_t * p_em)
  95. {
  96.     vlc_mutex_unlock(&queue(p_em)->lock);
  97. }
  98. /* Lock must be held */
  99. static bool pop(libvlc_event_manager_t * p_em, libvlc_event_listener_t * listener, libvlc_event_t * event)
  100. {
  101.     if(!queue(p_em)->elements)
  102.         return false; /* No elements */
  103.     *listener = queue(p_em)->elements->listener;
  104.     *event = queue(p_em)->elements->event;
  105.     
  106.     struct queue_elmt * elmt = queue(p_em)->elements;
  107.     queue(p_em)->elements = elmt->next;
  108.     free(elmt);
  109.     return true;
  110. }
  111. /* Lock must be held */
  112. static void pop_listener(libvlc_event_manager_t * p_em, libvlc_event_listener_t * listener)
  113. {
  114.     struct queue_elmt * iter = queue(p_em)->elements;
  115.     struct queue_elmt * prev = NULL;
  116.     while (iter) {
  117.         if(listeners_are_equal(&iter->listener, listener))
  118.         {
  119.             struct queue_elmt * to_delete = iter;
  120.             if(!prev)
  121.                 queue(p_em)->elements = to_delete->next;
  122.             else
  123.                 prev->next = to_delete->next;
  124.             iter = to_delete->next;
  125.             free(to_delete);
  126.         }
  127.         else {
  128.             prev = iter;
  129.             iter = iter->next;
  130.         }
  131.     }
  132. }
  133. /**************************************************************************
  134.  *       libvlc_event_async_fini (internal) :
  135.  *
  136.  * Destroy what might have been created by.
  137.  **************************************************************************/
  138. void
  139. libvlc_event_async_fini(libvlc_event_manager_t * p_em)
  140. {    
  141.     if(!is_queue_initialized(p_em)) return;
  142.     if(current_thread_is_asynch_thread(p_em))
  143.     {
  144.         fprintf(stderr, "*** Error: releasing the last reference of the observed object from its callback thread is not (yet!) supportedn");
  145.         abort();
  146.     }
  147.     
  148.     vlc_thread_t thread = queue(p_em)->thread;
  149.     if(thread)
  150.     {
  151.         vlc_cancel(thread);
  152.         vlc_join(thread, NULL);
  153.     }
  154.     vlc_mutex_destroy(&queue(p_em)->lock);
  155.     vlc_cond_destroy(&queue(p_em)->signal);
  156.     vlc_cond_destroy(&queue(p_em)->signal_idle);
  157.     vlc_threadvar_delete(&queue(p_em)->is_asynch_dispatch_thread_var);
  158.     struct queue_elmt * iter = queue(p_em)->elements;
  159.     while (iter) {
  160.         struct queue_elmt * elemt_to_delete = iter;
  161.         iter = iter->next;
  162.         free(elemt_to_delete);
  163.     }
  164.     
  165.     free(queue(p_em));
  166. }
  167. /**************************************************************************
  168.  *       libvlc_event_async_init (private) :
  169.  *
  170.  * Destroy what might have been created by.
  171.  **************************************************************************/
  172. static void
  173. libvlc_event_async_init(libvlc_event_manager_t * p_em)
  174. {
  175.     p_em->async_event_queue = calloc(1, sizeof(struct libvlc_event_async_queue));
  176.     int error = vlc_threadvar_create(&queue(p_em)->is_asynch_dispatch_thread_var, NULL);
  177.     assert(!error);
  178.     vlc_mutex_init(&queue(p_em)->lock);
  179.     vlc_cond_init(&queue(p_em)->signal);
  180.     vlc_cond_init(&queue(p_em)->signal_idle);
  181.     
  182.     error = vlc_clone (&queue(p_em)->thread, event_async_loop, p_em, VLC_THREAD_PRIORITY_LOW);
  183.     if(error)
  184.     {
  185.         free(p_em->async_event_queue);
  186.         p_em->async_event_queue = NULL;
  187.         return;
  188.     }
  189. }
  190. /**************************************************************************
  191.  *       libvlc_event_async_ensure_listener_removal (internal) :
  192.  *
  193.  * Make sure no more message will be issued to the listener.
  194.  **************************************************************************/
  195. void
  196. libvlc_event_async_ensure_listener_removal(libvlc_event_manager_t * p_em, libvlc_event_listener_t * listener)
  197. {
  198.     if(!is_queue_initialized(p_em)) return;
  199.     queue_lock(p_em);
  200.     pop_listener(p_em, listener);
  201.     
  202.     // Wait for the asynch_loop to have processed all events.
  203.     if(!current_thread_is_asynch_thread(p_em))
  204.     {
  205.         while(!queue(p_em)->is_idle)
  206.             vlc_cond_wait(&queue(p_em)->signal_idle, &queue(p_em)->lock);
  207.     }
  208.     queue_unlock(p_em);
  209. }
  210. /**************************************************************************
  211.  *       libvlc_event_async_dispatch (internal) :
  212.  *
  213.  * Send an event in an asynchronous way.
  214.  **************************************************************************/
  215. void
  216. libvlc_event_async_dispatch(libvlc_event_manager_t * p_em, libvlc_event_listener_t * listener, libvlc_event_t * event)
  217. {
  218.     // We do a lazy init here, to prevent constructing the thread when not needed.
  219.     vlc_mutex_lock(&p_em->object_lock);
  220.     if(!queue(p_em))
  221.         libvlc_event_async_init(p_em);
  222.     vlc_mutex_unlock(&p_em->object_lock);
  223.     queue_lock(p_em);
  224.     push(p_em, listener, event);
  225.     vlc_cond_signal(&queue(p_em)->signal);
  226.     queue_unlock(p_em);
  227. }
  228. /**************************************************************************
  229.  *       event_async_loop (private) :
  230.  *
  231.  * Send queued events.
  232.  **************************************************************************/
  233. static void * event_async_loop(void * arg)
  234. {
  235.     libvlc_event_manager_t * p_em = arg;
  236.     libvlc_event_listener_t listener;
  237.     libvlc_event_t event;
  238.     vlc_threadvar_set(queue(p_em)->is_asynch_dispatch_thread_var, (void*)true);
  239.     queue_lock(p_em);
  240.     while (true) {
  241.         int has_listener = pop(p_em, &listener, &event);
  242.         if (has_listener)
  243.         {
  244.             queue_unlock(p_em);
  245.             listener.pf_callback(&event, listener.p_user_data); // This might edit the queue
  246.             queue_lock(p_em);
  247.         }
  248.         else
  249.         {
  250.             queue(p_em)->is_idle = true;
  251.             mutex_cleanup_push(&queue(p_em)->lock);
  252.             vlc_cond_broadcast(&queue(p_em)->signal_idle); // We'll be idle
  253.             vlc_cond_wait(&queue(p_em)->signal, &queue(p_em)->lock);
  254.             vlc_cleanup_pop();
  255.             
  256.             queue(p_em)->is_idle = false;
  257.         }
  258.     }
  259.     queue_unlock(p_em);
  260.     return NULL;
  261. }