📄 tlibmpthrd.cxx
字号:
(void *)this, // param 2 == "PThread to clean up"
0, // no options
&PX_threadId);
PAssert(err == 0, "MPCreateTask failed");
if (err) throw std::bad_alloc();
}
long PThread::PX_ThreadStart(void * arg)
{
MPTaskID threadId = MPCurrentTaskID();
// self-detach (no need)
PThread * thread = (PThread *)arg;
thread->SetThreadName(thread->GetThreadName());
PProcess & process = PProcess::Current();
// add thread to thread list
process.threadMutex.Wait();
process.activeThreads.SetAt((unsigned)threadId, thread);
process.threadMutex.Signal();
// if we are not supposed to start suspended, then don't wait
// if we are supposed to start suspended, then wait for a resume
if (thread->PX_suspendCount != 0) {
thread->suspend_semaphore->Wait(); // Wait for the Resume
}
// now call the the thread main routine
//PTRACE(1, "tlibthrd\tAbout to call Main");
thread->Main();
#ifdef DEBUG_THREADS
if (debug_mpthreads)
fprintf(stderr,"thread %p returning\n", thread);
#endif
return 0;
}
#ifndef _DEBUG
#undef PMEMORY_CHECK
#endif
void PProcess::SignalTimerChange()
{
if (housekeepingThread == NULL) {
#if PMEMORY_CHECK
BOOL oldIgnoreAllocations = PMemoryHeap::SetIgnoreAllocations(TRUE);
#endif
housekeepingThread = new PHouseKeepingThread;
#if PMEMORY_CHECK
PMemoryHeap::SetIgnoreAllocations(oldIgnoreAllocations);
#endif
}
SetUpTermQueue();
MPNotifyQueue(terminationNotificationQueue, 0, 0, 0);
}
void PThread::PX_ThreadEnd(void * arg)
{
PThread * thread = (PThread *)arg;
PProcess & process = PProcess::Current();
MPTaskID id = thread->PX_GetThreadId();
if (id != 0) {
// remove this thread from the active thread list
process.threadMutex.Wait();
process.activeThreads.SetAt((unsigned)id, NULL);
process.threadMutex.Signal();
}
// delete the thread if required, note this is done this way to avoid
// a race condition, the thread ID cannot be zeroed before the if!
if (thread->autoDelete) {
thread->PX_threadId = 0; // Prevent terminating terminated thread
delete thread;
}
else
thread->PX_threadId = 0;
}
MPTaskID PThread::PX_GetThreadId() const
{
return PX_threadId;
}
void PThread::Restart()
{
if (IsTerminated())
return;
PX_NewThread(FALSE);
}
void PThread::Terminate()
{
if (PX_origStackSize <= 0)
return;
if (IsTerminated())
return;
PTRACE(1, "tlibthrd\tForcing termination of thread " << (void *)this);
if (Current() == this)
MPExit(0);
else {
MPTaskID taskId = PX_threadId;
WaitForTermination();
// XXX Dire Consequences[TM] are warned of when one uses MPTerminateTask.
// XXX However, the same Dire Consequences are predicted (I think) for
// XXX pthread_kill which the PWLIB code already uses.
// XXX However, the only thing the cleanup function does is removes the
// XXX thread from the thread table, which is already performed by the
// XXX housekeeping thread; PWLIB doesn't try to salvage locks or
// XXX anything clever like that.
// XXX I just hope taskIds aren't quickly reused.
if (taskId != 0)
(void)MPTerminateTask(taskId, kMPTaskAbortedErr);
}
}
void PThread::PXSetWaitingSemaphore(PSemaphore * sem)
{
// not needed
}
BOOL PThread::IsTerminated() const
{
if (PX_threadId == 0) {
//PTRACE(1, "tlibthrd\tIsTerminated(" << (void *)this << ") = 0");
return TRUE;
}
#ifdef _not_def_ // Sigh. no MPGetNextTaskID on MOSX
// This seems like a silly way to do this, but I think it might work.
// The end condition for MPGetNextTaskID isn't documented, so I try both
// logical possibilities.
MPTaskID sometask = 0;
MPProcessID myproc = 0;
while (MPGetNextTaskID(myproc, &sometask) == noErr) {
if (sometask == 0) break;
if (sometask == PX_threadId) {
//PTRACE(1, "tlibthrd\tIsTerminated(" << (void *)this << ") not dead yet");
return FALSE;
}
}
// didn't find it, it's dead
//PTRACE(1, "tlibthrd\tIsTerminated(" << (void *)this << ") = 0");
return TRUE;
#else
return FALSE; // ENOCLUE
#endif
}
// Mac OS X and Darwin 1.2 does not support pthread_kill() or sigwait()
// so we cannot implement suspend and resume using signals. Instead we have a
// partial implementation using a Semaphore.
// As a result, we can create a thread in a suspended state and then 'resume'
// it, but once it is going, we can no longer suspend it.
// So, for Mac OS X, we will accept Resume() calls (or Suspend(FALSE))
// but reject Suspend(TRUE) calls with an Assertion. This will indicate
// to a user that we cannot Suspend threads on Mac OS X
void PThread::Suspend(BOOL susp)
{
OSStatus err;
err = MPWaitOnSemaphore(PX_suspendMutex,kDurationForever);
PAssert(err == 0, "MPWaitOnSemaphore failed");
if (susp) {
// Suspend - warn the user with an Assertion
PAssertAlways("Cannot suspend threads on Mac OS X due to lack of pthread_kill()");
}
// if resuming, then see if to really resume
else if (PX_suspendCount > 0) {
PX_suspendCount--;
if (PX_suspendCount == 0) {
suspend_semaphore->Signal();
}
}
err = MPSignalSemaphore(PX_suspendMutex);
PAssert( err == 0, "MPSignalSemaphore failed");
}
void PThread::Resume()
{
Suspend(FALSE);
}
BOOL PThread::IsSuspended() const
{
OSStatus err;
if (IsTerminated())
return FALSE;
err = MPWaitOnSemaphore(PX_suspendMutex, kDurationForever);
PAssert(err == 0, "MPWaitOnSemaphore failed");
BOOL suspended = PX_suspendCount > 0;
err = MPSignalSemaphore(PX_suspendMutex);
PAssert(err == 0, "MPSignalSemaphore failed");
return suspended;
}
void PThread::SetAutoDelete(AutoDeleteFlag deletion)
{
PAssert(deletion != AutoDeleteThread || this != &PProcess::Current(), PLogicError);
autoDelete = deletion == AutoDeleteThread;
}
void PThread::SetPriority(Priority /*priorityLevel*/)
{
}
PThread::Priority PThread::GetPriority() const
{
return LowestPriority;
}
void PThread::Yield()
{
::sleep(0);
}
PThread * PThread::Current()
{
PProcess & process = PProcess::Current();
process.threadMutex.Wait();
PThread * thread = process.activeThreads.GetAt((unsigned)MPCurrentTaskID());
process.threadMutex.Signal();
return PAssertNULL(thread);
}
void PThread::Sleep(const PTimeInterval & timeout)
{
AbsoluteTime expiry;
Duration delta = kDurationForever;
if (timeout != PMaxTimeInterval) {
delta = timeout.GetMilliSeconds();
}
expiry = AddDurationToAbsolute(delta, UpTime());
(void)MPDelayUntil(&expiry);
}
void PThread::WaitForTermination() const
{
PAssert(Current() != this, "Waiting for self termination!");
PXAbortBlock();
while (!IsTerminated()) {
PAssert(PX_signature == kMPThreadSig, "bad signature in living thread");
Current()->Sleep(10);
#ifdef DEBUG_THREADS
if (debug_mpthreads)
fprintf(stderr,"spinning for termination of thread %p\n", (void *)this);
#endif
if (noHousekeeper) PollNotificationQueue(kDurationImmediate);
}
}
BOOL PThread::WaitForTermination(const PTimeInterval & maxWait) const
{
PAssert(Current() != this, "Waiting for self termination!");
//PTRACE(1, "tlibthrd\tWaitForTermination(delay)");
PXAbortBlock();
PTimer timeout = maxWait;
while (!IsTerminated()) {
if (timeout == 0)
return FALSE;
Current()->Sleep(10);
}
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
PSemaphore::PSemaphore(unsigned initial, unsigned maxCount)
{
OSStatus err = MPCreateSemaphore(maxCount, initial, &semId);
PAssert(err == 0, "MPCreateSemaphore failed");
PAssert((long)semId != 0 && (long)semId != -1, "stupid semId");
}
PSemaphore::~PSemaphore()
{
OSStatus err = MPDeleteSemaphore(semId);
PAssert(err == 0, "MPDeleteSemaphore failed");
*(long *)&semId = -1;
}
void PSemaphore::Wait()
{
assert((long)semId != 0);
assert((long)semId != -1);
PAssert((long)semId != -1, "wait on destructed PSemaphore");
PAssert((long)semId != 0, "semId stomped");
OSStatus err = MPWaitOnSemaphore(semId, kDurationForever);
PAssert(err == 0, "MPWaitOnSemaphore failed");
}
BOOL PSemaphore::Wait(const PTimeInterval & waitTime)
{
OSErr err = 0;
if (waitTime == PMaxTimeInterval) {
Wait();
return TRUE;
}
Duration timeout = waitTime.GetMilliSeconds();
if ((err = MPWaitOnSemaphore(semId, timeout)) == noErr)
return TRUE;
if (err == kMPTimeoutErr)
return FALSE;
PAssert(err == 0, psprintf("timed wait error = %i", err));
return FALSE;
}
void PSemaphore::Signal()
{
OSStatus err = MPSignalSemaphore(semId);
// was it already signalled?
if (err == kMPInsufficientResourcesErr) err = 0;
PAssert(err == 0, "MPSignalSemaphore failed");
}
BOOL PSemaphore::WillBlock() const
{
OSStatus err = MPWaitOnSemaphore(semId, kDurationImmediate);
if (err == kMPTimeoutErr)
return TRUE;
PAssert(err == 0, psprintf("timed wait error = %i", err));
(void)MPSignalSemaphore(semId);
return FALSE;
}
// Ideally, a PMutex would contain an MPCriticalSection instead of a
// semaphore, but the class derivation is outside the machine-specific
// code, and I'm unwilling to do something gross like implement a bogus
// constructor for PSemaphore which doesn't allocate a semaphore.
PMutex::PMutex()
: PSemaphore(1, 1)
{
}
void PMutex::Wait()
{
PSemaphore::Wait();
}
BOOL PMutex::Wait(const PTimeInterval & timeout)
{
return PSemaphore::Wait(timeout);
}
void PMutex::Signal()
{
PSemaphore::Signal();
}
BOOL PMutex::WillBlock() const
{
return PSemaphore::WillBlock();
}
PSyncPoint::PSyncPoint()
: PSemaphore(0, 1)
{
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -