예제 #1
0
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());

    }
}