Commit 80684e78 authored by Laurent Aimar's avatar Laurent Aimar

Fixed a race condition with input creation and events.

input_CreateThread* have been splitted in two functions:
 - input_Create
 - input_Start
to allow attaching to input events and creating settings/variables
before starting the input thread.

For consistency, input_StopThread has been renamed input_Stop.

Initial report from Hugo Beauzee-Luyssen.
parent a135b02f
......@@ -248,14 +248,16 @@ static inline void vlc_input_attachment_Delete( input_attachment_t *a )
#define INPUT_UPDATE_META 0x0040
#define INPUT_UPDATE_SIGNAL 0x0080
/** Get the input item for an input thread
* FIXME see src/input/item.c but is is unsafe unless
* you hold p_input
/**
* This defines private core storage for an input.
*/
VLC_EXPORT(input_item_t*, input_GetItem, (input_thread_t*));
typedef struct input_thread_private_t input_thread_private_t;
/**
* This defines an opaque input resource handler.
*/
typedef struct input_resource_t input_resource_t;
/**
* Main structure representing an input thread. This structure is mostly
* private. The only public fields are READ-ONLY. You must use the helpers
......@@ -431,32 +433,9 @@ typedef enum input_event_type_e
} input_event_type_e;
/** @}*/
/*****************************************************************************
* Prototypes
*****************************************************************************/
/**
* It will create a new input thread.
*
* You must call input_StopThread() on it and then vlc_object_release().
* Input queries
*/
#define input_CreateThread(a,b) __input_CreateThread(VLC_OBJECT(a),b)
VLC_EXPORT( input_thread_t *, __input_CreateThread, ( vlc_object_t *, input_item_t * ) );
/**
* It will ask a input_thread_t to stop.
*
* b_abort must be true when a user stop is requested and not because you have
* detected an error or an eof. It will be used to properly send the
* INPUT_EVENT_ABORT event.
*/
VLC_EXPORT( void, input_StopThread, ( input_thread_t *, bool b_abort ) );
#define input_Read(a,b,c) __input_Read(VLC_OBJECT(a),b, c)
VLC_EXPORT( int, __input_Read, ( vlc_object_t *, input_item_t *, bool ) );
enum input_query_e
{
/* input variable "position" */
......@@ -526,9 +505,37 @@ enum input_query_e
INPUT_GET_VOUTS, /* arg1=vout_thread_t ***, int * res=can fail */
};
/** @}*/
/*****************************************************************************
* Prototypes
*****************************************************************************/
#define input_Create(a,b,c,d) __input_Create(VLC_OBJECT(a),b,c,d)
VLC_EXPORT( input_thread_t *, __input_Create, ( vlc_object_t *p_parent, input_item_t *, const char *psz_log, input_resource_t * ) );
#define input_CreateAndStart(a,b,c) __input_CreateAndStart(VLC_OBJECT(a),b,c)
VLC_EXPORT( input_thread_t *, __input_CreateAndStart, ( vlc_object_t *p_parent, input_item_t *, const char *psz_log ) );
VLC_EXPORT( int, input_Start, ( input_thread_t * ) );
VLC_EXPORT( void, input_Stop, ( input_thread_t *, bool b_abort ) );
#define input_Read(a,b,c) __input_Read(VLC_OBJECT(a),b, c)
VLC_EXPORT( int, __input_Read, ( vlc_object_t *, input_item_t *, bool ) );
VLC_EXPORT( int, input_vaControl,( input_thread_t *, int i_query, va_list ) );
VLC_EXPORT( int, input_Control, ( input_thread_t *, int i_query, ... ) );
/**
* Get the input item for an input thread
*
* You have to keep a reference to the input or to the input_item_t until
* you do not need it anymore.
*/
VLC_EXPORT( input_item_t*, input_GetItem, ( input_thread_t * ) );
/**
* It will return the current state of the input.
* Provided for convenience.
......@@ -567,7 +574,7 @@ static inline vout_thread_t *input_GetVout( input_thread_t *p_input )
vlc_object_release( (vlc_object_t *)(pp_vout[i]) );
p_vout = (i_vout >= 1) ? pp_vout[0] : NULL;
free (pp_vout);
free( pp_vout );
return p_vout;
}
......
......@@ -159,12 +159,15 @@ static void Close( vlc_object_t *p_this )
for( i = 0; i < p_sys->i_input; i++ )
{
if( p_sd->p_sys->pp_input[i] )
{
input_StopThread( p_sd->p_sys->pp_input[i], true );
vlc_object_release( p_sd->p_sys->pp_input[i] );
p_sd->p_sys->pp_input[i] = NULL;
}
input_thread_t *p_input = p_sd->p_sys->pp_input[i];
if( !p_input )
continue;
input_Stop( p_input, true );
vlc_thread_join( p_input );
vlc_object_release( p_input );
p_sd->p_sys->pp_input[i] = NULL;
}
free( p_sd->p_sys->pp_input );
for( i = 0; i < p_sys->i_urls; i++ ) free( p_sys->ppsz_urls[i] );
......@@ -197,11 +200,14 @@ static void *Run( void *data )
for( int i = 0; i < p_sd->p_sys->i_input; i++ )
{
if( p_sd->p_sys->pp_input[i]->b_eof
|| p_sd->p_sys->pp_input[i]->b_error )
input_thread_t *p_input = p_sd->p_sys->pp_input[i];
if( p_input->b_eof || p_input->b_error )
{
input_StopThread( p_sd->p_sys->pp_input[i], false );
vlc_object_release( p_sd->p_sys->pp_input[i] );
input_Stop( p_input, false );
vlc_thread_join( p_input );
vlc_object_release( p_input );
p_sd->p_sys->pp_input[i] = NULL;
REMOVE_ELEM( p_sys->pp_input, p_sys->i_input, i );
i--;
......@@ -253,7 +259,7 @@ static void ParseUrls( services_discovery_t *p_sd, char *psz_urls )
services_discovery_AddItem( p_sd, p_input, NULL /* no cat */ );
vlc_gc_decref( p_input );
INSERT_ELEM( p_sys->pp_input, p_sys->i_input, p_sys->i_input,
input_CreateThread( p_sd, p_input ) );
input_CreateAndStart( p_sd, p_input, NULL ) );
}
if( psz_tok ) psz_urls = psz_tok+1;
else return;
......
......@@ -89,7 +89,7 @@ static void release_input_thread( libvlc_media_player_t *p_mi, bool b_input_abor
input_event_changed, p_mi );
/* We owned this one */
input_StopThread( p_input_thread, b_input_abort );
input_Stop( p_input_thread, b_input_abort );
vlc_thread_join( p_input_thread );
var_Destroy( p_input_thread, "drawable-hwnd" );
......@@ -606,8 +606,8 @@ void libvlc_media_player_play( libvlc_media_player_t *p_mi,
return;
}
p_mi->p_input_thread = input_CreateThread(
p_mi->p_libvlc_instance->p_libvlc_int, p_mi->p_md->p_input_item );
p_mi->p_input_thread = input_Create( p_mi->p_libvlc_instance->p_libvlc_int,
p_mi->p_md->p_input_item, NULL, NULL );
if( !p_mi->p_input_thread )
{
......@@ -643,6 +643,12 @@ void libvlc_media_player_play( libvlc_media_player_t *p_mi,
var_AddCallback( p_input_thread, "can-pause", input_pausable_changed, p_mi );
var_AddCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
if( input_Start( p_input_thread ) )
{
vlc_object_release( p_input_thread );
p_mi->p_input_thread = NULL;
}
vlc_mutex_unlock( &p_mi->object_lock );
}
......@@ -717,7 +723,7 @@ void libvlc_media_player_stop( libvlc_media_player_t *p_mi,
if( !p_input_thread )
return;
input_StopThread( p_input_thread, true );
input_Stop( p_input_thread, true );
vlc_object_release( p_input_thread );
p_mi->p_input_thread = NULL;
}
......
......@@ -113,6 +113,200 @@ static void input_ChangeState( input_thread_t *p_input, int i_state ); /* TODO f
/* Do not let a pts_delay from access/demux go beyong 60s */
#define INPUT_PTS_DELAY_MAX INT64_C(60000000)
/**
* Create a new input_thread_t.
*
* You need to call input_Start on it when you are done
* adding callback on the variables/events you want to monitor.
*
* \param p_parent a vlc_object
* \param p_item an input item
* \param psz_log an optional prefix for this input logs
* \param p_resource an optional input ressource
* \return a pointer to the spawned input thread
*/
input_thread_t *__input_Create( vlc_object_t *p_parent,
input_item_t *p_item,
const char *psz_log, input_resource_t *p_resource )
{
return Create( p_parent, p_item, psz_log, false, p_resource );
}
/**
* Create a new input_thread_t and start it.
*
* Provided for convenience.
*
* \see input_Create
*/
input_thread_t *__input_CreateAndStart( vlc_object_t *p_parent,
input_item_t *p_item, const char *psz_log )
{
input_thread_t *p_input = __input_Create( p_parent, p_item, psz_log, NULL );
if( input_Start( p_input ) )
{
vlc_object_release( p_input );
return NULL;
}
return p_input;
}
/**
* Initialize an input thread and run it. This thread will clean after itself,
* you can forget about it. It can work either in blocking or non-blocking mode
*
* \param p_parent a vlc_object
* \param p_item an input item
* \param b_block should we block until read is finished ?
* \return an error code, VLC_SUCCESS on success
*/
int __input_Read( vlc_object_t *p_parent, input_item_t *p_item,
bool b_block )
{
input_thread_t *p_input;
p_input = Create( p_parent, p_item, NULL, false, NULL );
if( !p_input )
return VLC_EGENERIC;
if( b_block )
{
RunAndDestroy( VLC_OBJECT(p_input) );
return VLC_SUCCESS;
}
else
{
if( vlc_thread_create( p_input, "input", RunAndDestroy,
VLC_THREAD_PRIORITY_INPUT ) )
{
input_ChangeState( p_input, ERROR_S );
msg_Err( p_input, "cannot create input thread" );
vlc_object_release( p_input );
return VLC_EGENERIC;
}
}
return VLC_SUCCESS;
}
/**
* Initialize an input and initialize it to preparse the item
* This function is blocking. It will only accept parsing regular files.
*
* \param p_parent a vlc_object_t
* \param p_item an input item
* \return VLC_SUCCESS or an error
*/
int input_Preparse( vlc_object_t *p_parent, input_item_t *p_item )
{
input_thread_t *p_input;
/* Allocate descriptor */
p_input = Create( p_parent, p_item, NULL, true, NULL );
if( !p_input )
return VLC_EGENERIC;
if( !Init( p_input ) )
End( p_input );
vlc_object_release( p_input );
return VLC_SUCCESS;
}
/**
* Start a input_thread_t created by input_Create.
*
* You must not start an already running input_thread_t.
*
* \param the input thread to start
*/
int input_Start( input_thread_t *p_input )
{
/* Create thread and wait for its readiness. */
if( vlc_thread_create( p_input, "input", Run,
VLC_THREAD_PRIORITY_INPUT ) )
{
input_ChangeState( p_input, ERROR_S );
msg_Err( p_input, "cannot create input thread" );
return VLC_EGENERIC;
}
return VLC_SUCCESS;
}
/**
* Request a running input thread to stop and die
*
* b_abort must be true when a user stop is requested and not because you have
* detected an error or an eof. It will be used to properly send the
* INPUT_EVENT_ABORT event.
*
* \param p_input the input thread to stop
* \param b_abort true if the input has been aborted by a user request
*/
void input_Stop( input_thread_t *p_input, bool b_abort )
{
/* Set die for input and ALL of this childrens (even (grand-)grand-childrens)
* It is needed here even if it is done in INPUT_CONTROL_SET_DIE handler to
* unlock the control loop */
ObjectKillChildrens( p_input, VLC_OBJECT(p_input) );
vlc_mutex_lock( &p_input->p->lock_control );
p_input->p->b_abort |= b_abort;
vlc_mutex_unlock( &p_input->p->lock_control );
input_ControlPush( p_input, INPUT_CONTROL_SET_DIE, NULL );
}
input_resource_t *input_DetachResource( input_thread_t *p_input )
{
assert( p_input->b_dead );
input_resource_SetInput( p_input->p->p_resource, NULL );
input_resource_t *p_resource = input_resource_Detach( p_input->p->p_resource );
p_input->p->p_sout = NULL;
return p_resource;
}
/**
* Get the item from an input thread
* FIXME it does not increase ref count of the item.
* if it is used after p_input is destroyed nothing prevent it from
* being freed.
*/
input_item_t *input_GetItem( input_thread_t *p_input )
{
assert( p_input && p_input->p );
return p_input->p->p_item;
}
/*****************************************************************************
* ObjectKillChildrens
*****************************************************************************/
static void ObjectKillChildrens( input_thread_t *p_input, vlc_object_t *p_obj )
{
vlc_list_t *p_list;
int i;
/* FIXME ObjectKillChildrens seems a very bad idea in fact */
i = vlc_internals( p_obj )->i_object_type;
if( i == VLC_OBJECT_VOUT ||i == VLC_OBJECT_AOUT ||
p_obj == VLC_OBJECT(p_input->p->p_sout) ||
i == VLC_OBJECT_DECODER || i == VLC_OBJECT_PACKETIZER )
return;
vlc_object_kill( p_obj );
p_list = vlc_list_children( p_obj );
for( i = 0; i < p_list->i_count; i++ )
ObjectKillChildrens( p_input, p_list->p_values[i].p_object );
vlc_list_release( p_list );
}
/*****************************************************************************
* This function creates a new input, and returns a pointer
* to its description. On error, it returns NULL.
......@@ -333,174 +527,6 @@ static void Destructor( input_thread_t * p_input )
free( p_input->p );
}
/**
* Initialize an input thread and run it. You will need to monitor the
* thread to clean up after it is done
*
* \param p_parent a vlc_object
* \param p_item an input item
* \return a pointer to the spawned input thread
*/
input_thread_t *__input_CreateThread( vlc_object_t *p_parent,
input_item_t *p_item )
{
return __input_CreateThreadExtended( p_parent, p_item, NULL, NULL );
}
/* */
input_thread_t *__input_CreateThreadExtended( vlc_object_t *p_parent,
input_item_t *p_item,
const char *psz_log, input_resource_t *p_resource )
{
input_thread_t *p_input;
p_input = Create( p_parent, p_item, psz_log, false, p_resource );
if( !p_input )
return NULL;
/* Create thread and wait for its readiness. */
if( vlc_thread_create( p_input, "input", Run,
VLC_THREAD_PRIORITY_INPUT ) )
{
input_ChangeState( p_input, ERROR_S );
msg_Err( p_input, "cannot create input thread" );
vlc_object_detach( p_input );
vlc_object_release( p_input );
return NULL;
}
return p_input;
}
/**
* Initialize an input thread and run it. This thread will clean after itself,
* you can forget about it. It can work either in blocking or non-blocking mode
*
* \param p_parent a vlc_object
* \param p_item an input item
* \param b_block should we block until read is finished ?
* \return an error code, VLC_SUCCESS on success
*/
int __input_Read( vlc_object_t *p_parent, input_item_t *p_item,
bool b_block )
{
input_thread_t *p_input;
p_input = Create( p_parent, p_item, NULL, false, NULL );
if( !p_input )
return VLC_EGENERIC;
if( b_block )
{
RunAndDestroy( VLC_OBJECT(p_input) );
return VLC_SUCCESS;
}
else
{
if( vlc_thread_create( p_input, "input", RunAndDestroy,
VLC_THREAD_PRIORITY_INPUT ) )
{
input_ChangeState( p_input, ERROR_S );
msg_Err( p_input, "cannot create input thread" );
vlc_object_release( p_input );
return VLC_EGENERIC;
}
}
return VLC_SUCCESS;
}
/**
* Initialize an input and initialize it to preparse the item
* This function is blocking. It will only accept parsing regular files.
*
* \param p_parent a vlc_object_t
* \param p_item an input item
* \return VLC_SUCCESS or an error
*/
int input_Preparse( vlc_object_t *p_parent, input_item_t *p_item )
{
input_thread_t *p_input;
/* Allocate descriptor */
p_input = Create( p_parent, p_item, NULL, true, NULL );
if( !p_input )
return VLC_EGENERIC;
if( !Init( p_input ) )
End( p_input );
vlc_object_detach( p_input );
vlc_object_release( p_input );
return VLC_SUCCESS;
}
/**
* Request a running input thread to stop and die
*
* \param the input thread to stop
*/
void input_StopThread( input_thread_t *p_input, bool b_abort )
{
/* Set die for input and ALL of this childrens (even (grand-)grand-childrens)
* It is needed here even if it is done in INPUT_CONTROL_SET_DIE handler to
* unlock the control loop */
ObjectKillChildrens( p_input, VLC_OBJECT(p_input) );
vlc_mutex_lock( &p_input->p->lock_control );
p_input->p->b_abort |= b_abort;
vlc_mutex_unlock( &p_input->p->lock_control );
input_ControlPush( p_input, INPUT_CONTROL_SET_DIE, NULL );
}
input_resource_t *input_DetachResource( input_thread_t *p_input )
{
assert( p_input->b_dead );
input_resource_SetInput( p_input->p->p_resource, NULL );
input_resource_t *p_resource = input_resource_Detach( p_input->p->p_resource );
p_input->p->p_sout = NULL;
return p_resource;
}
/**
* Get the item from an input thread
* FIXME it does not increase ref count of the item.
* if it is used after p_input is destroyed nothing prevent it from
* being freed.
*/
input_item_t *input_GetItem( input_thread_t *p_input )
{
assert( p_input && p_input->p );
return p_input->p->p_item;
}
/*****************************************************************************
* ObjectKillChildrens
*****************************************************************************/
static void ObjectKillChildrens( input_thread_t *p_input, vlc_object_t *p_obj )
{
vlc_list_t *p_list;
int i;
/* FIXME ObjectKillChildrens seems a very bad idea in fact */
i = vlc_internals( p_obj )->i_object_type;
if( i == VLC_OBJECT_VOUT ||i == VLC_OBJECT_AOUT ||
p_obj == VLC_OBJECT(p_input->p->p_sout) ||
i == VLC_OBJECT_DECODER || i == VLC_OBJECT_PACKETIZER )
return;
vlc_object_kill( p_obj );
p_list = vlc_list_children( p_obj );
for( i = 0; i < p_list->i_count; i++ )
ObjectKillChildrens( p_input, p_list->p_values[i].p_object );
vlc_list_release( p_list );
}
/*****************************************************************************
* Run: main thread loop
* This is the "normal" thread that spawns the input processing chain,
......
......@@ -46,11 +46,6 @@ int input_Preparse( vlc_object_t *, input_item_t * );
* FIXME it should NOT be defined here or not coded in misc/stats.c */
input_stats_t *stats_NewInputStats( input_thread_t *p_input );
/**
* This defines an opaque input resource handler.
*/
typedef struct input_resource_t input_resource_t;
/**
* This function releases an input_resource_t and all associated resources.
*/
......@@ -74,8 +69,6 @@ void input_resource_TerminateVout( input_resource_t *p_resource );
bool input_resource_HasVout( input_resource_t *p_resource );
/* input.c */
#define input_CreateThreadExtended(a,b,c,d) __input_CreateThreadExtended(VLC_OBJECT(a),b,c,d)
input_thread_t *__input_CreateThreadExtended ( vlc_object_t *, input_item_t *, const char *, input_resource_t * );
/**
* This function detaches resources from a dead input.
......
......@@ -531,12 +531,12 @@ static int vlm_OnMediaUpdate( vlm_t *p_vlm, vlm_media_sys_t *p_media )
if( asprintf( &psz_header, _("Media: %s"), p_cfg->psz_name ) == -1 )
psz_header = NULL;
if( (p_input = input_CreateThreadExtended( p_vlm->p_libvlc, p_media->vod.p_item, psz_header, NULL ) ) )
if( (p_input = input_CreateAndStart( p_vlm->p_libvlc, p_media->vod.p_item, psz_header ) ) )
{
while( !p_input->b_eof && !p_input->b_error )
msleep( 100000 );
input_StopThread( p_input, false );
input_Stop( p_input, false );
vlc_thread_join( p_input );
vlc_object_release( p_input );
}
......@@ -778,7 +778,7 @@ static void vlm_MediaInstanceDelete( vlm_t *p_vlm, int64_t id, vlm_media_instanc
{
input_resource_t *p_resource;
input_StopThread( p_input, true );
input_Stop( p_input, true );
vlc_thread_join( p_input );
p_resource = input_DetachResource( p_input );
......@@ -861,7 +861,7 @@ static int vlm_ControlMediaInstanceStart( vlm_t *p_vlm, int64_t id, const char *
return VLC_SUCCESS;
}
input_StopThread( p_input, !p_input->b_eof && !p_input->b_error );
input_Stop( p_input, !p_input->b_eof && !p_input->b_error );
vlc_thread_join( p_input );
p_instance->p_input_resource = input_DetachResource( p_input );
......@@ -881,8 +881,13 @@ static int vlm_ControlMediaInstanceStart( vlm_t *p_vlm, int64_t id, const char *
if( asprintf( &psz_log, _("Media: %s"), p_media->cfg.psz_name ) != -1 )
{
p_instance->p_input = input_CreateThreadExtended( p_vlm->p_libvlc, p_instance->p_item,
psz_log, p_instance->p_input_resource );
p_instance->p_input = input_Create( p_vlm->p_libvlc, p_instance->p_item,
psz_log, p_instance->p_input_resource );
if( p_instance->p_input && input_Start( p_instance->p_input ) )
{
vlc_object_release( p_instance->p_input );
p_instance->p_input = NULL;
}
p_instance->p_input_resource = NULL;
if( !p_instance->p_input )
......
......@@ -169,8 +169,9 @@ image_Mime2Fourcc
image_Type2Fourcc
InitMD5
input_Control
__input_Create
__input_CreateAndStart
input_CreateFilename
__input_CreateThread
input_DecoderDecode
input_DecoderDelete
input_DecoderNew
......@@ -199,7 +200,8 @@ input_item_SetURI
input_MetaTypeToLocalizedString
__input_Read
input_SplitMRL
input_StopThread
input_Start
input_Stop
input_vaControl
__intf_Create
__intf_Eject
......
......@@ -823,7 +823,7 @@ static void GoAndPreparse( playlist_t *p_playlist, int i_mode,
pl_priv(p_playlist)->request.i_skip = 0;
pl_priv(p_playlist)->request.p_item = p_toplay;
if( pl_priv(p_playlist)->p_input )
input_StopThread( pl_priv(p_playlist)->p_input, true );
input_Stop( pl_priv(p_playlist)->p_input, true );
pl_priv(p_playlist)->request.i_status = PLAYLIST_RUNNING;
vlc_cond_signal( &pl_priv(p_playlist)->signal );
}
......
......@@ -251,14 +251,17 @@ static int PlayItem( playlist_t *p_playlist, playlist_item_t *p_item )
assert( p_sys->p_input == NULL );
input_thread_t *p_input_thread =
input_CreateThreadExtended( p_playlist, p_input, NULL, p_sys->p_input_resource );
input_thread_t *p_input_thread = input_Create( p_playlist, p_input, NULL, p_sys->p_input_resource );
if( p_input_thread )
{
p_sys->p_input = p_input_thread;
var_AddCallback( p_input_thread, "intf-event", InputEvent, p_playlist );
if( input_Start( p_sys->p_input ) )
{
vlc_object_release( p_input_thread );
p_sys->p_input = p_input_thread = NULL;
}
}
p_sys->p_input_resource = NULL;
......@@ -473,7 +476,7 @@ static int LoopInput( playlist_t *p_playlist )
if( ( p_sys->request.b_request || !vlc_object_alive( p_playlist ) ) && !p_input->b_die )
{
PL_DEBUG( "incoming request - stopping current input" );
input_StopThread( p_input, true );
input_Stop( p_input, true );
}
/* This input is dead. Remove it ! */
......@@ -514,7 +517,7 @@ static int LoopInput( playlist_t *p_playlist )
else if( p_input->b_error || p_input->b_eof )
{
PL_DEBUG( "finished input" );
input_StopThread( p_input, false );
input_Stop( p_input, false );
}
return VLC_SUCCESS;
}
......
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