Commit 09392400 authored by Jean-Paul Saman's avatar Jean-Paul Saman

- Removed some dead wood

- Implemented C_Semaphore::TryWait()
- Fixed the 9 seconds delay for stopping a stream. The C_SyncFifo()::Pop() and HandlePacket() now make use of C_Semaphore::TryWait() and never block. If the semaphore is already held then the methods Pop() and HandlePacket() will sleep for a predefined time and try again. Should this sleep time be configurable?
- Fixed a bug in C_TrickPlay::StopWork() using Interrupt() instead of DelayedInterruption().
parent fc0f490a
......@@ -3,6 +3,10 @@
#===================#
0.6.0
* VLS supports TrickPlay streaming.
* src/modules/normaltrickplay: The default normal VLS functionality (start/stop/pause).
When implementing a custom trickplay policy the normaltrickplay module is a good starting
point.
* src/modules/localinput: added DVD title and chapter selection
* src/core/parsers.cpp: fix the multicast addresses range in the command
line parsing
......
/*******************************************************************************
* thread.cpp: thread management
*-------------------------------------------------------------------------------
* (c)1999-2001 VideoLAN
* $Id: thread.cpp,v 1.7 2002/07/12 18:09:36 massiot Exp $
*
* Authors: Benoit Steiner <benny@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*------------------------------------------------------------------------------*
*
*******************************************************************************/
//------------------------------------------------------------------------------
// Preamble
//------------------------------------------------------------------------------
#include "defs.h"
#ifdef PTHREAD_COND_T_IN_PTHREAD_H
#include <pthread.h>
#include <semaphore.h>
#include <errno.h>
#elif defined WIN32
#include <windows.h>
#include <process.h>
#endif
#include <stdio.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "common.h"
#include "string.h"
#include "system.h"
#include "exception.h"
#include "list.h"
#include "thread.h"
#include "debug.h"
#include "list.cpp"
#define INT_TEST_COUNT 15 // Test count before calling interruption
// process
#define INT_TEST_DELAY 1000000 // Delay in microseconds between each test
// before calling interruption process
//******************************************************************************
// Class C_ThreadKiller
//******************************************************************************
//
//******************************************************************************
/*
//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
class C_ThreadKiller
{
public:
// The thread is created detachable during the application startup. It will
// live until the end of the program
C_ThreadKiller();
~C_ThreadKiller();
void Kill(C_Thread* pThread);
protected:
// Main loop
static void* MainLoop(void* pThread);
private:
bool m_bStop;
#ifdef PTHREAD_COND_T_IN_PTHREAD_H
pthread_t tId;
#elif defined WIN32
HANDLE hThread;
WORD dwId;
#endif
E_Exception* m_pException;
C_Thread* m_pThread;
C_Mutex m_cLock;
C_Semaphore m_cJobAvailable;
C_Semaphore m_cJobDone;
};
//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
C_ThreadKiller::C_ThreadKiller() : m_cJobAvailable(0), m_cJobDone(0)
{
m_bStop = false;
m_pException = NULL;
m_pThread = NULL;
pthread_attr_t sAttr;
pthread_attr_init(&sAttr);
pthread_attr_setdetachstate(&sAttr, PTHREAD_CREATE_DETACHED);
int iRc = pthread_create(&tId, NULL, C_ThreadKiller::MainLoop, this);
pthread_attr_destroy(&sAttr);
ASSERT(!iRc);
}
//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
C_ThreadKiller::~C_ThreadKiller()
{
m_bStop = true;
// End of the main loop. No need to lock because all threads must be
// destroyed at this point of the execution.
m_pThread = NULL;
m_cJobAvailable.Post();
// Cleaning
void* pRc = NULL;
int iRc = pthread_join(tId, &pRc);
ASSERT(!iRc);
ASSERT(!pRc);
}
//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
void* C_ThreadKiller::MainLoop(void* pThread)
{
C_ThreadKiller* pThis = (C_ThreadKiller*)pThread;
while(!pThis->m_bStop)
{
// Wait for something to do
pThis->m_cJobAvailable.Wait();
// Do the job
if(pThis->m_pThread)
{
// Handle the request
try
{
pThis->m_pThread->Stop();
}
catch(E_Exception e)
{
pThis->m_pException = new E_Exception(e);
}
pThis->m_cJobDone.Post();
}
else
ASSERT(pThis->m_bStop);
}
return NULL;
}
//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
void C_ThreadKiller::Kill(C_Thread* pThread)
{
ASSERT(pThread);
// Only one thread can be killed at a time for now
m_cLock.Lock();
// Process the request
m_pThread = pThread;
m_cJobAvailable.Post();
m_cJobDone.Wait();
// Get the exception if there was one
if(m_pException)
{
E_Exception e(*m_pException);
delete m_pException;
m_pException = NULL;
m_cLock.UnLock();
throw e;
}
else
m_cLock.UnLock();
}
*/
//******************************************************************************
// Class C_Thread
//******************************************************************************
//
//******************************************************************************
//------------------------------------------------------------------------------
// Instanciation of the static list of running threads and of the thread killer
//------------------------------------------------------------------------------
C_List<C_Thread> C_Thread::s_cThreadList;
//C_ThreadKiller s_cThreadKiller;
//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
C_Thread::C_Thread(unsigned int iProperties)
{
m_iFlags = iProperties;
m_iStatus = THREAD_STATUS_NOTSTARTED;
#ifdef PTHREAD_COND_T_IN_PTHREAD_H
ZERO(tId);
#elif defined WIN32
ZERO(hThread);
ZERO(dwId);
#endif
}
//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
C_Thread::~C_Thread()
{
// Verify that the thread has been stopped before
ASSERT(s_cThreadList.Find(*this) < 0);
}
//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
void C_Thread::Create()
{
try
{
// Do some initialisations before starting the real job
InitWork();
// Now launch the thread
#ifdef PTHREAD_COND_T_IN_PTHREAD_H
// Set the thread attributes. It is important for the thread to be created
// joinable unless the instance of the class could be destroyed before the
// end of the execution of DoWork
pthread_attr_t sAttr;
pthread_attr_init(&sAttr);
pthread_attr_setdetachstate(&sAttr, PTHREAD_CREATE_JOINABLE);
int iRc = pthread_create(&tId, NULL, C_Thread::StartRoutine, this);
pthread_attr_destroy(&sAttr);
#elif defined _WIN32
unsigned long threadID;
hThread = (HANDLE)CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)C_Thread::StartRoutine, this, 0, &threadID);
int iRc = ( hThread ? 0 : 1 );
#endif
if(iRc)
{
throw E_Exception(GEN_ERR, "Unable to start thread: " + GetErrorMsg());
}
// Reference the thread in the list of running threads
s_cThreadList.PushEnd(this);
}
catch(E_Exception e)
{
throw E_Exception(GEN_ERR, "Unable to create thread", e);
}
}
//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
void C_Thread::Cancel()
{
try
{
// Ask for thread cancellation
#ifdef PTHREAD_COND_T_IN_PTHREAD_H
int iRc = pthread_cancel(tId);
#elif defined WIN32
int iRc = 0;
#endif
if(iRc)
throw E_Exception(iRc, "Thread cancellation failed: " + GetErrorMsg());
// Now Stop the cancelled thread
Stop();
}
catch(E_Exception e)
{
throw E_Exception(GEN_ERR, "Error when cancelling thread", e);
}
}
//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
void C_Thread::Stop()
{
try
{
// As all our threads are created joinable, we must ensure that the calling
// thread is a different thread to avoid a deadlock. So if a thread try to stop
/*******************************************************************************
* thread.cpp: thread management
*-------------------------------------------------------------------------------
* (c)1999-2003 VideoLAN
* $Id: thread.cpp,v 1.8 2003/07/02 15:05:28 jpsaman Exp $
*
* Authors: Benoit Steiner <benny@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*------------------------------------------------------------------------------*
*
*******************************************************************************/
//------------------------------------------------------------------------------
// Preamble
//------------------------------------------------------------------------------
#include "defs.h"
#ifdef PTHREAD_COND_T_IN_PTHREAD_H
#include <pthread.h>
#include <semaphore.h>
#include <errno.h>
#elif defined WIN32
#include <windows.h>
#include <process.h>
#endif
#include <stdio.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "common.h"
#include "string.h"
#include "system.h"
#include "exception.h"
#include "list.h"
#include "thread.h"
#include "debug.h"
#include "list.cpp"
#define INT_TEST_COUNT 15 // Test count before calling interruption
// process
#define INT_TEST_DELAY 1000000 // Delay in microseconds between each test
// before calling interruption process
//******************************************************************************
// Class C_Thread
//******************************************************************************
//
//******************************************************************************
//------------------------------------------------------------------------------
// Instanciation of the static list of running threads and of the thread killer
//------------------------------------------------------------------------------
C_List<C_Thread> C_Thread::s_cThreadList;
//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
C_Thread::C_Thread(unsigned int iProperties)
{
m_iFlags = iProperties;
m_iStatus = THREAD_STATUS_NOTSTARTED;
#ifdef PTHREAD_COND_T_IN_PTHREAD_H
ZERO(tId);
#elif defined WIN32
ZERO(hThread);
ZERO(dwId);
#endif
}
//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
C_Thread::~C_Thread()
{
// Verify that the thread has been stopped before
ASSERT(s_cThreadList.Find(*this) < 0);
}
//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
void C_Thread::Create()
{
try
{
// Do some initialisations before starting the real job
InitWork();
// Now launch the thread
#ifdef PTHREAD_COND_T_IN_PTHREAD_H
// Set the thread attributes. It is important for the thread to be created
// joinable unless the instance of the class could be destroyed before the
// end of the execution of DoWork
pthread_attr_t sAttr;
pthread_attr_init(&sAttr);
pthread_attr_setdetachstate(&sAttr, PTHREAD_CREATE_JOINABLE);
int iRc = pthread_create(&tId, NULL, C_Thread::StartRoutine, this);
pthread_attr_destroy(&sAttr);
#elif defined _WIN32
unsigned long threadID;
hThread = (HANDLE)CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)C_Thread::StartRoutine, this, 0, &threadID);
int iRc = ( hThread ? 0 : 1 );
#endif
if(iRc)
{
throw E_Exception(GEN_ERR, "Unable to start thread: " + GetErrorMsg());
}
// Reference the thread in the list of running threads
s_cThreadList.PushEnd(this);
}
catch(E_Exception e)
{
throw E_Exception(GEN_ERR, "Unable to create thread", e);
}
}
//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
void C_Thread::Cancel()
{
try
{
// Ask for thread cancellation
#ifdef PTHREAD_COND_T_IN_PTHREAD_H
int iRc = pthread_cancel(tId);
#elif defined WIN32
int iRc = 0;
#endif
if(iRc)
throw E_Exception(iRc, "Thread cancellation failed: " + GetErrorMsg());
// Now Stop the cancelled thread
Stop();
}
catch(E_Exception e)
{
throw E_Exception(GEN_ERR, "Error when cancelling thread", e);
}
}
//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
void C_Thread::Stop()
{
try
{
// As all our threads are created joinable, we must ensure that the calling
// thread is a different thread to avoid a deadlock. So if a thread try to stop
// itself, we ask another thread (the killer) to do the job.
#ifdef PTHREAD_COND_T_IN_PTHREAD_H
if(pthread_self() == tId)
#else
if (GetCurrentThread() == hThread)
#endif
{
ASSERT(false);
//s_cThreadKiller.Kill(this);
}
// Don't join if not started
// It avoids exception's generation because pthread_join returns an error
// when the thread was not started.
else if(m_iStatus != THREAD_STATUS_NOTSTARTED)
{
// Ask the user work routine to stop its normal processing
StopWork();
// Try to join the thread in order to clean the resources it used and
// to synchronise the calling thread with the destruction of this thread
// Seems that there is a bug here in the libpthread: when the thread
// is cancelled, pRc is set to any value so that there is no way to get
// the return value of the DoWork method
// Ok, let's try again to see if the pb still occurs
E_Exception* p_eError = NULL;
#ifdef PTHREAD_COND_T_IN_PTHREAD_H
int iRc = pthread_join(tId, (void**)&p_eError);
#elif defined _WIN32
int iRc = 0;
WaitForSingleObject (hThread, INFINITE);
#endif
if(iRc)
{
// Look at thre return code of the joined thread
ASSERT(p_eError);
E_Exception e(*p_eError);
delete p_eError;
throw e;
}
// Cleaning
CleanWork();
// Remove the thread from the list of running thread, but only if it has been
// successfully stopped. Do not delete it, this is the responsability of the
// user
int iThreadPos = s_cThreadList.Find(*this);
ASSERT(iThreadPos >= 0);
s_cThreadList.Remove(iThreadPos);
}
}
catch(E_Exception e)
{
throw E_Exception(GEN_ERR, "Unable to stop thread", e);
}
}
//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
void C_Thread::Interrupt()
{
#ifdef PTHREAD_COND_T_IN_PTHREAD_H
int iRc = pthread_cancel(tId);
#elif defined _WIN32
int iRc = 0;
#endif
if(iRc)
throw E_Exception(GEN_ERR, "Unable to interrupt thread: " + GetErrorMsg());
m_iStatus = THREAD_STATUS_INTERRUPTED;
}
//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
void C_Thread::DelayedInterruption()
{
if(m_iStatus == THREAD_STATUS_STARTED)
{
for(unsigned int ui = 0; (ui < INT_TEST_COUNT) &&
(m_iStatus == THREAD_STATUS_STARTED); ui++)
{
#ifdef WIN32
Sleep(INT_TEST_DELAY / 1000000);
#else
usleep(INT_TEST_DELAY);
#endif
}
}
if(m_iStatus == THREAD_STATUS_STARTED)
{
printf("Thread not stopped after %u s,"
" calling interruption process\n",
INT_TEST_COUNT * INT_TEST_DELAY / 1000000);
Interrupt();
}
}
//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
bool C_Thread::operator == (const C_Thread& cThread)
{
#ifdef PTHREAD_COND_T_IN_PTHREAD_H
return (this->tId == cThread.tId);
#elif defined WIN32
return (this->hThread == cThread.hThread);
#endif
};
//------------------------------------------------------------------------------
// Return the identifier of the calling thread
//-----------------------------------------------------------------------------
// The result can be NULL if the calling thread has not been created through
// a C_Thread::Create (eg: the main thread, launched with the process)
// TO DO: create an iterator on lists an use it, it would be really faster
//-----------------------------------------------------------------------------
C_Thread* C_Thread::Self()
{
C_Thread* pResult = NULL;
#ifdef PTHREAD_COND_T_IN_PTHREAD_H
pthread_t tId = pthread_self();
for(unsigned int i = 0; i < s_cThreadList.Size(); i++)
{
C_Thread& cCurrent = s_cThreadList[i];
if(cCurrent.tId == tId)
{
pResult = &cCurrent;
break;
}
}
#elif defined _WIN32
HANDLE hThread = GetCurrentThread();
for(unsigned int i = 0; i < s_cThreadList.Size(); i++)
{
C_Thread& cCurrent = s_cThreadList[i];
if(cCurrent.hThread == hThread)
{
pResult = &cCurrent;
break;
}
}
#endif
return pResult;
}
//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
bool C_Thread::IsRunning() const
{
return (m_iStatus == THREAD_STATUS_STARTED);
}
//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
// The purpose of this routine is only to allow the "extern C" pthread_create
// functions to call the DoWork() C++ method
//------------------------------------------------------------------------------
void* C_Thread::StartRoutine(void* pThread)
{
C_Thread* pThis = (C_Thread*)pThread;
E_Exception* p_eError = NULL;
pThis->m_iStatus = THREAD_STATUS_STARTED;
try
{
pThis->DoWork();
}
catch(E_Exception e)
{
p_eError = new E_Exception(e);
}
pThis->m_iStatus = THREAD_STATUS_STOPPED;
return p_eError;
}
//******************************************************************************
// Class C_Mutex
//******************************************************************************
//
//******************************************************************************
//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
C_Mutex::C_Mutex()
{
#ifdef PTHREAD_COND_T_IN_PTHREAD_H
int iRc = pthread_mutex_init(&m_sMutex, NULL);
ASSERT(!iRc); // The function shouldn't fail
USE(iRc);
#elif defined _WIN32
InitializeCriticalSection( &m_sMutex );
#endif
}
//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
C_Mutex::~C_Mutex()
{
#ifdef PTHREAD_COND_T_IN_PTHREAD_H
int iRc = pthread_mutex_destroy(&m_sMutex);
ASSERT(!iRc); // The mutex should not be locked when being destroyed
USE(iRc);
#elif defined _WIN32
DeleteCriticalSection( &m_sMutex );
#endif
}
//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------