• live555源码学习笔记之TaskScheduler


    今天抽空研究了下live555的任务实现:

    TaskScheduler分为三种任务:socket handler,event handler,delay task。这三种任务的特点是,前两个加入执行队列后会一直存在,而delay task在执行完一次后会立即弃掉。


    socket handler保存在队列BasicTaskScheduler0::HandlerSet* fHandlers中;

    event handler保存在数组BasicTaskScheduler0::TaskFunc * fTriggeredEventHandlers[MAX_NUM_EVENT_TRIGGERS]中;

    delay task保存在队列BasicTaskScheduler0::DelayQueue fDelayQueue中。

    一.研究delaytask:

    可以参考:http://www.cnblogs.com/nightwatcher/archive/2011/04/10/2011158.html

    在学习操作delay task的函数之前,先研究下live555中的这个DelayQueue :

    1.  
      class DelayQueueEntry {
    2.  
      public:
    3.  
      virtual ~DelayQueueEntry();
    4.  
       
    5.  
      intptr_t token() {
    6.  
      return fToken;
    7.  
      }
    8.  
       
    9.  
      protected: // abstract base class
    10.  
      DelayQueueEntry(DelayInterval delay);
    11.  
       
    12.  
      virtual void handleTimeout(); //执行超时任务;
    13.  
       
    14.  
      private:
    15.  
      friend class DelayQueue;
    16.  
      DelayQueueEntry* fNext; //后一个对象
    17.  
      DelayQueueEntry* fPrev; //前一个对象
    18.  
      DelayInterval fDeltaTimeRemaining; 超时时间(倒计时),该结构体含两个参数,一个是秒,一个是微秒;
    19.  
       
    20.  
      intptr_t fToken; //游标,方便查表
    21.  
      static intptr_t tokenCounter; //队列计数器;
    22.  
      };
    23.  
       
    24.  
       
    25.  
      class DelayQueue: public DelayQueueEntry {
    26.  
      public:
    27.  
      DelayQueue();
    28.  
      virtual ~DelayQueue();
    29.  
       
    30.  
      void addEntry(DelayQueueEntry* newEntry); // returns a token for the entry
    31.  
      void updateEntry(DelayQueueEntry* entry, DelayInterval newDelay); //更新一个任务的超时时间
    32.  
      void updateEntry(intptr_t tokenToFind, DelayInterval newDelay); //通过游标查找某个任务,再更新其超时时间
    33.  
      void removeEntry(DelayQueueEntry* entry); // but doesn't delete it //将某个任务从队列从移除,但是不销毁该对象
    34.  
      DelayQueueEntry* removeEntry(intptr_t tokenToFind); // but doesn't delete it /通过游标,将某个任务从队列从移除,但是不销毁该对象
    35.  
       
    36.  
      DelayInterval const& timeToNextAlarm(); //倒计时还剩多久时间
    37.  
      void handleAlarm(); //将超时的任务移除,然后执行
    38.  
       
    39.  
      private:
    40.  
      DelayQueueEntry* head() { return fNext; }
    41.  
      DelayQueueEntry* findEntryByToken(intptr_t token);
    42.  
      void synchronize(); //更新超时时间
    43.  
       
    44.  
      EventTime fLastSyncTime;
    45.  
      };
    46.  
       
    47.  
       
    48.  
       
    49.  
      class AlarmHandler: public DelayQueueEntry {
    50.  
      public:
    51.  
      AlarmHandler(TaskFunc* proc, void* clientData, DelayInterval timeToDelay)
    52.  
      : DelayQueueEntry(timeToDelay), fProc(proc), fClientData(clientData) {
    53.  
      }
    54.  
       
    55.  
      private: // redefined virtual functions
    56.  
      virtual void handleTimeout() {
    57.  
      (*fProc)(fClientData); //通过调用函数指针 +参数 执行任务
    58.  
      DelayQueueEntry::handleTimeout();
    59.  
      }
    60.  
       
    61.  
      private:
    62.  
      TaskFunc* fProc; //delaytask的函数指针
    63.  
      void* fClientData; //delaytask的函数所需要的参数
    64.  
      };

    操作该队列的方法在BasicTaskScheduler0.h、BasicTaskScheduler0.cpp 中声明和实现:

    1.  
      TaskToken BasicTaskScheduler0::scheduleDelayedTask(int64_t microseconds, TaskFunc* proc, void* clientData) {
    2.  
        if (microseconds < 0) microseconds = 0;
    3.  
        DelayInterval timeToDelay((long)(microseconds/1000000), (long)(microseconds%1000000));
    4.  
        AlarmHandler* alarmHandler = new AlarmHandler(proc, clientData, timeToDelay); //创建一个AlarmHandler对象;
    5.  
        fDelayQueue.addEntry(alarmHandler); //加入任务队列中;
    6.  
       
    7.  
        return (void*)(alarmHandler->token());//返回该任务在队列中的游标
    8.  
      }
    9.  
       
    10.  
      void BasicTaskScheduler0::unscheduleDelayedTask(TaskToken& prevTask) {
    11.  
        DelayQueueEntry* alarmHandler = fDelayQueue.removeEntry((intptr_t)prevTask);//根据任务对象的游标,将任务移除队列
    12.  
        prevTask = NULL;
    13.  
        delete alarmHandler;//销毁对象
    14.  
      }

    二.event handler

     event handler是被存在数组中。数组大小固定等于32(#define MAX_NUM_EVENT_TRIGGERS 32),用EventTriggerId来表示数组中的项,EventTriggerId是一个32位整数,因为数组是32项,所以用EventTriggerId中的第n位置为1,则表明对应数组中的第n项。成员变量fTriggersAwaitingHandling也是EventTriggerId类型,它里面置1的那些位对应了数组中所有需要处理的项。这样做节省了内存和计算,但降低了可读性,呵呵,而且也不够灵活,只能支持32项或64项,其它数量不被支持。

    1.  
      EventTriggerId BasicTaskScheduler0::createEventTrigger(TaskFunc* eventHandlerProc) {
    2.  
      unsigned i = fLastUsedTriggerNum;
    3.  
      EventTriggerId mask = fLastUsedTriggerMask;
    4.  
       
    5.  
      do {
    6.  
      i = (i+1)%MAX_NUM_EVENT_TRIGGERS; //序号加一
    7.  
      mask >>= 1; //mask右移一位,代表一个新的序号
    8.  
      if (mask == 0) mask = 0x80000000; //默认为第32位为1
    9.  
       
    10.  
      if (fTriggeredEventHandlers[i] == NULL) {//如果数组的该位置没有存入数据,则将函数指针存入fTriggeredEventHandlers,数据位置空,触发事件时传入
    11.  
      // This trigger number is free; use it:
    12.  
      fTriggeredEventHandlers[i] = eventHandlerProc;
    13.  
      fTriggeredEventClientDatas[i] = NULL; // sanity
    14.  
       
    15.  
      fLastUsedTriggerMask = mask; //记录最新的Mask和序号
    16.  
      fLastUsedTriggerNum = i;
    17.  
       
    18.  
      return mask; //分配成功,返回项数
    19.  
      }
    20.  
      } while (i != fLastUsedTriggerNum);
    21.  
       
    22.  
      // All available event triggers are allocated; return 0 instead:
    23.  
      return 0;
    24.  
      }
    25.  
       
    26.  
      void BasicTaskScheduler0::deleteEventTrigger(EventTriggerId eventTriggerId) {
    27.  
      fTriggersAwaitingHandling &=~ eventTriggerId;
    28.  
       
    29.  
      if (eventTriggerId == fLastUsedTriggerMask) { // common-case optimization:如果删除的事件正好是最后一个非空项,则直接将函数指针和参数置空;
    30.  
      fTriggeredEventHandlers[fLastUsedTriggerNum] = NULL;
    31.  
      fTriggeredEventClientDatas[fLastUsedTriggerNum] = NULL;
    32.  
      } else {
    33.  
      // "eventTriggerId" should have just one bit set.
    34.  
      // However, we do the reasonable thing if the user happened to 'or' together two or more "EventTriggerId"s:
    35.  
      EventTriggerId mask = 0x80000000;
    36.  
      for (unsigned i = 0; i < MAX_NUM_EVENT_TRIGGERS; ++i) {
    37.  
      if ((eventTriggerId&mask) != 0) { //通过移位,然后与操作比对,找出要删除的元素
    38.  
      fTriggeredEventHandlers[i] = NULL;
    39.  
      fTriggeredEventClientDatas[i] = NULL;
    40.  
      }
    41.  
      mask >>= 1;
    42.  
      }
    43.  
      }
    44.  
      }
    45.  
       
    46.  
      void BasicTaskScheduler0::triggerEvent(EventTriggerId eventTriggerId, void* clientData) {
    47.  
      // First, record the "clientData":
    48.  
      if (eventTriggerId == fLastUsedTriggerMask) { // common-case optimization:
    49.  
      fTriggeredEventClientDatas[fLastUsedTriggerNum] = clientData;
    50.  
      } else {
    51.  
      EventTriggerId mask = 0x80000000;
    52.  
      for (unsigned i = 0; i < MAX_NUM_EVENT_TRIGGERS; ++i) {
    53.  
      if ((eventTriggerId&mask) != 0) {
    54.  
      fTriggeredEventClientDatas[i] = clientData; //事件触发时,传入函数的参数
    55.  
       
    56.  
      fLastUsedTriggerMask = mask;
    57.  
      fLastUsedTriggerNum = i;
    58.  
      }
    59.  
      mask >>= 1;
    60.  
      }
    61.  
      }
    62.  
       
    63.  
      // Then, note this event as being ready to be handled.
    64.  
      // (Note that because this function (unlike others in the library) can be called from an external thread, we do this last, to
    65.  
      // reduce the risk of a race condition.)
    66.  
      fTriggersAwaitingHandling |= eventTriggerId;
    67.  
      }

    最后在BasicTaskScheduler::SingleStep中执行该事件函数。

    三.socket handler

    先看下几个用到的结构体

    1.  
      class HandlerDescriptor {
    2.  
      HandlerDescriptor(HandlerDescriptor* nextHandler);
    3.  
      virtual ~HandlerDescriptor();
    4.  
       
    5.  
      public:
    6.  
      int socketNum;//套接字的序号;
    7.  
      int conditionSet; //socket状态,有SOCKET_READABLE,SOCKET_WRITABLE,SOCKET_EXCEPTION三种;
    8.  
      TaskScheduler::BackgroundHandlerProc* handlerProc;//事件执行的函数;
    9.  
      void* clientData;//事件执行的函数的参数;
    10.  
       
    11.  
      private:
    12.  
      // Descriptors are linked together in a doubly-linked list:
    13.  
      friend class HandlerSet;
    14.  
      friend class HandlerIterator;
    15.  
      HandlerDescriptor* fNextHandler;//下一个节点;
    16.  
      HandlerDescriptor* fPrevHandler;//上一个节点;
    17.  
      };
    18.  
       
    19.  
      //Handlerset主要实现了一个HandlerDescriptort的双向链表,并实现了对链表的插入,查找,删除,移动的操作;
    20.  
      class HandlerSet {
    21.  
      public:
    22.  
      HandlerSet();
    23.  
      virtual ~HandlerSet();
    24.  
       
    25.  
      void assignHandler(int socketNum, int conditionSet, TaskScheduler::BackgroundHandlerProc* handlerProc, void* clientData);//插入;
    26.  
      void clearHandler(int socketNum);//删除;
    27.  
      void moveHandler(int oldSocketNum, int newSocketNum);//移动;
    28.  
       
    29.  
      private:
    30.  
      HandlerDescriptor* lookupHandler(int socketNum);//查找;
    31.  
       
    32.  
      private:
    33.  
      friend class HandlerIterator;
    34.  
      HandlerDescriptor fHandlers;//HandlerDescriptort链表头部;
    35.  
      };
    36.  
       
    37.  
      //主要实现在HandlerSet中的迭代容器;
    38.  
      class HandlerIterator {
    39.  
      public:
    40.  
      HandlerIterator(HandlerSet& handlerSet);
    41.  
      virtual ~HandlerIterator();
    42.  
       
    43.  
      HandlerDescriptor* next(); //返回set中的下一个HandlerDescriptor,并保存当前的查找位置;
    44.  
      void reset();
    45.  
       
    46.  
      private:
    47.  
      HandlerSet& fOurSet;
    48.  
      HandlerDescriptor* fNextPtr;
    49.  
      };

    处理socket handler的函数:

      1.  
        void BasicTaskScheduler
      2.  
        ::setBackgroundHandling(int socketNum, int conditionSet, BackgroundHandlerProc* handlerProc, void* clientData) {
      3.  
        if (socketNum < 0) return;
      4.  
        FD_CLR((unsigned)socketNum, &fReadSet); //删除该套接字;
      5.  
        FD_CLR((unsigned)socketNum, &fWriteSet); //删除该套接字;
      6.  
        FD_CLR((unsigned)socketNum, &fExceptionSet); //删除该套接字;
      7.  
        if (conditionSet == 0) { //将该套接字对应的节点从链表中删除;
      8.  
        fHandlers->clearHandler(socketNum);
      9.  
        if (socketNum+1 == fMaxNumSockets) {
      10.  
        --fMaxNumSockets;
      11.  
        }
      12.  
        } else {
      13.  
        fHandlers->assignHandler(socketNum, conditionSet, handlerProc, clientData);//将该节点加入双向链表;
      14.  
        if (socketNum+1 > fMaxNumSockets) {
      15.  
        fMaxNumSockets = socketNum+1;
      16.  
        }
      17.  
        if (conditionSet&SOCKET_READABLE) FD_SET((unsigned)socketNum, &fReadSet);//加入可读集合;
      18.  
        if (conditionSet&SOCKET_WRITABLE) FD_SET((unsigned)socketNum, &fWriteSet);//加入可写集合;
      19.  
        if (conditionSet&SOCKET_EXCEPTION) FD_SET((unsigned)socketNum, &fExceptionSet);//加入异常集合;
      20.  
        }
      21.  
        }
      22.  
         
      23.  
        void BasicTaskScheduler::moveSocketHandling(int oldSocketNum, int newSocketNum) {
      24.  
        //先对套接字参数进行验证;
      25.  
        if (oldSocketNum < 0 || newSocketNum < 0) return; // sanity check
      26.  
        //根据老的套接字对应的集合,设置新套接字的集合;
      27.  
        if (FD_ISSET(oldSocketNum, &fReadSet)) {FD_CLR((unsigned)oldSocketNum, &fReadSet); FD_SET((unsigned)newSocketNum, &fReadSet);}
      28.  
        if (FD_ISSET(oldSocketNum, &fWriteSet)) {FD_CLR((unsigned)oldSocketNum, &fWriteSet); FD_SET((unsigned)newSocketNum, &fWriteSet);}
      29.  
        if (FD_ISSET(oldSocketNum, &fExceptionSet)) {FD_CLR((unsigned)oldSocketNum, &fExceptionSet); FD_SET((unsigned)newSocketNum, &fExceptionSet);}
      30.  
        //更新套接字;
      31.  
        fHandlers->moveHandler(oldSocketNum, newSocketNum);
      32.  
         
      33.  
        if (oldSocketNum+1 == fMaxNumSockets) {
      34.  
        --fMaxNumSockets;
      35.  
        }
      36.  
        if (newSocketNum+1 > fMaxNumSockets) {
      37.  
        fMaxNumSockets = newSocketNum+1;
      38.  
        }
      39.  
        }
  • 相关阅读:
    A Simple Problem with Integers-POJ3468 区间修改+区间查询
    致橡树
    OJ-Triangle
    Myeclipse+AJAX+Servlet
    opnet学习过程
    yii2在ubuntu下执行定时任务
    php创建文件夹后设置文件夹权限(转)
    “Request Entity Too Large” 上传图片出现大小限制
    慕课网,我的yii2学习笔记(基础篇)
    Yii2高级版本复制新项目出现问题解决(转)
  • 原文地址:https://www.cnblogs.com/lidabo/p/9467194.html
Copyright © 2020-2023  润新知