]> git.sesse.net Git - vlc/commitdiff
Support for cancellation cleanup functions
authorRémi Denis-Courmont <rdenis@simphalempin.com>
Sun, 10 Aug 2008 18:06:11 +0000 (21:06 +0300)
committerRémi Denis-Courmont <rdenis@simphalempin.com>
Wed, 27 Aug 2008 19:43:09 +0000 (22:43 +0300)
include/vlc_threads.h
src/misc/threads.c

index 24d85c0b3f4760030f227f0205ee31fab02fe3ae..e044e56d0a9ee625ca96f035580abe68de4a8fc0 100644 (file)
@@ -187,6 +187,8 @@ enum {
     VLC_RESTORE_CANCEL,
     VLC_TEST_CANCEL,
     VLC_DO_CANCEL,
+    VLC_CLEANUP_PUSH,
+    VLC_CLEANUP_POP,
 };
 #endif
 
@@ -317,6 +319,60 @@ static inline void vlc_testcancel (void)
 #endif
 }
 
+#if defined (LIBVLC_USE_PTHREAD)
+/**
+ * Registers a new procedure to run if the thread is cancelled (or otherwise
+ * exits prematurely). Any call to vlc_cleanup_push() <b>must</b> paired with a
+ * call to either vlc_cleanup_pop() or vlc_cleanup_run(). Branching into or out
+ * of the block between these two function calls is not allowed (read: it will
+ * likely crash the whole process). If multiple procedures are registered,
+ * they are handled in last-in first-out order.
+ *
+ * @param routine procedure to call if the thread ends
+ * @param arg argument for the procedure
+ */
+# define vlc_cleanup_push( routine, arg ) pthread_cleanup_push (routine, arg)
+
+/**
+ * Removes a cleanup procedure that was previously registered with
+ * vlc_cleanup_push().
+ */
+# define vlc_cleanup_pop( ) pthread_cleanup_pop (0)
+
+/**
+ * Removes a cleanup procedure that was previously registered with
+ * vlc_cleanup_push(), and executes it.
+ */
+# define vlc_cleanup_run( ) pthread_cleanup_pop (1)
+#else
+typedef struct vlc_cleanup_t vlc_cleanup_t;
+
+struct vlc_cleanup_t
+{
+    vlc_cleanup_t *next;
+    void         (*proc) (void *);
+    void          *data;
+};
+
+/* This macros opens a code block on purpose. This is needed for multiple
+ * calls within a single function. This also prevent Win32 developpers from
+ * writing code that would break on POSIX (POSIX opens a block as well). */
+# define vlc_cleanup_push( routine, arg ) \
+    do { \
+        vlc_cleanup_t vlc_cleanup_data = { NULL, routine, arg, }; \
+        vlc_control_cancel (VLC_CLEANUP_PUSH, &vlc_cleanup_data)
+
+# define vlc_cleanup_pop( ) \
+        vlc_control_cancel (VLC_CLEANUP_POP); \
+    } while (0)
+
+# define vlc_cleanup_run( ) \
+        vlc_control_cancel (VLC_CLEANUP_POP); \
+        vlc_cleanup_data.proc (vlc_cleanup_data.data); \
+    } while (0)
+
+#endif /* LIBVLC_USE_PTHREAD */
+
 /*****************************************************************************
  * vlc_cond_init: initialize a condition
  *****************************************************************************/
index 69b9fd44697df0695c63ae2bc15847d9fca636a5..33a76e4759deb1dfa4e3482d62e1c5d6ec17332d 100644 (file)
@@ -864,22 +864,19 @@ void vlc_thread_cancel (vlc_object_t *obj)
         vlc_cancel (priv->thread_id);
 }
 
-typedef struct vlc_cleanup_t
-{
-    struct vlc_cleanup_t *next;
-    void                (*proc) (void *);
-    void                 *data;
-} vlc_cleanup_t;
-
+#ifndef LIBVLC_USE_PTHREAD
 typedef struct vlc_cancel_t
 {
     vlc_cleanup_t *cleaners;
     bool           killable;
     bool           killed;
 } vlc_cancel_t;
+#endif
 
 void vlc_control_cancel (int cmd, ...)
 {
+    /* NOTE: This function only modifies thread-specific data, so there is no
+     * need to lock anything. */
 #ifdef LIBVLC_USE_PTHREAD
     (void) cmd;
     assert (0);
@@ -935,6 +932,22 @@ void vlc_control_cancel (int cmd, ...)
         case VLC_DO_CANCEL:
             nfo->killed = true;
             break;
+
+        case VLC_CLEANUP_PUSH:
+        {
+            /* cleaner is a pointer to the caller stack, no need to allocate
+             * and copy anything. As a nice side effect, this cannot fail. */
+            vlc_cleanup_t *cleaner = va_arg (ap, vlc_cleanup_t *);
+            cleaner->next = nfo->cleaners;
+            nfo->cleaners = cleaner;
+            break;
+        }
+
+        case VLC_CLEANUP_POP:
+        {
+            nfo->cleaners = nfo->cleaners->next;
+            break;
+        }
     }
     va_end (ap);
 #endif