📄 carbthrd.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 ***** */
#if defined(_CARBON) || defined(_MAC_UNIX)
#include "platform/mac/carbthrd.h"
#include "hxmsgs.h" //for HXMSG_ASYNC_TIMER message.
HXCarbonThread::HXCarbonThread()
: m_mpTaskID(NULL)
, m_mpQueueID(NULL)
, m_pQueueMutex(NULL)
, m_pQueuePostSemaphore(kInvalidID)
, m_mpInternalTerminationNotificationQueueID(NULL)
{
// xxxbobclark there's a kludgey main app thread wrapper thingie
// for handling networked threading. It's a case where an
// HXCarbonThread gets used without HXCarbonThread::CreateThread()
// being called... but it requires m_mpQueueID. So we create it in
// the ctor.
HXMutex::MakeMutex(m_pQueueMutex);
::MPCreateSemaphore(INT_MAX, 0, &m_pQueuePostSemaphore);
OSStatus osResult = MPCreateQueue(&m_mpQueueID);
}
HXCarbonThread::~HXCarbonThread()
{
this->Exit(0);
// xxxbobclark should I remove the queue here if it exists?
HX_ASSERT(m_mpQueueID == NULL);
HX_ASSERT(m_mpTaskID == NULL);
HX_ASSERT(m_pQueueMutex == NULL);
HX_ASSERT(m_pQueuePostSemaphore == kInvalidID);
}
HX_RESULT
HXCarbonThread::CreateThread( void* (pExecAddr(void*)), void* pArg, ULONG32 ulCreationFlags )
{
HX_ASSERT(m_mpTaskID == NULL);
HX_RESULT retval = HXR_OK;
MPTaskOptions options = 0;
if (ulCreationFlags & HX_CREATE_SUSPENDED)
{
options |= 1;
}
OSStatus osResult = MPCreateQueue(&m_mpInternalTerminationNotificationQueueID);
if (osResult == noErr)
{
osResult = MPCreateTask((TaskProc)pExecAddr, pArg, 0, m_mpInternalTerminationNotificationQueueID, NULL, NULL, options, &m_mpTaskID);
}
if (osResult != noErr) retval = HXR_FAIL;
return retval;
}
HX_RESULT
HXCarbonThread::Suspend(void)
{
HX_RESULT retval = HXR_NOTIMPL;
// xxxbobclark this is actually why interrupt-like callbacks are impossible
// to simulate correctly under OS X: there's no suspend or resume available
// in the MPTask API. I may need to grab the corresponding pthread and tell
// it to suspend if the pthread API supports it.
HX_ASSERT(!"Unimplemented Suspend!");
return retval;
}
HX_RESULT
HXCarbonThread::Resume(void)
{
HX_RESULT retval = HXR_NOTIMPL;
HX_ASSERT(!"Unimplemented Resume!");
return retval;
}
HX_RESULT
HXCarbonThread::SetPriority(UINT32 ulPriority)
{
HX_RESULT retval = HXR_OK;
OSStatus osStatus = MPSetTaskWeight(m_mpTaskID, ulPriority);
return retval;
}
HX_RESULT
HXCarbonThread::GetPriority(UINT32& ulPriority)
{
HX_RESULT retval = HXR_NOTIMPL;
HX_ASSERT(!"Unimplemented GetPriority!");
return retval;
}
HX_RESULT
HXCarbonThread::YieldTimeSlice(void)
{
// unnecessary with MPTask implementation on Carbon.
// I'll call MPYield anyway, although the MP API SDK
// says "In most cases you should not need to call
// this function".
HX_RESULT retval = HXR_OK;
MPYield();
return retval;
}
HX_RESULT
HXCarbonThread::Exit(UINT32 ulExitCode)
{
// xxxbobclark MPExit must be called from within the
// MPTask. That's why I think we need to use MPTerminateTask here.
// This is also called by the destructor, just in case
HX_RESULT retval = HXR_OK;
OSStatus osStatus = noErr;
if (m_mpTaskID && (::MPCurrentTaskID() != m_mpTaskID))
{
// xxxbobclark The windows implementation of HXThread doesn't
// shoot the thread in the head, like ::MPTerminateTask() does...
// it just waits for it to end.
// There is conflicting advice in Apple documentation on how advisable
// it is to call MPTerminateTask. Listing 3-3 Terminating Tasks, at
// http://developer.apple.com/techpubs/macosx/Carbon/oss/MultiPServices/Multitasking_MultiproServ/Concepts/MP.1b.html
// is pretty clear: "When you want to terminate a task, you should call
// the function MPTerminateTask"... but if you follow the link to
// MPTerminateTask, it says "you should be very careful when calling
// MPTerminateTask".
// osStatus = ::MPTerminateTask(m_mpTaskID, NULL);
::MPWaitOnQueue(m_mpInternalTerminationNotificationQueueID, NULL, NULL, NULL, kDurationForever);
::MPDeleteQueue(m_mpInternalTerminationNotificationQueueID);
m_mpInternalTerminationNotificationQueueID = NULL;
m_mpTaskID = NULL;
}
if (m_mpQueueID)
{
::MPDeleteQueue(m_mpQueueID);
m_mpQueueID = NULL;
}
HX_DELETE(m_pQueueMutex);
if (m_pQueuePostSemaphore != kInvalidID)
{
::MPDeleteSemaphore(m_pQueuePostSemaphore);
m_pQueuePostSemaphore = kInvalidID;
}
if (osStatus != noErr) retval = HXR_FAIL;
return retval;
}
HX_RESULT
HXCarbonThread::GetThreadId(UINT32& ulThreadId)
{
HX_RESULT retval = HXR_OK;
ulThreadId = (UINT32) m_mpTaskID;
return retval;
}
ULONG32
HXCarbonThread::GetCurrentThreadID()
{
MPTaskID id = ::MPCurrentTaskID();
return (ULONG32) id;
}
HX_RESULT
HXCarbonThread::PostMessage(HXThreadMessage* pMsg, void* pWindowHandle)
{
HX_RESULT retval = HXR_OK;
HX_ASSERT(pMsg);
if (!pMsg) return HXR_FAIL;
HX_ASSERT(m_mpQueueID != NULL);
m_pQueueMutex->Lock();
OSStatus osStatus = ::MPNotifyQueue(m_mpQueueID, (void*)pMsg->m_ulMessage,
(void*)pMsg->m_pParam1, (void*)pMsg->m_pParam2);
::MPSignalSemaphore(m_pQueuePostSemaphore);
m_pQueueMutex->Unlock();
if (osStatus != noErr)
{
retval = HXR_FAIL;
}
return retval;
}
HX_RESULT
HXCarbonThread::GetMessage(HXThreadMessage* pMsg, UINT32 ulMsgFilterMix, UINT32 ulMsgFilterMax)
{
HX_RESULT retval = HXR_OK;
HX_ASSERT(m_mpQueueID != NULL);
HX_ASSERT(pMsg);
if (!pMsg) return HXR_FAIL;
void* param1;
void* param2;
void* param3;
OSStatus osStatus = ::MPWaitOnSemaphore(m_pQueuePostSemaphore, kDurationForever);
if (osStatus == noErr)
{
m_pQueueMutex->Lock();
osStatus = ::MPWaitOnQueue(m_mpQueueID, ¶m1, ¶m2, ¶m3, kDurationImmediate);
HX_ASSERT(osStatus == noErr);
m_pQueueMutex->Unlock();
}
if (osStatus == noErr)
{
pMsg->m_ulMessage = (UINT32)param1;
pMsg->m_pParam1 = param2; // xxxbobclark I know, I know, this is crazy.
pMsg->m_pParam2 = param3;
}
else
{
retval = HXR_FAIL;
}
return retval;
}
HX_RESULT
HXCarbonThread::PeekMessage(HXThreadMessage* pMsg, UINT32 ulMsgFilterMix, UINT32 ulMsgFilterMax, BOOL bRemoveMessage)
{
// xxxbobclark OK so PeekMessage is going to grab the first message (if it exists)
// and dupe it and replace it in the queue. It will eventually be more complicated
// than this I think. I think I'll need to mutex-protect this routine and do some
// wacky stuff to ensure that the queue is in the same order when it exits as it
// is when it enters.
HX_ASSERT(m_mpQueueID != NULL);
HX_RESULT retval = HXR_OK;
HX_ASSERT(pMsg);
if (!pMsg) return HXR_FAIL;
void* param1;
void* param2;
void* param3;
m_pQueueMutex->Lock();
OSStatus osStatus = ::MPWaitOnQueue(m_mpQueueID, ¶m1, ¶m2, ¶m3, kDurationImmediate);
m_pQueueMutex->Unlock();
if (osStatus == noErr)
{
pMsg->m_ulMessage = (UINT32)param1;
pMsg->m_pParam1 = param2;
pMsg->m_pParam2 = param3;
if (!bRemoveMessage)
{
// replace it in the queue. !!!xxxbobclark but this is
// in the wrong place!!!
HX_ASSERT(!"THIS IS NOT FINISHED! THE QUEUE IS BEING RESTORED INCORRECTLY!");
PostMessage(pMsg, NULL);
}
}
else
{
pMsg->m_ulMessage = 0;
retval = HXR_FAIL; // no event to get!
}
return retval;
}
HX_RESULT
HXCarbonThread::PeekMessageMatching( HXThreadMessage* pMsg,
HXThreadMessage* pMatch,
BOOL bRemoveMessage )
{
// we're going to create a temp queue, copy each message from the main queue into it until we find
// a matching message, then copy the rest
HX_ASSERT(m_mpQueueID != kInvalidID);
HX_RESULT retval = HXR_OK;
HX_ASSERT(pMsg);
if (!pMsg) return HXR_FAIL;
HX_ASSERT(pMatch);
if (!pMatch) return HXR_FAIL;
void* param1;
void* param2;
void* param3;
OSStatus osResult;
m_pQueueMutex->Lock();
// create a temp queue, all messages will be copied into this queue
// (except matching message if bRemoveMessage == TRUE)
MPQueueID tempQ;
osResult = ::MPCreateQueue(&tempQ);
HX_ASSERT(tempQ != kInvalidID);
BOOL foundAMatch = FALSE;
const BOOL bSkipMessage = (pMatch->m_ulMessage == 0);
const BOOL bSkipParam1 = (pMatch->m_pParam1 == NULL);
const BOOL bSkipParam2 = (pMatch->m_pParam2 == NULL);
while((osResult = ::MPWaitOnQueue(m_mpQueueID, ¶m1, ¶m2, ¶m3, kDurationImmediate)) == noErr)
{
// does it match?
if( ( bSkipMessage || pMatch->m_ulMessage == (UINT32) param1 ) &&
( bSkipParam1 || pMatch->m_pParam1 == param2 ) &&
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -