DSS Source Code Analyse (11) - TimeoutTask

1. Related Data Structure

 

// Only one copy of its instance (sThread, which is shared by all

// TimeoutTask objects) existed in DSS, see TimeoutTask declare and its

// Initialize implementation.

// TimeoutTask::Initialize called in StartServer of RunServer.cpp as following

// TimeoutTask::Initialize()

// Therefore all members in this object, also only one of copy

// its own instance. e.g., fMutex, fQueue of TimeoutTaskThread

 

//  TimeoutTaskThread is one task object rather than thread object

class TimeoutTaskThread : public IdleTask
{
    public:
   
        //All timeout tasks get timed out from this thread
                    TimeoutTaskThread() : IdleTask(), fMutex() {this->SetTaskName("TimeoutTask");}
        virtual     ~TimeoutTaskThread(){}

    private:
       
        //this thread runs every minute and checks for timeouts
        enum
        {
            kIntervalSeconds = 60   //UInt32
        };

        virtual SInt64          Run();
        OSMutex                 fMutex;
        OSQueue                 fQueue;  // All TimeoutTask objects are enqueued this queue
        
        friend class TimeoutTask;
};

 

class TimeoutTask
{
    //TimeoutTask is not a derived object off of Task, to add flexibility as
    //to how this object can be utilitized
   
    public:
   
        //Call Initialize before using this class
        static  void Initialize();
        //Pass in the task you'd like to send timeouts to.
        //Also pass in the timeout you'd like to use. By default, the timeout is 0 (NEVER).
        TimeoutTask(Task* inTask, SInt64 inTimeoutInMilSecs = 60);
        ~TimeoutTask();
       
        //MODIFIERS

        // Changes the timeout time, also refreshes the timeout
        void        SetTimeout(SInt64 inTimeoutInMilSecs);
       
        // Specified task will get a Task::kTimeoutEvent if this
        // function isn't called within the timeout period
        void        RefreshTimeout() { fTimeoutAtThisTime = OS::Milliseconds() + fTimeoutInMilSecs; Assert(fTimeoutAtThisTime > 0); }
       
        void        SetTask(Task* inTask) { fTask = inTask; }
    private:
   
        Task*       fTask;
        SInt64      fTimeoutAtThisTime;
        SInt64      fTimeoutInMilSecs;
        //for putting on our global queue of timeout tasks
        OSQueueElem fQueueElem;
       

        // static object instanced in Initialize that was called in StartServer of RunServer.cpp as following

        // TimeoutTask::Initialize();


        static TimeoutTaskThread*   sThread;   


        
        friend class TimeoutTaskThread;
};

 

2.  function implementation of above data structure

2.1 TimeoutTask::Initialize

       void TimeoutTask::Initialize()
       {
              if (sThread == NULL)
             {

                    // unqiue instanced object of TimeoutTaskThread
                    sThread = NEW TimeoutTaskThread();  

                    // enqueue TimeoutTaskThread object (one task object only really) just created

                    // to task queue of some taskthread
                    sThread->Signal(Task::kStartEvent);

                    // because TimeoutTaskThread derived from IdleTask, which derived from Task.

                    // This TimeoutTaskThread(sThread) object is with  fTaskQueueElem, and 

                    // fTimerHeapElem members, and In Task Construct function, fTaskQueueElem and

                    // fTimeHeapElem instanced, and enclosed as followings

                    //  fTaskQueueElem.SetEnclosingObject(this);
                    //  fTimerHeapElem.SetEnclosingObject(this);

                    //  then fTaskQueueElem is enqueued into task queue of TaskThread in task's Signal

                    //  function, fUseThisThread->fTaskQueue.EnQueue(&fTaskQueueElem); or

                    // TaskThreadPool::sTaskThreadArray[theThreadIndex]->fTaskQueue.EnQueue(&fTaskQueueElem);
             }
   
       }

2.2 TimeoutTask::TimeoutTask(Task* inTask, SInt64 inTimeoutInMilSecs)

       // TimeoutTask construct function.

       // instance own fQueueElem, associate with fTask to inTask(from outer), noted later

       // set timeout time

       // enclosing own into own fQueueElem

       // enqueue own fQueueElem into TimeoutTaskThread(sThread)'s fQueue,

       // equiv to, enqueue own into TimeoutTaskThread(sThread)'s fQueue

      TimeoutTask::TimeoutTask(Task* inTask, SInt64 inTimeoutInMilSecs)
            : fTask(inTask), fQueueElem()  
      {
             fQueueElem.SetEnclosingObject(this);
             this->SetTimeout(inTimeoutInMilSecs);
             if (NULL == inTask)  fTask = (Task *) this;
             Assert(sThread != NULL); // this can happen if RunServer intializes tasks in the wrong order

             OSMutexLocker locker(&sThread->fMutex);
            sThread->fQueue.EnQueue(&fQueueElem);
     }

2.3  TimeoutTask::SetTimeout(SInt64 inTimeoutInMilSecs)

void TimeoutTask::SetTimeout(SInt64 inTimeoutInMilSecs)
{
    fTimeoutInMilSecs = inTimeoutInMilSecs;   // Timeout time milliseconds unit
    if (inTimeoutInMilSecs == 0)
        fTimeoutAtThisTime = 0;
    else
        fTimeoutAtThisTime = OS::Milliseconds() + fTimeoutInMilSecs;
}

 

2.4  TimeoutTask::RefreshTimeout()

       TimeoutTask::RefreshTimeout()

       {  // refresh timeout time

               fTimeoutAtThisTime = OS::Milliseconds() + fTimeoutInMilSecs;

               Assert(fTimeoutAtThisTime > 0);

        }

2.5   TimeoutTask::~TimeoutTask()

        TimeoutTask::~TimeoutTask()
        {
                OSMutexLocker locker(&sThread->fMutex);

                // unenqueue from TimeoutTaskThread's fQueue
                sThread->fQueue.Remove(&fQueueElem);
        }

 

2.6   TimeoutTaskThread::Run()

         // TimeoutTaskThread::Run run in some TaskThread's env

         // by theTimeout = theTask->Run(), when some theTask is

         // exactly TimeoutTaskThread object in TaskThread::Entry()

        SInt64 TimeoutTaskThread::Run()
        {
               //ok, check for timeouts now. Go through the whole queue
               OSMutexLocker locker(&fMutex);
               SInt64 curTime = OS::Milliseconds();

               //always default to 60 seconds but adjust to smallest interval > 0
               SInt64 intervalMilli = kIntervalSeconds * 1000;


               SInt64 taskInterval = intervalMilli;
 

               // iterator all TimeoutTask object from TimeoutTask's fQueue
               for (OSQueueIter iter(&fQueue); !iter.IsDone(); iter.Next()) { 
                      TimeoutTask* theTimeoutTask = (TimeoutTask*)iter.GetCurrent()->GetEnclosingObject();
        
                       // if it's time to time this task out, signal it
                      if ((theTimeoutTask->fTimeoutAtThisTime > 0)

                     && (curTime >= theTimeoutTask->fTimeoutAtThisTime)) { 

                              // timeout occur here
#if TIMEOUT_DEBUGGING
                              qtss_printf("TimeoutTask %"_S32BITARG_" timed out.

                                                 Curtime = %"_64BITARG_"d,  timeout time =

                                                 %"_64BITARG_"d\n",(SInt32)theTimeoutTask,

                                                 curTime,  theTimeoutTask->fTimeoutAtThisTime);
#endif
                              //   theTimeoutTask->fTask may be RTSPSession(Task), or

                              //   RTPSession(Task) etc, forever, each RTSPSession, RTPSession

                              //   such Task associates with one own TimeoutTask by its member

                              //   fTimeoutTask pointer. They assocaite with each other.

 

                              //   Therefore, by theTimeoutTask's fTask, find its related some real Task

                              //    and enqueue this real Task into task queue of some TaskThread with

                              //    TimeoutEvent

                              theTimeoutTask->fTask->Signal(Task::kTimeoutEvent);
                     }  else {
                             taskInterval = theTimeoutTask->fTimeoutAtThisTime - curTime;
                             if ( (taskInterval > 0) && (theTimeoutTask->fTimeoutInMilSecs > 0)

                             && (intervalMilli > taskInterval) ) {
                                      // set timeout to 1 second past this task's timeout 

                                      intervalMilli = taskInterval + 1000;

                             }
#if TIMEOUT_DEBUGGING
                             qtss_printf("TimeoutTask %"_S32BITARG_" not being timed out.

                                               Curtime = %"_64BITARG_"d. timeout time =

                                               %"_64BITARG_"d\n", (SInt32)theTimeoutTask,

                                               curTime, theTimeoutTask->fTimeoutAtThisTime);
#endif
                     }
               }


               (void)this->GetEvents();  // we must clear the event mask!
 
              OSThread::ThreadYield();  // do nothing in Linux
 
#if TIMEOUT_DEBUGGING
             qtss_printf ("TimeoutTaskThread::Run interval seconds=

                                  %"_S32BITARG_"\n", (SInt32) intervalMilli/1000);
#endif
             // this will be return value theTimeout = theTask->Run(), denote exec me in intervalMill

             // milliseconds next time
             return intervalMilli;  // don't delete me!
}

3.  TimeoutEvent Process by Real Task such as RTSPSession Task, RTPSession Task etc

      TaskThread::Entry() ==> theTask->Run() 

      if theTask is RTSPSession Task or RTPSession

      Task, following Run will be called

3.1  TimeoutEvent Process of RTSPSession::Run()

        if ((events & Task::kTimeoutEvent) || (events & Task::kKillEvent))
               fLiveSession = false;  // mark session dead

        .......

 

        // Make absolutely sure there are no resources being occupied by the session
        // at this point.
        this->CleanupRequest();

        // Only delete if it is ok to delete!
        if (fObjectHolders == 0)    
return -1;  // destructor will be called

        // If we are here because of a timeout, but we can't delete because someone
        // is holding onto a reference to this session, just reschedulethe timeout.
        //
        // At this point, however, the session is DEAD.

        return 0; 

        // session can't be destructor because of reference num to this

        // session not zero; maybe Run called next time, the fObjectHolders is zero.

 

3.2  TimeoutEvent Process of RTPSession::Run()

     

    //if we have been instructed to go away, then let's delete ourselves
    if ((events & Task::kKillEvent) || (events & Task::kTimeoutEvent)

   || (fModuleDoingAsyncStuff))   {
          if (!fModuleDoingAsyncStuff)  {
                if (events & Task::kTimeoutEvent)
                fClosingReason = qtssCliSesCloseTimeout;
            
                //deletion is a bit complicated. For one thing, it must happen from within
                //the Run function to ensure that we aren't getting events when we are deleting
                //ourselves. We also need to make sure that we aren't getting RTSP requests
                //(or, more accurately, that the stream object isn't being used by any other
                //threads). We do this by first removing the session from the session map.
       
#if RTPSESSION_DEBUGGING
                qtss_printf("RTPSession %"_S32BITARG_": about to be killed.

                                 Eventmask = %"_S32BITARG_"\n",(SInt32)this, (SInt32)events);
#endif
                // We cannot block waiting to UnRegister, because we have to
                // give the RTSPSessionTask a chance to release the RTPSession.
               OSRefTable* sessionTable = QTSServerInterface::GetServer()->GetRTPSessionMap();
               Assert(sessionTable != NULL);
               if (!sessionTable->TryUnRegister(&fRTPMapElem)) {
                       this->Signal(Task::kKillEvent);// So that we get back to this place in the code
                      return kCantGetMutexIdleTime;
               }
       
               // The ClientSessionClosing role is allowed to do async stuff
               fModuleState.curTask = this;
               fModuleDoingAsyncStuff = true;  // So that we know to jump back to the
               fCurrentModule = 0;             // right place in the code
       
               // Set the reason parameter
               theParams.clientSessionClosingParams.inReason = fClosingReason;
           
                // If RTCP packets are being generated internally for this stream,
                // Send a BYE now.
                RTPStream** theStream = NULL;
                UInt32 theLen = 0;
           
                if (this->GetPlayFlags() & qtssPlayFlagsSendRTCP) {
                         SInt64 byePacketTime = OS::Milliseconds();
                         for (int x = 0; this->GetValuePtr(qtssCliSesStreamObjects, x,

                                (void**)&theStream, &theLen) == QTSS_NoErr; x++) {
                               if (theStream && *theStream != NULL)
                                       (*theStream)->SendRTCPSR(byePacketTime, true);

                         }
                }
          }
       

          // KillEvent or TimeoutEvent, session cleanup


          //at this point, we know no one is using this session, so invoke the
          //session cleanup role. We don't need to grab the session mutex before
          //invoking modules here, because the session is unregistered and
          //therefore there's no way another thread could get involved anyway

          UInt32 numModules = QTSServerInterface::GetNumModulesInRole(

                           QTSSModule::kClientSessionClosingRole);
          {
                 for (; fCurrentModule < numModules; fCurrentModule++)     {  
                        fModuleState.eventRequested = false;
                        fModuleState.idleTime = 0;
                        QTSSModule* theModule = QTSServerInterface::GetModule(

                                    QTSSModule::kClientSessionClosingRole, fCurrentModule);
                        (void)theModule->CallDispatch(QTSS_ClientSessionClosing_Role, &theParams);

                        // If this module has requested an event, return and wait for the event to transpire

                        // return 0
                        if (fModuleState.eventRequested) return fModuleState.idleTime;

                        // If the module has requested idle time...
                 }
          }
       
          return -1; // doing this will cause the destructor to get called.
    }

 

4. TimeoutTask of Real Task(such as RTSPSession, RTPSession)

4.1 TimeoutTask Instance of RTSPSession

       // when TimeoutTask constructor called, its fQueueElem enclosing TimeoutTask itself

       // and fQueueElem was enqueued into TimeoutTaskThread(really one Task rather than thread)

       // (sThread)'s fQueue. Notes: sThread of TimeoutTask is static variable, therefore only

       // one copy of its instance is shared by all TimeoutTask objects

      RTSPSessionInterface::RTSPSessionInterface()
              :   QTSSDictionary(QTSSDictionaryMap::GetMap(QTSSDictionaryMap::kRTSPSessionDictIndex)),
             Task(),
              fTimeoutTask(NULL, QTSServerInterface::GetServer()->GetPrefs()->GetRealRTSPTimeoutInSecs() * 1000),

              .........

              fRTSPSession3GPP(QTSServerInterface::GetServer()->GetPrefs()->Get3GPPEnabled() ),
              fRTSPSession3GPPPtr(&fRTSPSession3GPP)
     {

              // TimeoutTask and RTSPSession(Task) associate with each other

              fTimeoutTask.SetTask(this);
              fSocket.SetTask(this);

     }

 

4.2 TimeoutTask Instance of RTPSession

      Similiar with RTSPSession

4.3  RefreshTimeout of RTSPSession

       In RTSPSession::Run

       case case kFilteringRequest:

       {
                // We received something so auto refresh
                // The need to auto refresh is because the api doesn't allow a module to refresh at this point
                // fTimeoutTask, TimeoutTask object of RTSPSession
                fTimeoutTask.RefreshTimeout();

                ........

         }

44   RefreshTimeout of RTPSession

       In RTPStream, fSession saved RTPSession object, In some functions implementation

       of RTPStream, fSession's RefreshTimeout is called.

       fSession->RefreshTimeout();

5.  Summary

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值