From aa6b5c552d671fc1fce4ca38e5aff41ae259c28e Mon Sep 17 00:00:00 2001 From: =?utf8?q?R=C3=A9mi=20Denis-Courmont?= Date: Sun, 10 Aug 2008 21:06:11 +0300 Subject: [PATCH] Support for cancellation cleanup functions --- include/vlc_threads.h | 56 +++++++++++++++++++++++++++++++++++++++++++ src/misc/threads.c | 27 +++++++++++++++------ 2 files changed, 76 insertions(+), 7 deletions(-) diff --git a/include/vlc_threads.h b/include/vlc_threads.h index 24d85c0b3f..e044e56d0a 100644 --- a/include/vlc_threads.h +++ b/include/vlc_threads.h @@ -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() must 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 *****************************************************************************/ diff --git a/src/misc/threads.c b/src/misc/threads.c index 69b9fd4469..33a76e4759 100644 --- a/src/misc/threads.c +++ b/src/misc/threads.c @@ -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 -- 2.39.5