今天抽空研究了下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.  
      }

live555源码学习笔记之TaskScheduler的更多相关文章

  1. Spring源码学习笔记12——总结篇,IOC,Bean的生命周期,三大扩展点

    Spring源码学习笔记12--总结篇,IOC,Bean的生命周期,三大扩展点 参考了Spring 官网文档 https://docs.spring.io/spring-framework/docs/ ...

  2. Underscore.js 源码学习笔记(下)

    上接 Underscore.js 源码学习笔记(上) === 756 行开始 函数部分. var executeBound = function(sourceFunc, boundFunc, cont ...

  3. Underscore.js 源码学习笔记(上)

    版本 Underscore.js 1.9.1 一共 1693 行.注释我就删了,太长了… 整体是一个 (function() {...}());  这样的东西,我们应该知道这是一个 IIFE(立即执行 ...

  4. AXI_LITE源码学习笔记

    AXI_LITE源码学习笔记 1. axi_awready信号的产生 准备接收写地址信号 // Implement axi_awready generation // axi_awready is a ...

  5. Hadoop源码学习笔记(6)——从ls命令一路解剖

    Hadoop源码学习笔记(6) ——从ls命令一路解剖 Hadoop几个模块的程序我们大致有了点了解,现在我们得细看一下这个程序是如何处理命令的. 我们就从原头开始,然后一步步追查. 我们先选中ls命 ...

  6. Hadoop源码学习笔记(5) ——回顾DataNode和NameNode的类结构

    Hadoop源码学习笔记(5) ——回顾DataNode和NameNode的类结构 之前我们简要的看过了DataNode的main函数以及整个类的大至,现在结合前面我们研究的线程和RPC,则可以进一步 ...

  7. Hadoop源码学习笔记(4) ——Socket到RPC调用

    Hadoop源码学习笔记(4) ——Socket到RPC调用 Hadoop是一个分布式程序,分布在多台机器上运行,事必会涉及到网络编程.那这里如何让网络编程变得简单.透明的呢? 网络编程中,首先我们要 ...

  8. Hadoop源码学习笔记(3) ——初览DataNode及学习线程

    Hadoop源码学习笔记(3) ——初览DataNode及学习线程 进入了main函数,我们走出了第一步,接下来看看再怎么走: public class DataNode extends Config ...

  9. Hadoop源码学习笔记(2) ——进入main函数打印包信息

    Hadoop源码学习笔记(2) ——进入main函数打印包信息 找到了main函数,也建立了快速启动的方法,然后我们就进去看一看. 进入NameNode和DataNode的主函数后,发现形式差不多: ...

随机推荐

  1. PHP黑魔法(该篇文章转自:http://www.91ri.org/12634.html 目的是作为自己的笔记方便查找)

    那些年我们学过的PHP黑魔法 作者:Matrix_ling 序 这里必须得说一下==和===这俩货的重要性.==是比较运算,它不会去检查条件式的表达式的类型===是恒等,它会检查查表达式的值与类型是否 ...

  2. GinKgoCTF-Crypto

    一:古典密码 一只小羊跳过了栅栏,密码如下: GpKlCeT_FC{rAy_pStiym} 栅栏解密——>11栏 二:滴滴滴 --./-.-/-.-./-/..-./----.--/-../.. ...

  3. 《DSP using MATLAB》Problem 6.1

    今早不知道怎么5点就醒了,起来喝了口水,走到阳台,看看窗外,远处高楼上也有灯亮着,也许已经开始新的一天. 今天开始第6章了,继续努力.

  4. 使用JQuery提交表单的两种方式选择

    有一个表单,如果使用JQuery提交的话,可以使用下面2中方式,但他们的区别却是根据实际需求需要进行选择的. 第一种:表单按照action路径提交后,页面会刷新. $("#id") ...

  5. LeetCode - Subtree of Another Tree

    Given two non-empty binary trees s and t, check whether tree t has exactly the same structure and no ...

  6. Singer 学习五 docker 运行说明

    介绍过一个工具knots ,方便Singer 可视化开发的工具,但是默认这个工具包含的tap 以及target 比较少(可以自己扩展) 同时这个工具就是基于docker 运行的 docker 运行的几 ...

  7. IAR intrinsic functions

    You can insert asm code example asm("NOP") into the c or c++ source code to get a good per ...

  8. WikiBooks/Cg Programming

    https://en.wikibooks.org/wiki/Cg_Programming Basics Minimal Shader(about shaders, materials, and gam ...

  9. str_replace中的匹配空白符,必须用双引号

    例: $minUnit = str_replace(array('\r','\n'),"",$content); 执行上面的语句,你会发现,文本没有任何变化,该换行的地方还是换行. ...

  10. Redis&MongoDB&Zookeeper&Kafka

    目录 Redis MongoDB Zookeeper Kafka Redis 概念 Redis是NoSQL中比较常典型的一个非关系型数据库,在日常工作中也是最为常见的.Redis是一个由C语言编写的开 ...