📄 unixthreads.cpp
字号:
/* ***** BEGIN LICENSE BLOCK *****
* Version: RCSL 1.0/RPSL 1.0
*
* Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved.
*
* The contents of this file, and the files included with this file, are
* subject to the current version of the RealNetworks Public Source License
* Version 1.0 (the "RPSL") available at
* http://www.helixcommunity.org/content/rpsl unless you have licensed
* the file under the RealNetworks Community Source License Version 1.0
* (the "RCSL") available at http://www.helixcommunity.org/content/rcsl,
* in which case the RCSL will apply. You may also obtain the license terms
* directly from RealNetworks. You may not use this file except in
* compliance with the RPSL or, if you have a valid RCSL with RealNetworks
* applicable to this file, the RCSL. Please see the applicable RPSL or
* RCSL for the rights, obligations and limitations governing use of the
* contents of the file.
*
* This file is part of the Helix DNA Technology. RealNetworks is the
* developer of the Original Code and owns the copyrights in the portions
* it created.
*
* This file, and the files included with this file, is distributed and made
* available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
*
* Technology Compatibility Kit Test Suite(s) Location:
* http://www.helixcommunity.org/content/tck
*
* Contributor(s):
*
* ***** END LICENSE BLOCK ***** */
//This is used to turn off threads in libc5 builds.
#ifdef _UNIX_THREADS_SUPPORTED
#include "hxtypes.h" //for UINT32
#include "hxresult.h" //for HX_RESULT
#include <errno.h>
#include "UnixThreads.h"
#include <signal.h> //for SIGKILL
#include "hxassert.h" //for HX_ASSERT
#include "microsleep.h"
#include "hxmsgs.h" //for HXMSG_ASYNC_TIMER message.
#include "hxtick.h" //for GetTickCount()
//=================================================================
// In this section here include the OS specific headerfiles for
// each implementation.
//=================================================================
#if defined( _LINUX ) || defined(_HPUX) || defined(_MAC_UNIX)
#include <pthread.h>
#include <semaphore.h>
#include "pthreadthreads.h"
#endif
#if defined( _SOLARIS )
#include <synch.h>
#include <thread.h>
#include "solaristhreads.h"
#endif
#if defined( _IRIX )
#endif
#if defined( _AIX )
#endif
//=======================================================================
//
// HXUnixThread
// ------------------
//
//=======================================================================
HXUnixThread::HXUnixThread()
: m_threadID(0),
m_messageQue(),
m_pCond(NULL),
m_pCondLock(NULL)
{
//Create the condition with its associated mutex.
//Do NOT delete the mutex that the cond makes for us....
HXUnixCondition::MakeCondition(m_pCond, m_pCondLock );
HX_ASSERT( m_pCondLock && m_pCond);
}
HXUnixThread::~HXUnixThread()
{
if( m_threadID != 0 )
{
//XXXgfw Had to get rid of this. Some threads are destroyed on their own thread.
//HX_ASSERT( "Thread has not been joined! Memory/mutex leaks????\n" == NULL );
//We can try to cancel here but it won't work!
//The thread may not even honor the cancel request. If it does and has not
//set up cancelation/clean-up routine you better hope that it doesn't have
//any mutexes locked or resources open!!! Just don't do it.
}
//Clean up message que.
while( !m_messageQue.IsEmpty() )
{
HXThreadMessage* pTmp = (HXThreadMessage *)(m_messageQue.RemoveHead());
HX_DELETE( pTmp );
}
HX_DELETE( m_pCond );
m_pCondLock = NULL;
}
HX_RESULT HXUnixThread::MakeThread(HXThread*& pThread )
{
#if defined( _LINUX ) || defined (_HPUX) || defined(_MAC_UNIX)
pThread = new HXPthreadThread();
#elif defined( _SOLARIS )
pThread = new HXSolarisThread();
#else
HX_ASSERT( "No unix thread for this platform" == NULL );
#endif
if(pThread == NULL)
{
HX_ASSERT( 0 );
return HXR_OUTOFMEMORY;
}
return HXR_OK;
}
//ulCreationFlags is ignored. We can't suspend pthreads and that is the only
//flag possible right now.
HX_RESULT HXUnixThread::CreateThread( void*(pfExecFunc(void*)), void* pArg, ULONG32 ulCreationFlags)
{
HX_RESULT retVal = HXR_OK;
HX_ASSERT( m_threadID==0 );
if( m_threadID != 0)
{
retVal = HXR_UNEXPECTED;
}
else
{
if( _thread_create( m_threadID, pfExecFunc, pArg ) != HXR_OK )
{
retVal = HXR_FAIL;
m_threadID = 0;
}
}
return retVal;
}
HX_RESULT HXUnixThread::Exit(UINT32 unExitCode)
{
//Make sure that only the thread that was created it calling the exit
//routine. If the parent thread wants to kill it maybe we should have
//a Kill method or something.
if( 0 == m_threadID )
{
//Thread has already gone. Just return.
return HXR_UNEXPECTED;
}
if( m_threadID != GetCurrentThreadID() )
{
//Ok, because of the way winthrd.cpp does it, this call also
//acts as a 'pthread_join' when the calling thread isn't the
//m_threadID.
//Also, it looks like this method isn't set up to look at the
//return value of the thread. We could return HXR_FAIL is it
//is anything except 0 but for now just throw it away.
JoinThread();
}
else
{
_thread_exit( unExitCode );
}
return HXR_OK;
}
HX_RESULT HXUnixThread::SetPriority( UINT32 ulPriority)
{
//XXXGFW not supported yet.
// struct sched_param stThreadParams;
// int nPolicy = 0;
// int nRet = -1;
// HX_ASSERT( m_threadID );
// if( m_threadID != 0)
// {
// nPolicy = SCHED_OTHER;
// stThreadParams.sched_priority = (int)ulPriority;
// nRet = pthread_setschedparam( m_threadID, nPolicy, &stThreadParams );
// }
// return nRet==0 ? HXR_OK : HXR_FAIL;
return HXR_OK;
}
//XXXgfw not tested yet!!!!!!! ...but should work. :)
HX_RESULT HXUnixThread::GetPriority( UINT32& ulPriority)
{
//XXXGFW not supported yet.....
// struct sched_param stThreadParams;
// int nPolicy = 0;
// int nRet = -1;
// HX_ASSERT( m_threadID );
// if( m_threadID != 0)
// {
// nRet = pthread_getschedparam( m_threadID, &nPolicy, &stThreadParams );
// ulPriority = stThreadParams.sched_priority;
// }
// return nRet==0 ? HXR_OK : HXR_FAIL;
return HXR_OK;
}
HX_RESULT HXUnixThread::YieldTimeSlice()
{
microsleep(1);
return HXR_OK;
}
HX_RESULT HXUnixThread::GetThreadId(UINT32& ulThreadId)
{
ulThreadId = m_threadID;
return HXR_OK;
}
ULONG32 HXUnixThread::GetCurrentThreadID()
{
return _thread_self();
}
//Cancels the thread that was created.
HX_RESULT HXUnixThread::CancelThread()
{
HX_ASSERT( "This isn't portable. Don't use it" == NULL );
if( 0 == m_threadID )
{
return HXR_UNEXPECTED;
}
_thread_cancel(m_threadID);
return HXR_OK;
}
//Must wait for our child thread to exit and return the ret code.
ULONG32 HXUnixThread::JoinThread()
{
ULONG32 ulRetVal = 0;
if( 0 == m_threadID || GetCurrentThreadID() == m_threadID )
{
return HXR_UNEXPECTED;
}
ulRetVal = _thread_join( m_threadID );
//Thread has exited and we are done.
m_threadID = 0;
return ulRetVal;
}
HX_RESULT HXUnixThread::Suspend()
{
HX_ASSERT( "HXUnixThread::Suspend is not implemented yet." == NULL );
return HXR_FAIL;
}
HX_RESULT HXUnixThread::Resume()
{
HX_ASSERT( "HXUnixThread::Resume is not implemented yet." == NULL );
return HXR_FAIL;
}
HX_RESULT HXUnixThread::PostMessage(HXThreadMessage* pMsg, void* pWindowHandle)
{
HX_RESULT retVal = HXR_OK;
//Assert that we don't use pWindowHandle.
HX_ASSERT( pWindowHandle == NULL );
//To mimic the windows PostMessage we must COPY the pMsg and put
//it on our que. pMsg is going to go out of scope most likely
if( NULL != pMsg )
{
HXThreadMessage *pMsgTmp = new HXThreadMessage(pMsg);
if( pMsgTmp == NULL )
{
retVal = HXR_OUTOFMEMORY;
}
else
{
//Lock the mutex protecting the message que.
m_pCondLock->Lock();
m_messageQue.AddTail((void*)pMsgTmp);
//If we were empty the the GetMessage thread could have been waiting
//on us to post. Signal it.
m_pCond->Signal();
m_pCondLock->Unlock();
}
}
return retVal;
}
HX_RESULT HXUnixThread::GetMessage( HXThreadMessage* pMsg,
UINT32 ulMsgFilterMin,
UINT32 ulMsgFilterMax)
{
HX_RESULT retVal = HXR_OK;
//assert that ulMsgFilterMax/Min is zero as we don't support it yet.
HX_ASSERT( ulMsgFilterMax == 0 && ulMsgFilterMin == 0 );
HX_ASSERT( pMsg );
//We must pop the next message, COPY it into pMsg and delete our copy.
if( pMsg != NULL )
{
//Get access
m_pCondLock->Lock();
//If the que is empty we need to block and wait.
while( m_messageQue.IsEmpty() )
{
m_pCond->Wait();
}
if( !m_messageQue.IsEmpty())
{
HXThreadMessage* pMsgTmp = (HXThreadMessage*)m_messageQue.RemoveHead();
pMsg->m_ulMessage = pMsgTmp->m_ulMessage;
pMsg->m_pParam1 = pMsgTmp->m_pParam1;
pMsg->m_pParam2 = pMsgTmp->m_pParam2;
pMsg->m_pPlatformSpecificData = pMsgTmp->m_pPlatformSpecificData;
//free it.
HX_DELETE( pMsgTmp );
}
else
{
HX_ASSERT( "que panic" == NULL );
}
m_pCondLock->Unlock();
}
return retVal;
}
HX_RESULT HXUnixThread::PeekMessageMatching( HXThreadMessage* pMsg,
HXThreadMessage* pMatch,
BOOL bRemoveMessage )
{
HX_RESULT retVal = HXR_OK;
HX_ASSERT( pMsg );
HX_ASSERT( pMatch );
if( pMsg != NULL && pMatch!=NULL )
{
//Protect the que.
m_pCondLock->Lock();
if( !m_messageQue.IsEmpty() )
{
HXThreadMessage* pMsgTmp = NULL;
//Loop throught the messages and find a matching
//one.
BOOL bSkipMessage = (pMatch->m_ulMessage==0);
BOOL bSkipParam1 = (pMatch->m_pParam1==NULL);
BOOL bSkipParam2 = (pMatch->m_pParam2==NULL);
BOOL bSkipPlatform = (pMatch->m_pPlatformSpecificData==NULL);
CHXSimpleList::Iterator i;
for( i=m_messageQue.Begin(); i!=m_messageQue.End(); ++i)
{
pMsgTmp = (HXThreadMessage*)(*i);
//Does it match?
if( bSkipMessage || pMatch->m_ulMessage==pMsgTmp->m_ulMessage )
if( bSkipParam1 || pMatch->m_pParam1==pMsgTmp->m_pParam1 )
if( bSkipParam2 || pMatch->m_pParam2==pMsgTmp->m_pParam2 )
if( bSkipPlatform || pMatch->m_pPlatformSpecificData==pMsgTmp->m_pPlatformSpecificData )
break;
}
//Did we find a match?
if( i != m_messageQue.End())
{
//We found one!
pMsg->m_ulMessage = pMsgTmp->m_ulMessage;
pMsg->m_pParam1 = pMsgTmp->m_pParam1;
pMsg->m_pParam2 = pMsgTmp->m_pParam2;
pMsg->m_pPlatformSpecificData = pMsgTmp->m_pPlatformSpecificData;
//Only free it if we removed it from the queue.
if( bRemoveMessage )
{
//XXXgfw That has to be a better way than this. We
//have the iterator up above. How do you delete with
//one.
LISTPOSITION listpos = m_messageQue.Find(pMsgTmp);
HX_ASSERT( listpos );
if(listpos)
{
m_messageQue.RemoveAt(listpos);
}
HX_DELETE( pMsgTmp );
}
}
else
{
retVal=HXR_FAIL;
}
}
else
{
//There was no message to get
retVal=HXR_FAIL;
}
m_pCondLock->Unlock();
}
return retVal;
}
HX_RESULT HXUnixThread::PeekMessage( HXThreadMessage* pMsg,
UINT32 ulMsgFilterMin,
UINT32 ulMsgFilterMax,
BOOL bRemoveMessage
)
{
HX_RESULT retVal = HXR_OK;
//assert that ulMsgFilterMax/Min is zero as we don't support it yet.
HX_ASSERT( ulMsgFilterMax == 0 && ulMsgFilterMin == 0 );
HX_ASSERT( pMsg );
//We must pop the next message, COPY it into pMsg and delete our copy.
if( pMsg != NULL )
{
//Protect the que.
m_pCondLock->Lock();
if( !m_messageQue.IsEmpty() )
{
HXThreadMessage* pMsgTmp = NULL;
//Do we romove the message or peek at it?
if( bRemoveMessage )
{
pMsgTmp = (HXThreadMessage*)m_messageQue.RemoveHead();
}
else
{
pMsgTmp = (HXThreadMessage*)m_messageQue.GetHead();
}
if( pMsgTmp != NULL )
{
pMsg->m_ulMessage = pMsgTmp->m_ulMessage;
pMsg->m_pParam1 = pMsgTmp->m_pParam1;
pMsg->m_pParam2 = pMsgTmp->m_pParam2;
pMsg->m_pPlatformSpecificData = pMsgTmp->m_pPlatformSpecificData;
//Only free it if we removed it from the queue.
if( bRemoveMessage )
HX_DELETE( pMsgTmp );
}
else
{
HX_ASSERT( "que panic" == NULL );
}
}
else
{
//There was no message to get
retVal=HXR_FAIL;
}
m_pCondLock->Unlock();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -