Commit d007a98f authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

vlc_clone, vlc_join: untangle objects and threads

parent 079a3e8f
...@@ -122,7 +122,13 @@ typedef pthread_cond_t vlc_cond_t; ...@@ -122,7 +122,13 @@ typedef pthread_cond_t vlc_cond_t;
typedef pthread_key_t vlc_threadvar_t; typedef pthread_key_t vlc_threadvar_t;
#elif defined( WIN32 ) || defined( UNDER_CE ) #elif defined( WIN32 ) || defined( UNDER_CE )
typedef HANDLE vlc_thread_t; typedef struct
{
HANDLE handle;
void *(*entry) (void *);
void *data;
} *vlc_thread_t;
typedef HANDLE vlc_mutex_t; typedef HANDLE vlc_mutex_t;
typedef HANDLE vlc_cond_t; typedef HANDLE vlc_cond_t;
typedef DWORD vlc_threadvar_t; typedef DWORD vlc_threadvar_t;
...@@ -170,6 +176,9 @@ VLC_EXPORT( int, __vlc_thread_create, ( vlc_object_t *, const char *, int, cons ...@@ -170,6 +176,9 @@ VLC_EXPORT( int, __vlc_thread_create, ( vlc_object_t *, const char *, int, cons
VLC_EXPORT( int, __vlc_thread_set_priority, ( vlc_object_t *, const char *, int, int ) ); VLC_EXPORT( int, __vlc_thread_set_priority, ( vlc_object_t *, const char *, int, int ) );
VLC_EXPORT( void, __vlc_thread_join, ( vlc_object_t *, const char *, int ) ); VLC_EXPORT( void, __vlc_thread_join, ( vlc_object_t *, const char *, int ) );
VLC_EXPORT( int, vlc_clone, (vlc_thread_t *, void * (*) (void *), void *, int) );
VLC_EXPORT( int, vlc_join, (vlc_thread_t, void **) );
#define vlc_thread_ready vlc_object_signal #define vlc_thread_ready vlc_object_signal
/***************************************************************************** /*****************************************************************************
......
...@@ -416,6 +416,7 @@ vlc_b64_decode_binary_to_buffer ...@@ -416,6 +416,7 @@ vlc_b64_decode_binary_to_buffer
vlc_b64_encode vlc_b64_encode
vlc_b64_encode_binary vlc_b64_encode_binary
VLC_Changeset VLC_Changeset
vlc_clone
VLC_CompileBy VLC_CompileBy
VLC_CompileDomain VLC_CompileDomain
VLC_CompileHost VLC_CompileHost
...@@ -447,6 +448,7 @@ vlc_iconv_close ...@@ -447,6 +448,7 @@ vlc_iconv_close
vlc_iconv_open vlc_iconv_open
vlc_inet_ntop vlc_inet_ntop
vlc_inet_pton vlc_inet_pton
vlc_join
__vlc_list_children __vlc_list_children
__vlc_list_find __vlc_list_find
vlc_list_release vlc_list_release
......
/***************************************************************************** /*****************************************************************************
* threads.c : threads implementation for the VideoLAN client * threads.c : threads implementation for the VideoLAN client
***************************************************************************** *****************************************************************************
* Copyright (C) 1999-2007 the VideoLAN team * Copyright (C) 1999-2008 the VideoLAN team
* $Id$ * $Id$
* *
* Authors: Jean-Marc Dressler <polux@via.ecp.fr> * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
...@@ -438,59 +438,29 @@ void vlc_threadvar_delete (vlc_threadvar_t *p_tls) ...@@ -438,59 +438,29 @@ void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
#endif #endif
} }
struct vlc_thread_boot
{
void * (*entry) (vlc_object_t *);
vlc_object_t *object;
};
#if defined (LIBVLC_USE_PTHREAD) #if defined (LIBVLC_USE_PTHREAD)
# define THREAD_RTYPE void *
# define THREAD_RVAL NULL
#elif defined (WIN32) #elif defined (WIN32)
# define THREAD_RTYPE __stdcall unsigned static unsigned __stdcall vlc_entry (void *data)
# define THREAD_RVAL 0
#endif
static THREAD_RTYPE thread_entry (void *data)
{ {
vlc_object_t *obj = ((struct vlc_thread_boot *)data)->object; vlc_thread_t self = data;
void *(*func) (vlc_object_t *) = ((struct vlc_thread_boot *)data)->entry; self->data = self->entry (self->data);
return 0;
free (data);
#ifndef NDEBUG
vlc_threadvar_set (&thread_object_key, obj);
#endif
msg_Dbg (obj, "thread started");
func (obj);
msg_Dbg (obj, "thread ended");
return THREAD_RVAL;
} }
#endif
/***************************************************************************** /**
* vlc_thread_create: create a thread, inner version * Creates and starts new thread.
***************************************************************************** *
* Note that i_priority is only taken into account on platforms supporting * @param p_handle [OUT] pointer to write the handle of the created thread to
* userland real-time priority threads. * @param entry entry point for the thread
*****************************************************************************/ * @param data data parameter given to the entry point
int __vlc_thread_create( vlc_object_t *p_this, const char * psz_file, int i_line, * @param priority thread priority value
const char *psz_name, void * ( *func ) ( vlc_object_t * ), * @return 0 on success, a standard error code on error.
int i_priority, bool b_wait ) */
int vlc_clone (vlc_thread_t *p_handle, void * (*entry) (void *), void *data,
int priority)
{ {
int i_ret; int ret;
vlc_object_internals_t *p_priv = vlc_internals( p_this );
struct vlc_thread_boot *boot = malloc (sizeof (*boot));
if (boot == NULL)
return errno;
boot->entry = func;
boot->object = p_this;
vlc_object_lock( p_this );
/* Make sure we don't re-create a thread if the object has already one */
assert( !p_priv->b_thread );
#if defined( LIBVLC_USE_PTHREAD ) #if defined( LIBVLC_USE_PTHREAD )
pthread_attr_t attr; pthread_attr_t attr;
...@@ -506,7 +476,9 @@ int __vlc_thread_create( vlc_object_t *p_this, const char * psz_file, int i_line ...@@ -506,7 +476,9 @@ int __vlc_thread_create( vlc_object_t *p_this, const char * psz_file, int i_line
* where it fails to handle EINTR (bug reports welcome). Some underlying * where it fails to handle EINTR (bug reports welcome). Some underlying
* libraries might also not handle EINTR properly. * libraries might also not handle EINTR properly.
*/ */
sigset_t set, oldset; sigset_t oldset;
{
sigset_t set;
sigemptyset (&set); sigemptyset (&set);
sigdelset (&set, SIGHUP); sigdelset (&set, SIGHUP);
sigaddset (&set, SIGINT); sigaddset (&set, SIGINT);
...@@ -515,27 +487,21 @@ int __vlc_thread_create( vlc_object_t *p_this, const char * psz_file, int i_line ...@@ -515,27 +487,21 @@ int __vlc_thread_create( vlc_object_t *p_this, const char * psz_file, int i_line
sigaddset (&set, SIGPIPE); /* We don't want this one, really! */ sigaddset (&set, SIGPIPE); /* We don't want this one, really! */
pthread_sigmask (SIG_BLOCK, &set, &oldset); pthread_sigmask (SIG_BLOCK, &set, &oldset);
}
#ifndef __APPLE__
if( config_GetInt( p_this, "rt-priority" ) > 0 )
#endif
{ {
struct sched_param p = { .sched_priority = i_priority, }; struct sched_param sp = { .sched_priority = priority, };
int policy; int policy;
/* Hack to avoid error msg */ if (sp.sched_priority <= 0)
if( config_GetType( p_this, "rt-offset" ) ) sp.sched_priority += sched_get_priority_max (policy = SCHED_OTHER);
p.sched_priority += config_GetInt( p_this, "rt-offset" );
if( p.sched_priority <= 0 )
p.sched_priority += sched_get_priority_max (policy = SCHED_OTHER);
else else
p.sched_priority += sched_get_priority_min (policy = SCHED_RR); sp.sched_priority += sched_get_priority_min (policy = SCHED_RR);
pthread_attr_setschedpolicy (&attr, policy); pthread_attr_setschedpolicy (&attr, policy);
pthread_attr_setschedparam (&attr, &p); pthread_attr_setschedparam (&attr, &sp);
} }
i_ret = pthread_create( &p_priv->thread_id, &attr, thread_entry, boot ); ret = pthread_create (p_handle, &attr, entry, data);
pthread_sigmask (SIG_SETMASK, &oldset, NULL); pthread_sigmask (SIG_SETMASK, &oldset, NULL);
pthread_attr_destroy (&attr); pthread_attr_destroy (&attr);
...@@ -544,34 +510,144 @@ int __vlc_thread_create( vlc_object_t *p_this, const char * psz_file, int i_line ...@@ -544,34 +510,144 @@ int __vlc_thread_create( vlc_object_t *p_this, const char * psz_file, int i_line
* function instead of CreateThread, otherwise you'll end up with * function instead of CreateThread, otherwise you'll end up with
* memory leaks and the signal functions not working (see Microsoft * memory leaks and the signal functions not working (see Microsoft
* Knowledge Base, article 104641) */ * Knowledge Base, article 104641) */
HANDLE hThread;
vlc_thread_t th = malloc (sizeof (*p_handle));
if (th == NULL)
return ENOMEM;
th->data = data;
th->entry = entry;
#if defined( UNDER_CE ) #if defined( UNDER_CE )
HANDLE hThread = CreateThread( NULL, 0, thread_entry, hThread = CreateThread (NULL, 0, vlc_entry, th, CREATE_SUSPENDED, NULL);
(LPVOID)boot, CREATE_SUSPENDED, NULL );
#else #else
HANDLE hThread = (HANDLE)(uintptr_t) hThread = (HANDLE)(uintptr_t)
_beginthreadex( NULL, 0, thread_entry, boot, CREATE_SUSPENDED, NULL ); _beginthreadex (NULL, 0, vlc_entry, th, CREATE_SUSPENDED, NULL);
#endif #endif
if( hThread )
if (hThread)
{ {
p_priv->thread_id = hThread;
ResumeThread (hThread); ResumeThread (hThread);
i_ret = 0; th->handle = hThread;
if( i_priority && !SetThreadPriority (hThread, i_priority) ) if (priority)
{ SetThreadPriority (hThread, priority);
msg_Warn( p_this, "couldn't set a faster priority" ); ret = 0;
i_priority = 0;
}
} }
else else
i_ret = errno; {
ret = errno;
free (th);
th = NULL;
}
*p_handle = th;
#elif defined( HAVE_KERNEL_SCHEDULER_H ) #elif defined( HAVE_KERNEL_SCHEDULER_H )
p_priv->thread_id = spawn_thread( (thread_func)thread_entry, psz_name, *p_handle = spawn_thread( entry, psz_name, priority, data );
i_priority, p_data ); ret = resume_thread( *p_handle );
i_ret = resume_thread( p_priv->thread_id );
#endif #endif
return ret;
}
/**
* Waits for a thread to complete (if needed), and destroys it.
* @param handle thread handle
* @param p_result [OUT] pointer to write the thread return value or NULL
* @return 0 on success, a standard error code otherwise.
*/
int vlc_join (vlc_thread_t handle, void **result)
{
#if defined( LIBVLC_USE_PTHREAD )
return pthread_join (handle, result);
#elif defined( UNDER_CE ) || defined( WIN32 )
HANDLE hThread;
/*
** object will close its thread handle when destroyed, duplicate it here
** to be on the safe side
*/
if (!DuplicateHandle (GetCurrentProcess (), handle->handle,
GetCurrentProcess(), &hThread, 0, FALSE,
DUPLICATE_SAME_ACCESS))
return GetLastError (); /* FIXME: errno */
WaitForSingleObject (hThread, INFINITE);
CloseHandle (hThread);
if (result)
*result = handle->data;
free (handle);
return 0;
#elif defined( HAVE_KERNEL_SCHEDULER_H )
int32_t exit_value;
ret = (B_OK == wait_for_thread( p_priv->thread_id, &exit_value ));
if( !ret && result )
*result = (void *)exit_value;
return ret;
#endif
}
struct vlc_thread_boot
{
void * (*entry) (vlc_object_t *);
vlc_object_t *object;
};
static void *thread_entry (void *data)
{
vlc_object_t *obj = ((struct vlc_thread_boot *)data)->object;
void *(*func) (vlc_object_t *) = ((struct vlc_thread_boot *)data)->entry;
free (data);
#ifndef NDEBUG
vlc_threadvar_set (&thread_object_key, obj);
#endif
msg_Dbg (obj, "thread started");
func (obj);
msg_Dbg (obj, "thread ended");
return NULL;
}
/*****************************************************************************
* vlc_thread_create: create a thread, inner version
*****************************************************************************
* Note that i_priority is only taken into account on platforms supporting
* userland real-time priority threads.
*****************************************************************************/
int __vlc_thread_create( vlc_object_t *p_this, const char * psz_file, int i_line,
const char *psz_name, void * ( *func ) ( vlc_object_t * ),
int i_priority, bool b_wait )
{
int i_ret;
vlc_object_internals_t *p_priv = vlc_internals( p_this );
struct vlc_thread_boot *boot = malloc (sizeof (*boot));
if (boot == NULL)
return errno;
boot->entry = func;
boot->object = p_this;
vlc_object_lock( p_this );
/* Make sure we don't re-create a thread if the object has already one */
assert( !p_priv->b_thread );
#if defined( LIBVLC_USE_PTHREAD )
#ifndef __APPLE__
if( config_GetInt( p_this, "rt-priority" ) > 0 )
#endif
{
/* Hack to avoid error msg */
if( config_GetType( p_this, "rt-offset" ) )
i_priority += config_GetInt( p_this, "rt-offset" );
}
#endif
i_ret = vlc_clone( &p_priv->thread_id, thread_entry, boot, i_priority );
if( i_ret == 0 ) if( i_ret == 0 )
{ {
if( b_wait ) if( b_wait )
...@@ -645,7 +721,7 @@ int __vlc_thread_set_priority( vlc_object_t *p_this, const char * psz_file, ...@@ -645,7 +721,7 @@ int __vlc_thread_set_priority( vlc_object_t *p_this, const char * psz_file,
#elif defined( WIN32 ) || defined( UNDER_CE ) #elif defined( WIN32 ) || defined( UNDER_CE )
VLC_UNUSED( psz_file); VLC_UNUSED( i_line ); VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
if( !SetThreadPriority(p_priv->thread_id, i_priority) ) if( !SetThreadPriority(p_priv->thread_id->handle, i_priority) )
{ {
msg_Warn( p_this, "couldn't set a faster priority" ); msg_Warn( p_this, "couldn't set a faster priority" );
return 1; return 1;
...@@ -673,22 +749,15 @@ void __vlc_thread_join( vlc_object_t *p_this, const char * psz_file, int i_line ...@@ -673,22 +749,15 @@ void __vlc_thread_join( vlc_object_t *p_this, const char * psz_file, int i_line
i_ret = pthread_detach (p_priv->thread_id); i_ret = pthread_detach (p_priv->thread_id);
} }
else else
i_ret = pthread_join (p_priv->thread_id, NULL); i_ret = vlc_join (p_priv->thread_id, NULL);
#elif defined( UNDER_CE ) || defined( WIN32 ) #elif defined( UNDER_CE ) || defined( WIN32 )
HMODULE hmodule; HANDLE hThread;
BOOL (WINAPI *OurGetThreadTimes)( HANDLE, FILETIME*, FILETIME*,
FILETIME*, FILETIME* );
FILETIME create_ft, exit_ft, kernel_ft, user_ft; FILETIME create_ft, exit_ft, kernel_ft, user_ft;
int64_t real_time, kernel_time, user_time; int64_t real_time, kernel_time, user_time;
HANDLE hThread;
/*
** object will close its thread handle when destroyed, duplicate it here
** to be on the safe side
*/
if( ! DuplicateHandle(GetCurrentProcess(), if( ! DuplicateHandle(GetCurrentProcess(),
p_priv->thread_id, p_priv->thread_id->handle,
GetCurrentProcess(), GetCurrentProcess(),
&hThread, &hThread,
0, 0,
...@@ -700,20 +769,9 @@ void __vlc_thread_join( vlc_object_t *p_this, const char * psz_file, int i_line ...@@ -700,20 +769,9 @@ void __vlc_thread_join( vlc_object_t *p_this, const char * psz_file, int i_line
goto error; goto error;
} }
WaitForSingleObject( hThread, INFINITE ); vlc_join( p_priv->thread_id, NULL );
#if defined( UNDER_CE ) if( GetThreadTimes( hThread, &create_ft, &exit_ft, &kernel_ft, &user_ft ) )
hmodule = GetModuleHandle( _T("COREDLL") );
#else
hmodule = GetModuleHandle( _T("KERNEL32") );
#endif
OurGetThreadTimes = (BOOL (WINAPI*)( HANDLE, FILETIME*, FILETIME*,
FILETIME*, FILETIME* ))
GetProcAddress( hmodule, _T("GetThreadTimes") );
if( OurGetThreadTimes &&
OurGetThreadTimes( hThread,
&create_ft, &exit_ft, &kernel_ft, &user_ft ) )
{ {
real_time = real_time =
((((int64_t)exit_ft.dwHighDateTime)<<32)| exit_ft.dwLowDateTime) - ((((int64_t)exit_ft.dwHighDateTime)<<32)| exit_ft.dwLowDateTime) -
...@@ -740,9 +798,8 @@ void __vlc_thread_join( vlc_object_t *p_this, const char * psz_file, int i_line ...@@ -740,9 +798,8 @@ void __vlc_thread_join( vlc_object_t *p_this, const char * psz_file, int i_line
CloseHandle( hThread ); CloseHandle( hThread );
error: error:
#elif defined( HAVE_KERNEL_SCHEDULER_H ) #else
int32_t exit_value; i_ret = vlc_join( p_priv->thread_id, NULL );
i_ret = (B_OK == wait_for_thread( p_priv->thread_id, &exit_value ));
#endif #endif
......
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