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 @@
* 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 ) )
......@@ -789,7 +815,7 @@ int lua_ExecuteFunction( 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 )
{
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 );
}
......@@ -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, ... )
{
......
......@@ -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;
......
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