Commit 5980be37 authored by Jean-Philippe André's avatar Jean-Philippe André

Lua: allow killing Extensions

If an Extension didn't respond in 10s, a dialog pops up and allows
the user to kill the Extension (thread cancellation)
parent a9e47d71
...@@ -21,13 +21,23 @@ ...@@ -21,13 +21,23 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. * 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 "vlc.h"
#include "libs.h" #include "libs.h"
#include "extension.h" #include "extension.h"
#include "assert.h" #include "assert.h"
#include <vlc_common.h>
#include <vlc_input.h> #include <vlc_input.h>
#include <vlc_events.h> #include <vlc_events.h>
#include <vlc_dialog.h>
/* Functions to register */ /* Functions to register */
static const luaL_Reg p_reg[] = static const luaL_Reg p_reg[] =
...@@ -54,6 +64,8 @@ static const char* const ppsz_capabilities[] = { ...@@ -54,6 +64,8 @@ static const char* const ppsz_capabilities[] = {
NULL NULL
}; };
#define WATCH_TIMER_PERIOD (10 * CLOCK_FREQ) ///< 10s period for the timer
static int ScanExtensions( extensions_manager_t *p_this ); static int ScanExtensions( extensions_manager_t *p_this );
static int ScanLuaCallback( vlc_object_t *p_this, const char *psz_script, static int ScanLuaCallback( vlc_object_t *p_this, const char *psz_script,
void *dummy ); void *dummy );
...@@ -65,8 +77,10 @@ static lua_State* GetLuaState( extensions_manager_t *p_mgr, ...@@ -65,8 +77,10 @@ static lua_State* GetLuaState( extensions_manager_t *p_mgr,
static int TriggerMenu( extension_t *p_ext, int id ); static int TriggerMenu( extension_t *p_ext, int id );
static int TriggerExtension( extensions_manager_t *p_mgr, static int TriggerExtension( extensions_manager_t *p_mgr,
extension_t *p_ext ); 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 */ /* Interactions */
static int vlclua_extension_dialog_callback( vlc_object_t *p_this, static int vlclua_extension_dialog_callback( vlc_object_t *p_this,
...@@ -166,6 +180,7 @@ void Close_Extension( 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->running_lock );
vlc_mutex_destroy( &p_ext->p_sys->command_lock ); vlc_mutex_destroy( &p_ext->p_sys->command_lock );
vlc_cond_destroy( &p_ext->p_sys->wait ); vlc_cond_destroy( &p_ext->p_sys->wait );
vlc_timer_destroy( p_ext->p_sys->timer );
free( p_ext->p_sys ); free( p_ext->p_sys );
free( p_ext ); free( p_ext );
...@@ -237,6 +252,15 @@ int ScanLuaCallback( vlc_object_t *p_this, const char *psz_script, ...@@ -237,6 +252,15 @@ int ScanLuaCallback( vlc_object_t *p_this, const char *psz_script,
} }
p_ext->p_sys->p_mgr = p_mgr; 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 */ /* Mutexes and conditions */
vlc_mutex_init( &p_ext->p_sys->command_lock ); vlc_mutex_init( &p_ext->p_sys->command_lock );
vlc_mutex_init( &p_ext->p_sys->running_lock ); vlc_mutex_init( &p_ext->p_sys->running_lock );
...@@ -743,6 +767,8 @@ static lua_State* GetLuaState( extensions_manager_t *p_mgr, ...@@ -743,6 +767,8 @@ static lua_State* GetLuaState( extensions_manager_t *p_mgr,
lua_getglobal( L, "vlc" ); lua_getglobal( L, "vlc" );
lua_pushcfunction( L, vlclua_extension_deactivate ); lua_pushcfunction( L, vlclua_extension_deactivate );
lua_setfield( L, -2, "deactivate" ); lua_setfield( L, -2, "deactivate" );
lua_pushcfunction( L, vlclua_extension_keep_alive );
lua_setfield( L, -2, "keep_alive" );
/* Setup the module search path */ /* Setup the module search path */
if( vlclua_add_modules_path( p_mgr, L, p_ext->psz_name ) ) 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, ...@@ -787,9 +813,9 @@ int lua_ExecuteFunction( extensions_manager_t *p_mgr, extension_t *p_ext,
* (see extension_thread.c) * (see extension_thread.c)
**/ **/
int lua_ExecuteFunctionVa( extensions_manager_t *p_mgr, extension_t *p_ext, 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; int i_args = 0;
assert( p_mgr != NULL ); assert( p_mgr != NULL );
assert( p_ext != NULL ); assert( p_ext != NULL );
...@@ -824,15 +850,33 @@ int lua_ExecuteFunctionVa( extensions_manager_t *p_mgr, extension_t *p_ext, ...@@ -824,15 +850,33 @@ int lua_ExecuteFunctionVa( extensions_manager_t *p_mgr, extension_t *p_ext,
} }
i_args ++; 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 ) ) if( lua_pcall( L, i_args, 1, 0 ) )
{ {
msg_Warn( p_mgr, "Error while running script %s, " msg_Warn( p_mgr, "Error while running script %s, "
"function %s(): %s", p_ext->psz_name, psz_function, "function %s(): %s", p_ext->psz_name, psz_function,
lua_tostring( L, lua_gettop( L ) ) ); 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: exit:
return i_ret; return i_ret;
...@@ -846,7 +890,7 @@ static inline int TriggerMenu( extension_t *p_ext, int i_id ) ...@@ -846,7 +890,7 @@ static inline int TriggerMenu( extension_t *p_ext, int i_id )
int lua_ExtensionTriggerMenu( extensions_manager_t *p_mgr, int lua_ExtensionTriggerMenu( extensions_manager_t *p_mgr,
extension_t *p_ext, int id ) extension_t *p_ext, int id )
{ {
int i_ret = VLC_EGENERIC; int i_ret = VLC_SUCCESS;
lua_State *L = GetLuaState( p_mgr, p_ext ); lua_State *L = GetLuaState( p_mgr, p_ext );
if( !L ) if( !L )
...@@ -865,20 +909,36 @@ int lua_ExtensionTriggerMenu( extensions_manager_t *p_mgr, ...@@ -865,20 +909,36 @@ int lua_ExtensionTriggerMenu( extensions_manager_t *p_mgr,
/* Pass id as unique argument to the function */ /* Pass id as unique argument to the function */
lua_pushinteger( L, id ); 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 ) if( lua_pcall( L, 1, 1, 0 ) != 0 )
{ {
msg_Warn( p_mgr, "Error while running script %s, " msg_Warn( p_mgr, "Error while running script %s, "
"function trigger_menu(): %s", p_ext->psz_name, "function trigger_menu(): %s", p_ext->psz_name,
lua_tostring( L, lua_gettop( L ) ) ); 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 ) if( i_ret < VLC_SUCCESS )
{ {
msg_Dbg( p_mgr, "Something went wrong in %s (%s:%d)", msg_Dbg( p_mgr, "Something went wrong in %s (%s:%d)",
__func__, __FILE__, __LINE__ ); __func__, __FILE__, __LINE__ );
} }
return i_ret; return i_ret;
} }
...@@ -937,6 +997,26 @@ int vlclua_extension_deactivate( lua_State *L ) ...@@ -937,6 +997,26 @@ int vlclua_extension_deactivate( lua_State *L )
return ( i_ret == VLC_SUCCESS ) ? 1 : 0; 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" /** Callback for the variable "dialog-event"
* @param p_this Current object owner of the extension and the dialog * @param p_this Current object owner of the extension and the dialog
* @param psz_var "dialog-event" * @param psz_var "dialog-event"
...@@ -995,23 +1075,87 @@ static void inputItemMetaChanged( const vlc_event_t *p_event, ...@@ -995,23 +1075,87 @@ static void inputItemMetaChanged( const vlc_event_t *p_event,
PushCommandUnique( p_ext, CMD_UPDATE_META ); PushCommandUnique( p_ext, CMD_UPDATE_META );
} }
/* Lock this extension. Can fail. */ /** Lock this extension. Can fail. */
bool LockExtension( extension_t *p_ext ) bool LockExtension( extension_t *p_ext )
{ {
vlc_mutex_lock( &p_ext->p_sys->command_lock );
if( p_ext->p_sys->b_exiting ) if( p_ext->p_sys->b_exiting )
{
vlc_mutex_unlock( &p_ext->p_sys->command_lock );
return false; return false;
}
vlc_mutex_lock( &p_ext->p_sys->running_lock ); vlc_mutex_lock( &p_ext->p_sys->running_lock );
if( p_ext->p_sys->b_exiting ) if( p_ext->p_sys->b_exiting )
{ {
vlc_mutex_unlock( &p_ext->p_sys->running_lock ); vlc_mutex_unlock( &p_ext->p_sys->running_lock );
vlc_mutex_unlock( &p_ext->p_sys->command_lock );
return false; return false;
} }
vlc_mutex_unlock( &p_ext->p_sys->command_lock );
return true; return true;
} }
/** Unlock this extension. */
void UnlockExtension( extension_t *p_ext ) void UnlockExtension( extension_t *p_ext )
{ {
vlc_mutex_unlock( &p_ext->p_sys->running_lock ); 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 );
}
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <vlc_extensions.h> #include <vlc_extensions.h>
#include <vlc_arrays.h> #include <vlc_arrays.h>
#include <vlc_dialog.h>
/* List of available commands */ /* List of available commands */
typedef enum typedef enum
...@@ -87,6 +88,10 @@ struct extension_sys_t ...@@ -87,6 +88,10 @@ struct extension_sys_t
struct command_t *next; ///< Next command struct command_t *next; ///< Next command
} *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; bool b_exiting;
}; };
...@@ -94,6 +99,7 @@ struct extension_sys_t ...@@ -94,6 +99,7 @@ struct extension_sys_t
int Activate( extensions_manager_t *p_mgr, extension_t * ); int Activate( extensions_manager_t *p_mgr, extension_t * );
bool IsActivated( 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 * ); 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 ); int __PushCommand( extension_t *ext, bool unique, command_type_e cmd, va_list options );
static inline int PushCommand( extension_t *ext, int cmd, ... ) static inline int PushCommand( extension_t *ext, int cmd, ... )
{ {
......
...@@ -145,6 +145,16 @@ int Deactivate( extensions_manager_t *p_mgr, extension_t *p_ext ) ...@@ -145,6 +145,16 @@ int Deactivate( extensions_manager_t *p_mgr, extension_t *p_ext )
return VLC_EGENERIC; 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 */ /* Free the list of commands */
if( p_ext->p_sys->command ) if( p_ext->p_sys->command )
FreeCommands( p_ext->p_sys->command->next ); FreeCommands( p_ext->p_sys->command->next );
...@@ -200,6 +210,16 @@ static int RemoveActivated( extensions_manager_t *p_mgr, extension_t *p_ext ) ...@@ -200,6 +210,16 @@ static int RemoveActivated( extensions_manager_t *p_mgr, extension_t *p_ext )
return (i_idx >= 0) ? VLC_SUCCESS : VLC_EGENERIC; 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 */ /** Push a UI command */
int __PushCommand( extension_t *p_ext, bool b_unique, command_type_e i_command, int __PushCommand( extension_t *p_ext, bool b_unique, command_type_e i_command,
va_list args ) va_list args )
...@@ -294,6 +314,7 @@ static void* Run( void *data ) ...@@ -294,6 +314,7 @@ static void* Run( void *data )
extension_t *p_ext = data; extension_t *p_ext = data;
extensions_manager_t *p_mgr = p_ext->p_sys->p_mgr; extensions_manager_t *p_mgr = p_ext->p_sys->p_mgr;
int cancel = vlc_savecancel();
vlc_mutex_lock( &p_ext->p_sys->command_lock ); vlc_mutex_lock( &p_ext->p_sys->command_lock );
while( !p_ext->p_sys->b_exiting ) while( !p_ext->p_sys->b_exiting )
...@@ -301,12 +322,14 @@ static void* Run( void *data ) ...@@ -301,12 +322,14 @@ static void* Run( void *data )
/* Pop command in front */ /* Pop command in front */
struct command_t *cmd = p_ext->p_sys->command; struct command_t *cmd = p_ext->p_sys->command;
vlc_mutex_unlock( &p_ext->p_sys->command_lock ); vlc_mutex_unlock( &p_ext->p_sys->command_lock );
vlc_restorecancel( cancel );
/* Run command */ /* Run command */
if( cmd ) if( cmd )
{ {
if( LockExtension( p_ext ) ) if( LockExtension( p_ext ) )
{ {
mutex_cleanup_push( &p_ext->p_sys->running_lock );
switch( cmd->i_command ) switch( cmd->i_command )
{ {
case CMD_ACTIVATE: case CMD_ACTIVATE:
...@@ -389,10 +412,12 @@ static void* Run( void *data ) ...@@ -389,10 +412,12 @@ static void* Run( void *data )
break; break;
} }
} }
vlc_cleanup_pop();
UnlockExtension( p_ext ); UnlockExtension( p_ext );
} }
} }
cancel = vlc_savecancel();
vlc_mutex_lock( &p_ext->p_sys->command_lock ); vlc_mutex_lock( &p_ext->p_sys->command_lock );
if( p_ext->p_sys->command ) if( p_ext->p_sys->command )
{ {
...@@ -410,6 +435,7 @@ static void* Run( void *data ) ...@@ -410,6 +435,7 @@ static void* Run( void *data )
vlc_mutex_unlock( &p_ext->p_sys->command_lock ); vlc_mutex_unlock( &p_ext->p_sys->command_lock );
msg_Dbg( p_mgr, "Extension thread end: '%s'", p_ext->psz_title ); msg_Dbg( p_mgr, "Extension thread end: '%s'", p_ext->psz_title );
vlc_restorecancel( cancel );
// Note: At this point, the extension should be deactivated // Note: At this point, the extension should be deactivated
return NULL; return NULL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment