今天抽空研究了下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. Hive错误:java.net.ConnectException: Connection refused: connect

    修改hive配置文件 hive-site.xml,加入如下配置项: <property> <name>hive.server2.thrift.port</name> ...

  2. Python字符集

    字符集: 美国:ASCII      需要8bit表示     英文字母一个字节,不支持中文中国:GBK                           英文字母一个字节,汉字占两个字节万国:un ...

  3. Win-Lin双系统重装Windows找回Linux启动

    第一系统Windows,第二系统Linux:Ubuntu18.10: 1. 重新安装Windows系统后,使用Ubuntu的安装光盘,或启动U盘启动电脑:2. 选择:Try Ubuntu ;3. 进入 ...

  4. 03 重定向,请求转发,cookie,session

    重定向: /* 之前的写法 response.setStatus(302); response.setHeader("Location", "login_success. ...

  5. 05 面向对象:构造方法&static&继承&方法 &final

    构造方法及其重载: /* 构造方法格式特点 * a:方法名与类名相同(大小也要与类名一致) * b:没有返回值类型,连void都没有 * c:没有具体的返回值return; * 构造方法的重载 * 重 ...

  6. [小A与最大子段和][斜率优化dp+二分]

    链接:https://ac.nowcoder.com/acm/contest/545/A来源:牛客网题目描述 小A在网上看到了 "最大子段和" 问题的解法.第二天,小A向小B讲解了 ...

  7. 使用Maven插件启动tomcat服务

    新建maven web项目,首先保证maven环境OK,maven项目能正常install1.pom.xml文件配置如下: <build> <pluginManagement> ...

  8. mongodb副本集加分片集群安全认证使用账号密码登录

    mongodb副本集加分片集群搭建网上资料有很多.粘贴一个写的比较好的.副本集加分片搭建 对于搭建好的mongodb副本集加分片集群,为了安全,启动安全认证,使用账号密码登录. 默认的mongodb是 ...

  9. Windows和linux 文件互传

    一般我是直接在windows下创建一个共享文件夹,然后在linux挂载,我一直都是这么干的. 直到有一天,我需要往一个公网linux上面传几个文件,可是我的Windows是内网的,所以,这种方法行不通 ...

  10. mysql新建用户在本地无法登录

    新建了一个mysql用户,但是无法在本地登录,即使已经授权任一ip都可以登录,甚至特地写清楚localhost登录,还是不行,情况如下 [root@localhost zabbix-release-3 ...