void TaskThread::Entry() { Task* theTask = NULL; while (true) { theTask = this->WaitForTask(); // // WaitForTask returns NULL when it is time to quit if (theTask == NULL || false == theTask->Valid()) return; Bool16 doneProcessingEvent = false; while (!doneProcessingEvent) { //If a task holds locks when it returns from its Run function, //that would be catastrophic and certainly lead to a deadlock #if DEBUG Assert(this->GetNumLocksHeld() == 0); Assert(theTask->fInRunCount == 0); theTask->fInRunCount++; #endif theTask->fUseThisThread = NULL; // Each invocation of Run must independently // request a specific thread. SInt64 theTimeout = 0; if (theTask->fWriteLock) { OSMutexWriteLocker mutexLocker(&TaskThreadPool::sMutexRW); if (TASK_DEBUG) qtss_printf("TaskThread::Entry run global locked TaskName=%s CurMSec=%.3f thread=%p task=%p\n", theTask->fTaskName, OS::StartTimeMilli_Float(), (void *) this, (void *)theTask); theTimeout = theTask->Run(); theTask->fWriteLock = false; } else { OSMutexReadLocker mutexLocker(&TaskThreadPool::sMutexRW); if (TASK_DEBUG) qtss_printf("TaskThread::Entry run TaskName=%s CurMSec=%.3f thread=%p task=%p\n", theTask->fTaskName, OS::StartTimeMilli_Float(), (void *) this, (void *)theTask); theTimeout = theTask->Run(); } #if DEBUG Assert(this->GetNumLocksHeld() == 0); theTask->fInRunCount--; Assert(theTask->fInRunCount == 0); #endif if (theTimeout < 0) { if (TASK_DEBUG) { qtss_printf("TaskThread::Entry delete TaskName=%s CurMSec=%.3f thread=%p task=%p\n", theTask->fTaskName, OS::StartTimeMilli_Float(), (void *) this, (void *)theTask); theTask->fUseThisThread = NULL; if (NULL != fHeap.Remove(&theTask->fTimerHeapElem)) qtss_printf("TaskThread::Entry task still in heap before delete\n"); if (NULL != theTask->fTaskQueueElem.InQueue()) qtss_printf("TaskThread::Entry task still in queue before delete\n"); theTask->fTaskQueueElem.Remove(); if (theTask->fEvents &~Task::kAlive) qtss_printf("TaskThread::Entry flags still set before delete\n"); (void)atomic_sub(&theTask->fEvents, 0); ::strncat(theTask->fTaskName, " deleted", sizeof(theTask->fTaskName) - 1); } theTask->fTaskName[0] = 'D'; //mark as dead delete theTask; theTask = NULL; doneProcessingEvent = true; } else if (theTimeout == 0) { //We want to make sure that 100% definitely the task's Run function WILL //be invoked when another thread calls Signal. We also want to make sure //that if an event sneaks in right as the task is returning from Run() //(via Signal) that the Run function will be invoked again. doneProcessingEvent = compare_and_store(Task::kAlive, 0, &theTask->fEvents); if (doneProcessingEvent) theTask = NULL; } else { //note that if we get here, we don't reset theTask, so it will get passed into //WaitForTask if (TASK_DEBUG) qtss_printf("TaskThread::Entry insert TaskName=%s in timer heap thread=%p elem=%p task=%p timeout=%.2f\n", theTask->fTaskName, (void *) this, (void *)&theTask->fTimerHeapElem, (void *)theTask, (float)theTimeout / (float)1000); theTask->fTimerHeapElem.SetValue(OS::Milliseconds() + theTimeout); fHeap.Insert(&theTask->fTimerHeapElem); (void)atomic_or(&theTask->fEvents, Task::kIdleEvent); doneProcessingEvent = true; } #if TASK_DEBUG SInt64 yieldStart = OS::Milliseconds(); #endif this->ThreadYield(); #if TASK_DEBUG SInt64 yieldDur = OS::Milliseconds() - yieldStart; static SInt64 numZeroYields; if (yieldDur > 1) { if (TASK_DEBUG) qtss_printf("TaskThread::Entry time in Yield %qd, numZeroYields %qd \n", yieldDur, numZeroYields); numZeroYields = 0; } else numZeroYields++; #endif } } }
void EventContext::RequestEvent(int theMask) { #if DEBUG fModwatched = true; #endif // // The first time this function gets called, we're supposed to // call watchevent. Each subsequent time, call modwatch. That's // the way the MacOS X event queue works. if (fWatchEventCalled) { fEventReq.er_eventbits = theMask; #if MACOSXEVENTQUEUE if (modwatch(&fEventReq, theMask) != 0) #else if (select_modwatch(&fEventReq, theMask) != 0) #endif AssertV(false, OSThread::GetErrno()); } else { //allocate a Unique ID for this socket, and add it to the ref table #ifdef __Win32__ // // Kind of a hack. On Win32, the way that we pass around the unique ID is // by making it the message ID of our Win32 message (see win32ev.cpp). // Messages must be >= WM_USER. Hence this code to restrict the numberspace // of our UniqueIDs. if (!compare_and_store(8192, WM_USER, &sUniqueID)) // Fix 2466667: message IDs above a fUniqueID = (PointerSizedInt)atomic_add(&sUniqueID, 1); // level are ignored, so wrap at 8192 else fUniqueID = (PointerSizedInt)WM_USER; #else if (!compare_and_store(10000000, 1, &sUniqueID)) fUniqueID = (PointerSizedInt)atomic_add(&sUniqueID, 1); else fUniqueID = 1; #endif fRef.Set(fUniqueIDStr, this); Assert(fEventThread->fRefTable.Register(&fRef) == OS_NoErr); //fill out the eventreq data structure ::memset( &fEventReq, '\0', sizeof(fEventReq)); fEventReq.er_type = EV_FD; fEventReq.er_handle = fFileDesc; fEventReq.er_eventbits = theMask; fEventReq.er_data = (void*)fUniqueID; fWatchEventCalled = true; #if MACOSXEVENTQUEUE if (watchevent(&fEventReq, theMask) != 0) #else if (select_watchevent(&fEventReq, theMask) != 0) #endif //this should never fail, but if it does, cleanup. AssertV(false, OSThread::GetErrno()); } }