DSS Source Code Analyse (12) - IdleTask and IdleTaskThread

来源:互联网 发布:尼泊尔弯刀淘宝 编辑:程序博客网 时间:2024/06/01 08:57

1.  Related Data Structure

     // Only one copy of IdleTaskThread (IdleTask::sIdleThread)

     // is in DSS, and it is shared by all IdleTask objects

     // It is instanced in StartServer of RunServer.cpp

     // by IdleTask::Initialize. See IdleTask::Initialize below

     // merely a private implementation detail of IdleTask
     class IdleTaskThread : private OSThread
     { 
     private:

             IdleTaskThread() : OSThread(), fHeapMutex() {}
             virtual ~IdleTaskThread() { Assert(fIdleHeap.CurrentHeapSize() == 0); }

             //  set idletime, and enqueue IdleTask object's fIdleEelm

             // into IdleTaskThread's fIdleHeap, conditionally signal IdleTaskThread

             void SetIdleTimer(IdleTask *idleObj, SInt64 msec);
             void CancelTimeout(IdleTask *idleObj);
   

             // IdleTaskThread's thread body function
             virtual void Entry();
             OSHeap  fIdleHeap;   // min heap as idletime
             OSMutex fHeapMutex;  // mutex
             OSCond  fHeapCond;   // conditional variable
             friend class IdleTask;
     };

         
     class IdleTask : public Task
     {

     public:

           //Call Initialize before using this class
           static void Initialize();  // instance IdleTaskThread sIdleThread below
    

           // IdleTask constructor, instance fIdleElem and enclosing self into own fIdleElem;
           IdleTask() : Task(), fIdleElem() { this->SetTaskName("IdleTask"); fIdleElem.SetEnclosingObject(this); }
   
           //This object does a "best effort" of making sure a timeout isn't
           //pending for an object being deleted. In other words, if there is
           //a timeout pending, and the destructor is called, things will get cleaned
           //up. But callers must ensure that SetIdleTimer isn't called at the same
           //time as the destructor, or all hell will break loose.
           virtual ~IdleTask();
    
           //SetIdleTimer:
           //This object will receive an OS_IDLE event in the following number of milliseconds.
           //Only one timeout can be outstanding, if there is already a timeout scheduled, this
           //does nothing.

           // SetIdleTimer's outer interface of IdleTaskThread
           void SetIdleTimer(SInt64 msec) { sIdleThread->SetIdleTimer(this, msec); }

           //CancelTimeout
           //If there is a pending timeout for this object, this function cancels it.
           //If there is no pending timeout, this function does nothing.
           //Currently not supported because OSHeap doesn't support random remove
           void CancelTimeout() { sIdleThread->CancelTimeout(this); }

     private:

           OSHeapElem fIdleElem;  // heapElem enclosing IdleTask itself;

           //there is only one idle thread shared by all idle tasks.
           static IdleTaskThread*  sIdleThread;   

           friend class IdleTaskThread;
};

 

2.  Function Implementation of IdleTask and IdleTaskThread

2.1 Function Implementation of IdleTask (ignored)

2.2 Function Implementation of IdleTaskThread

2.2.1 IdleTaskThread::SetIdleTimer

         void IdleTaskThread::SetIdleTimer(IdleTask *activeObj, SInt64 msec)
         {
                 //note: OSHeap doesn't support a random remove, so this function
                 //won't change the timeout value if there is already one set

                 // this IdleTask (activeObj)'s fIdleElem had been in IdleTaskThread's

                 // heap, therefore return at once.
                 if (activeObj->fIdleElem.IsMemberOfAnyHeap()) return;

                 // set IdleTask (activeObj)'s idle time by its fIdleElem
                 activeObj->fIdleElem.SetValue(OS::Milliseconds() + msec);
                 {

                             // take mutex and insert IdleTask (activeObj)'s fIdleElem

                             // into fIdleHeap of IdleTaskThread. Because the IdleTask

                             // object (activeObj) is enclosing its fIdleElem, it is equiv to

                             // insert the IdleTask object (activeObj) into fIdleHeap of 

                             // IdleTaskThread
                             OSMutexLocker locker(&fHeapMutex);
                             fIdleHeap.Insert(&activeObj->fIdleElem);
                 }

                 // Signal IdleTaskThread conditionally, if it waited conditionally
                 fHeapCond.Signal();
         }

2.2.2   IdleTaskThread::Entry()

void IdleTaskThread::Entry()
{

       // take mutex, the IdleTaskThread will hold the mutex at all times.

       // the mutex will be freed temporarily only when some condition

       // is not meeted.
       OSMutexLocker locker(&fHeapMutex); 
    
       while (true)
       {
               //if there are no events to process, block.

               // if there is nothing in fIdleHeap of IdleTaskThread, wait  conditionally

               // without timeout, it isn't waked up until there is thread signal it.

               // here, it frees fHeapMutex temporarily.
               if (fIdleHeap.CurrentHeapSize() == 0)      fHeapCond.Wait(&fHeapMutex);
               SInt64 msec = OS::Milliseconds();   // current time
        
               // pop elements out of the heap as long as their timeout time has arrived
               while ((fIdleHeap.CurrentHeapSize() > 0) && (fIdleHeap.PeekMin()->GetValue() <= msec))
              {

                        // Idle time of the IdleTask object in heaphead has expired, remove it from heap

                        // and reenqueue it to TaskThread's task queue so that it will be rescheduled

                        // once again 
                        IdleTask* elem = (IdleTask*)fIdleHeap.ExtractMin()->GetEnclosingObject();
                        Assert(elem != NULL);
                       elem->Signal(Task::kIdleEvent);   // reenqueue into TaskThread's task queue
              }
                       
             //we are done sending idle events. If there is a lowest tick count, then
             //we need to sleep until that time or signalled.

             if (fIdleHeap.CurrentHeapSize() > 0)
            {
                     SInt64 timeoutTime = fIdleHeap.PeekMin()->GetValue();
                     //because sleep takes a 32 bit number
                     timeoutTime -= msec;
                     Assert(timeoutTime > 0);
                    UInt32 smallTime = (UInt32)timeoutTime;

                    // wait conditionally with timeout. that says, if smalltime escaped

                    // or it was signalled, it will wake up.

                    // here, it frees fHeapMutex temporarily.
                    fHeapCond.Wait(&fHeapMutex, smallTime);
            }
      }  
}

3. IdleTask object in DSS

3.1 TCPListenerSocket Object

       TCPListenerSocket is derived from EventContext and IdleTask,

        therefore its instance is both EventContext object and Task object

        (IdleTask object)

 

        EventContext::Entry() => TCPListenerSocket::ProcessEvent()

        =>TCPListnerSocket::SetIdleTimer // IdleTask's method

       

        In TCPListenerSocket::ProcessEvent()

        if (fSleepBetweenAccepts)  
        {  // when more than max connection number set, fSleepBetweenAccepts is ture
                  // We are at our maximum supported sockets
                  // slow down so we have time to process the active ones (we will respond with errors or service).
                  // wake up and execute again after sleeping. The timer must be reset each time through
                  //qtss_printf("TCPListenerSocket slowing down\n");
                  this->SetIdleTimer(kTimeBetweenAcceptsInMsec); //sleep 1 second
        }

3.2  ReflectorSocket object

3.3  TimeoutTaskThread object (really only one task rather than thread)