]> git.sesse.net Git - vlc/commitdiff
Lua: allow killing Extensions
authorJean-Philippe André <jpeg@videolan.org>
Mon, 17 Jan 2011 01:34:33 +0000 (02:34 +0100)
committerJean-Philippe André <jpeg@videolan.org>
Fri, 21 Jan 2011 23:42:09 +0000 (00:42 +0100)
If an Extension didn't respond in 10s, a dialog pops up and allows
the user to kill the Extension (thread cancellation)

modules/misc/lua/extension.c
modules/misc/lua/extension.h
modules/misc/lua/extension_thread.c

index f2a3261dbbe8e0ce2ce45bda1e5944faa30516b0..c50a76d8f5e36e8fcdbc0181c37f34cbdc462d16 100644 (file)
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
 
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
 #include "vlc.h"
 #include "libs.h"
 #include "extension.h"
 #include "assert.h"
 
+#include <vlc_common.h>
 #include <vlc_input.h>
 #include <vlc_events.h>
+#include <vlc_dialog.h>
 
 /* Functions to register */
 static const luaL_Reg p_reg[] =
@@ -54,6 +64,8 @@ static const char* const ppsz_capabilities[] = {
     NULL
 };
 
+#define WATCH_TIMER_PERIOD    (10 * CLOCK_FREQ) ///< 10s period for the timer
+
 static int ScanExtensions( extensions_manager_t *p_this );
 static int ScanLuaCallback( vlc_object_t *p_this, const char *psz_script,
                             void *dummy );
@@ -65,8 +77,10 @@ static lua_State* GetLuaState( extensions_manager_t *p_mgr,
 static int TriggerMenu( extension_t *p_ext, int id );
 static int TriggerExtension( extensions_manager_t *p_mgr,
                              extension_t *p_ext );
+static void WatchTimerCallback( void* );
 
-int vlclua_extension_deactivate( lua_State *L );
+static int vlclua_extension_deactivate( lua_State *L );
+static int vlclua_extension_keep_alive( lua_State *L );
 
 /* Interactions */
 static int vlclua_extension_dialog_callback( vlc_object_t *p_this,
@@ -166,6 +180,7 @@ void Close_Extension( vlc_object_t *p_this )
         vlc_mutex_destroy( &p_ext->p_sys->running_lock );
         vlc_mutex_destroy( &p_ext->p_sys->command_lock );
         vlc_cond_destroy( &p_ext->p_sys->wait );
+        vlc_timer_destroy( p_ext->p_sys->timer );
 
         free( p_ext->p_sys );
         free( p_ext );
@@ -237,6 +252,15 @@ int ScanLuaCallback( vlc_object_t *p_this, const char *psz_script,
     }
     p_ext->p_sys->p_mgr = p_mgr;
 
+    /* Watch timer */
+    if( vlc_timer_create( &p_ext->p_sys->timer, WatchTimerCallback, p_ext ) )
+    {
+        free( p_ext->psz_name );
+        free( p_ext->p_sys );
+        free( p_ext );
+        return 0;
+    }
+
     /* Mutexes and conditions */
     vlc_mutex_init( &p_ext->p_sys->command_lock );
     vlc_mutex_init( &p_ext->p_sys->running_lock );
@@ -743,6 +767,8 @@ static lua_State* GetLuaState( extensions_manager_t *p_mgr,
             lua_getglobal( L, "vlc" );
             lua_pushcfunction( L, vlclua_extension_deactivate );
             lua_setfield( L, -2, "deactivate" );
+            lua_pushcfunction( L, vlclua_extension_keep_alive );
+            lua_setfield( L, -2, "keep_alive" );
 
             /* Setup the module search path */
             if( vlclua_add_modules_path( p_mgr, L, p_ext->psz_name ) )
@@ -787,9 +813,9 @@ int lua_ExecuteFunction( extensions_manager_t *p_mgr, extension_t *p_ext,
  * (see extension_thread.c)
  **/
 int lua_ExecuteFunctionVa( extensions_manager_t *p_mgr, extension_t *p_ext,
-                            const char *psz_function, va_list args )
+                           const char *psz_function, va_list args )
 {
-    int i_ret = VLC_EGENERIC;
+    int i_ret = VLC_SUCCESS;
     int i_args = 0;
     assert( p_mgr != NULL );
     assert( p_ext != NULL );
@@ -824,15 +850,33 @@ int lua_ExecuteFunctionVa( extensions_manager_t *p_mgr, extension_t *p_ext,
         }
         i_args ++;
     }
+
+    // Create watch timer
+    vlc_mutex_lock( &p_ext->p_sys->command_lock );
+    vlc_timer_schedule( p_ext->p_sys->timer, false, WATCH_TIMER_PERIOD, 0 );
+    vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+
+    // Start actual call to Lua
     if( lua_pcall( L, i_args, 1, 0 ) )
     {
         msg_Warn( p_mgr, "Error while running script %s, "
                   "function %s(): %s", p_ext->psz_name, psz_function,
                   lua_tostring( L, lua_gettop( L ) ) );
-        goto exit;
+        i_ret = VLC_EGENERIC;
     }
 
-    i_ret = lua_DialogFlush( L );
+    // Reset watch timer and timestamp
+    vlc_mutex_lock( &p_ext->p_sys->command_lock );
+    if( p_ext->p_sys->progress )
+    {
+        dialog_ProgressDestroy( p_ext->p_sys->progress );
+        p_ext->p_sys->progress = NULL;
+    }
+    vlc_timer_schedule( p_ext->p_sys->timer, false, 0, 0 );
+    vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+
+    i_ret |= lua_DialogFlush( L );
+
 exit:
     return i_ret;
 
@@ -846,7 +890,7 @@ static inline int TriggerMenu( extension_t *p_ext, int i_id )
 int lua_ExtensionTriggerMenu( extensions_manager_t *p_mgr,
                               extension_t *p_ext, int id )
 {
-    int i_ret = VLC_EGENERIC;
+    int i_ret = VLC_SUCCESS;
     lua_State *L = GetLuaState( p_mgr, p_ext );
 
     if( !L )
@@ -865,20 +909,36 @@ int lua_ExtensionTriggerMenu( extensions_manager_t *p_mgr,
     /* Pass id as unique argument to the function */
     lua_pushinteger( L, id );
 
+    // Create watch timer
+    vlc_mutex_lock( &p_ext->p_sys->command_lock );
+    vlc_timer_schedule( p_ext->p_sys->timer, false, WATCH_TIMER_PERIOD, 0 );
+    vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+
     if( lua_pcall( L, 1, 1, 0 ) != 0 )
     {
         msg_Warn( p_mgr, "Error while running script %s, "
                   "function trigger_menu(): %s", p_ext->psz_name,
                   lua_tostring( L, lua_gettop( L ) ) );
-        return VLC_EGENERIC;
+        i_ret = VLC_EGENERIC;
+    }
+
+    // Reset watch timer and timestamp
+    vlc_mutex_lock( &p_ext->p_sys->command_lock );
+    if( p_ext->p_sys->progress )
+    {
+        dialog_ProgressDestroy( p_ext->p_sys->progress );
+        p_ext->p_sys->progress = NULL;
     }
+    vlc_timer_schedule( p_ext->p_sys->timer, false, 0, 0 );
+    vlc_mutex_unlock( &p_ext->p_sys->command_lock );
 
-    i_ret = lua_DialogFlush( L );
+    i_ret |= lua_DialogFlush( L );
     if( i_ret < VLC_SUCCESS )
     {
         msg_Dbg( p_mgr, "Something went wrong in %s (%s:%d)",
                  __func__, __FILE__, __LINE__ );
     }
+
     return i_ret;
 }
 
@@ -937,6 +997,26 @@ int vlclua_extension_deactivate( lua_State *L )
     return ( i_ret == VLC_SUCCESS ) ? 1 : 0;
 }
 
+/** Keep an extension alive. This resets the watch timer to 0
+ * @param L lua_State
+ * @note This is the "vlc.keep_alive()" function
+ **/
+int vlclua_extension_keep_alive( lua_State *L )
+{
+    extension_t *p_ext = vlclua_extension_get( L );
+
+    vlc_mutex_lock( &p_ext->p_sys->command_lock );
+    if( p_ext->p_sys->progress )
+    {
+        dialog_ProgressDestroy( p_ext->p_sys->progress );
+        p_ext->p_sys->progress = NULL;
+    }
+    vlc_timer_schedule( p_ext->p_sys->timer, false, WATCH_TIMER_PERIOD, 0 );
+    vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+
+    return 1;
+}
+
 /** Callback for the variable "dialog-event"
  * @param p_this Current object owner of the extension and the dialog
  * @param psz_var "dialog-event"
@@ -995,23 +1075,87 @@ static void inputItemMetaChanged( const vlc_event_t *p_event,
     PushCommandUnique( p_ext, CMD_UPDATE_META );
 }
 
-/* Lock this extension. Can fail. */
+/** Lock this extension. Can fail. */
 bool LockExtension( extension_t *p_ext )
 {
+    vlc_mutex_lock( &p_ext->p_sys->command_lock );
     if( p_ext->p_sys->b_exiting )
+    {
+        vlc_mutex_unlock( &p_ext->p_sys->command_lock );
         return false;
+    }
 
     vlc_mutex_lock( &p_ext->p_sys->running_lock );
     if( p_ext->p_sys->b_exiting )
     {
         vlc_mutex_unlock( &p_ext->p_sys->running_lock );
+        vlc_mutex_unlock( &p_ext->p_sys->command_lock );
         return false;
     }
 
+    vlc_mutex_unlock( &p_ext->p_sys->command_lock );
     return true;
 }
 
+/** Unlock this extension. */
 void UnlockExtension( extension_t *p_ext )
 {
     vlc_mutex_unlock( &p_ext->p_sys->running_lock );
 }
+
+/** Watch timer callback
+ * The timer expired, Lua may be stuck, ask the user what to do now
+ **/
+static void WatchTimerCallback( void *data )
+{
+    extension_t *p_ext = data;
+    extensions_manager_t *p_mgr = p_ext->p_sys->p_mgr;
+
+    char *message;
+    if( asprintf( &message, _( "Extension '%s' does not respond.\n"
+                               "Do you want to kill it now? " ),
+                  p_ext->psz_title ) == -1 )
+    {
+        return;
+    }
+
+    vlc_mutex_lock( &p_ext->p_sys->command_lock );
+
+    // Do we have a pending Deactivate command?
+    if( ( p_ext->p_sys->command &&
+          p_ext->p_sys->command->i_command == CMD_DEACTIVATE )
+        || ( p_ext->p_sys->command->next
+             && p_ext->p_sys->command->next->i_command == CMD_DEACTIVATE) )
+    {
+        if( p_ext->p_sys->progress )
+        {
+            dialog_ProgressDestroy( p_ext->p_sys->progress );
+            p_ext->p_sys->progress = NULL;
+        }
+        vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+        KillExtension( p_mgr, p_ext );
+        return;
+    }
+
+    if( !p_ext->p_sys->progress )
+    {
+        p_ext->p_sys->progress =
+                dialog_ProgressCreate( p_mgr, _( "Extension not responding!" ),
+                                       message,
+                                       _( "Yes" ) );
+        vlc_timer_schedule( p_ext->p_sys->timer, false, 100000, 0 );
+    }
+    else
+    {
+        if( dialog_ProgressCancelled( p_ext->p_sys->progress ) )
+        {
+            dialog_ProgressDestroy( p_ext->p_sys->progress );
+            p_ext->p_sys->progress = NULL;
+            vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+            KillExtension( p_mgr, p_ext );
+            return;
+        }
+        vlc_timer_schedule( p_ext->p_sys->timer, false, 100000, 0 );
+    }
+    vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+}
index 261de76dedeec78f7c321d5cad7b69f57beff0cd..05776729ca5686b46c7535dd0e5c24c95b6a634c 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <vlc_extensions.h>
 #include <vlc_arrays.h>
+#include <vlc_dialog.h>
 
 /* List of available commands */
 typedef enum
@@ -87,6 +88,10 @@ struct extension_sys_t
         struct command_t *next; ///< Next command
     } *command;
 
+    // The two following booleans are protected by command_lock
+    dialog_progress_bar_t *progress;
+    vlc_timer_t timer; ///< This timer makes sure Lua never gets stuck >5s
+
     bool b_exiting;
 };
 
@@ -94,6 +99,7 @@ struct extension_sys_t
 int Activate( extensions_manager_t *p_mgr, extension_t * );
 bool IsActivated( extensions_manager_t *p_mgr, extension_t * );
 int Deactivate( extensions_manager_t *p_mgr, extension_t * );
+void KillExtension( extensions_manager_t *p_mgr, extension_t *p_ext );
 int __PushCommand( extension_t *ext, bool unique, command_type_e cmd, va_list options );
 static inline int PushCommand( extension_t *ext, int cmd, ... )
 {
index 7f2ab3622ffee6064e9efe03d9d7d1162504f461..161fbbd721d0286a7b7d78babaeab96fbc1231f9 100644 (file)
@@ -145,6 +145,16 @@ int Deactivate( extensions_manager_t *p_mgr, extension_t *p_ext )
         return VLC_EGENERIC;
     }
 
+    if( p_ext->p_sys->progress )
+    {
+        // Extension is stuck, kill it now
+        dialog_ProgressDestroy( p_ext->p_sys->progress );
+        p_ext->p_sys->progress = NULL;
+        vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+        KillExtension( p_mgr, p_ext );
+        return VLC_SUCCESS;
+    }
+
     /* Free the list of commands */
     if( p_ext->p_sys->command )
         FreeCommands( p_ext->p_sys->command->next );
@@ -200,6 +210,16 @@ static int RemoveActivated( extensions_manager_t *p_mgr, extension_t *p_ext )
     return (i_idx >= 0) ? VLC_SUCCESS : VLC_EGENERIC;
 }
 
+void KillExtension( extensions_manager_t *p_mgr, extension_t *p_ext )
+{
+    /* Cancel thread if it seems stuck for a while */
+    msg_Dbg( p_mgr, "Killing extension now" );
+    vlc_cancel( p_ext->p_sys->thread );
+    lua_ExtensionDeactivate( p_mgr, p_ext );
+    p_ext->p_sys->b_exiting = true;
+    RemoveActivated( p_mgr, p_ext );
+}
+
 /** Push a UI command */
 int __PushCommand( extension_t *p_ext,  bool b_unique, command_type_e i_command,
                    va_list args )
@@ -294,6 +314,7 @@ static void* Run( void *data )
     extension_t *p_ext = data;
     extensions_manager_t *p_mgr = p_ext->p_sys->p_mgr;
 
+    int cancel = vlc_savecancel();
     vlc_mutex_lock( &p_ext->p_sys->command_lock );
 
     while( !p_ext->p_sys->b_exiting )
@@ -301,12 +322,14 @@ static void* Run( void *data )
         /* Pop command in front */
         struct command_t *cmd = p_ext->p_sys->command;
         vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+        vlc_restorecancel( cancel );
 
         /* Run command */
         if( cmd )
         {
             if( LockExtension( p_ext ) )
             {
+                mutex_cleanup_push( &p_ext->p_sys->running_lock );
                 switch( cmd->i_command )
                 {
                     case CMD_ACTIVATE:
@@ -389,10 +412,12 @@ static void* Run( void *data )
                         break;
                     }
                 }
+                vlc_cleanup_pop();
                 UnlockExtension( p_ext );
             }
         }
 
+        cancel = vlc_savecancel();
         vlc_mutex_lock( &p_ext->p_sys->command_lock );
         if( p_ext->p_sys->command )
         {
@@ -410,6 +435,7 @@ static void* Run( void *data )
 
     vlc_mutex_unlock( &p_ext->p_sys->command_lock );
     msg_Dbg( p_mgr, "Extension thread end: '%s'", p_ext->psz_title );
+    vlc_restorecancel( cancel );
 
     // Note: At this point, the extension should be deactivated
     return NULL;