📄 tlibmpthrd.cxx
字号:
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;}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 Xvoid 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 + -