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 :
- class DelayQueueEntry {
- public:
- virtual ~DelayQueueEntry();
- intptr_t token() {
- return fToken;
- }
- protected: // abstract base class
- DelayQueueEntry(DelayInterval delay);
- virtual void handleTimeout(); //执行超时任务;
- private:
- friend class DelayQueue;
- DelayQueueEntry* fNext; //后一个对象
- DelayQueueEntry* fPrev; //前一个对象
- DelayInterval fDeltaTimeRemaining; 超时时间(倒计时),该结构体含两个参数,一个是秒,一个是微秒;
- intptr_t fToken; //游标,方便查表
- static intptr_t tokenCounter; //队列计数器;
- };
- class DelayQueue: public DelayQueueEntry {
- public:
- DelayQueue();
- virtual ~DelayQueue();
- void addEntry(DelayQueueEntry* newEntry); // returns a token for the entry
- void updateEntry(DelayQueueEntry* entry, DelayInterval newDelay); //更新一个任务的超时时间
- void updateEntry(intptr_t tokenToFind, DelayInterval newDelay); //通过游标查找某个任务,再更新其超时时间
- void removeEntry(DelayQueueEntry* entry); // but doesn't delete it //将某个任务从队列从移除,但是不销毁该对象
- DelayQueueEntry* removeEntry(intptr_t tokenToFind); // but doesn't delete it /通过游标,将某个任务从队列从移除,但是不销毁该对象
- DelayInterval const& timeToNextAlarm(); //倒计时还剩多久时间
- void handleAlarm(); //将超时的任务移除,然后执行
- private:
- DelayQueueEntry* head() { return fNext; }
- DelayQueueEntry* findEntryByToken(intptr_t token);
- void synchronize(); //更新超时时间
- EventTime fLastSyncTime;
- };
- class AlarmHandler: public DelayQueueEntry {
- public:
- AlarmHandler(TaskFunc* proc, void* clientData, DelayInterval timeToDelay)
- : DelayQueueEntry(timeToDelay), fProc(proc), fClientData(clientData) {
- }
- private: // redefined virtual functions
- virtual void handleTimeout() {
- (*fProc)(fClientData); //通过调用函数指针 +参数 执行任务
- DelayQueueEntry::handleTimeout();
- }
- private:
- TaskFunc* fProc; //delaytask的函数指针
- void* fClientData; //delaytask的函数所需要的参数
- };
操作该队列的方法在BasicTaskScheduler0.h、BasicTaskScheduler0.cpp 中声明和实现:
- TaskToken BasicTaskScheduler0::scheduleDelayedTask(int64_t microseconds, TaskFunc* proc, void* clientData) {
- if (microseconds < 0) microseconds = 0;
- DelayInterval timeToDelay((long)(microseconds/1000000), (long)(microseconds%1000000));
- AlarmHandler* alarmHandler = new AlarmHandler(proc, clientData, timeToDelay); //创建一个AlarmHandler对象;
- fDelayQueue.addEntry(alarmHandler); //加入任务队列中;
- return (void*)(alarmHandler->token());//返回该任务在队列中的游标
- }
- void BasicTaskScheduler0::unscheduleDelayedTask(TaskToken& prevTask) {
- DelayQueueEntry* alarmHandler = fDelayQueue.removeEntry((intptr_t)prevTask);//根据任务对象的游标,将任务移除队列
- prevTask = NULL;
- delete alarmHandler;//销毁对象
- }
二.event handler
event handler是被存在数组中。数组大小固定等于32(#define MAX_NUM_EVENT_TRIGGERS 32),用EventTriggerId来表示数组中的项,EventTriggerId是一个32位整数,因为数组是32项,所以用EventTriggerId中的第n位置为1,则表明对应数组中的第n项。成员变量fTriggersAwaitingHandling也是EventTriggerId类型,它里面置1的那些位对应了数组中所有需要处理的项。这样做节省了内存和计算,但降低了可读性,呵呵,而且也不够灵活,只能支持32项或64项,其它数量不被支持。
- EventTriggerId BasicTaskScheduler0::createEventTrigger(TaskFunc* eventHandlerProc) {
- unsigned i = fLastUsedTriggerNum;
- EventTriggerId mask = fLastUsedTriggerMask;
- do {
- i = (i+1)%MAX_NUM_EVENT_TRIGGERS; //序号加一
- mask >>= 1; //mask右移一位,代表一个新的序号
- if (mask == 0) mask = 0x80000000; //默认为第32位为1
- if (fTriggeredEventHandlers[i] == NULL) {//如果数组的该位置没有存入数据,则将函数指针存入fTriggeredEventHandlers,数据位置空,触发事件时传入
- // This trigger number is free; use it:
- fTriggeredEventHandlers[i] = eventHandlerProc;
- fTriggeredEventClientDatas[i] = NULL; // sanity
- fLastUsedTriggerMask = mask; //记录最新的Mask和序号
- fLastUsedTriggerNum = i;
- return mask; //分配成功,返回项数
- }
- } while (i != fLastUsedTriggerNum);
- // All available event triggers are allocated; return 0 instead:
- return 0;
- }
- void BasicTaskScheduler0::deleteEventTrigger(EventTriggerId eventTriggerId) {
- fTriggersAwaitingHandling &=~ eventTriggerId;
- if (eventTriggerId == fLastUsedTriggerMask) { // common-case optimization:如果删除的事件正好是最后一个非空项,则直接将函数指针和参数置空;
- fTriggeredEventHandlers[fLastUsedTriggerNum] = NULL;
- fTriggeredEventClientDatas[fLastUsedTriggerNum] = NULL;
- } else {
- // "eventTriggerId" should have just one bit set.
- // However, we do the reasonable thing if the user happened to 'or' together two or more "EventTriggerId"s:
- EventTriggerId mask = 0x80000000;
- for (unsigned i = 0; i < MAX_NUM_EVENT_TRIGGERS; ++i) {
- if ((eventTriggerId&mask) != 0) { //通过移位,然后与操作比对,找出要删除的元素
- fTriggeredEventHandlers[i] = NULL;
- fTriggeredEventClientDatas[i] = NULL;
- }
- mask >>= 1;
- }
- }
- }
- void BasicTaskScheduler0::triggerEvent(EventTriggerId eventTriggerId, void* clientData) {
- // First, record the "clientData":
- if (eventTriggerId == fLastUsedTriggerMask) { // common-case optimization:
- fTriggeredEventClientDatas[fLastUsedTriggerNum] = clientData;
- } else {
- EventTriggerId mask = 0x80000000;
- for (unsigned i = 0; i < MAX_NUM_EVENT_TRIGGERS; ++i) {
- if ((eventTriggerId&mask) != 0) {
- fTriggeredEventClientDatas[i] = clientData; //事件触发时,传入函数的参数
- fLastUsedTriggerMask = mask;
- fLastUsedTriggerNum = i;
- }
- mask >>= 1;
- }
- }
- // Then, note this event as being ready to be handled.
- // (Note that because this function (unlike others in the library) can be called from an external thread, we do this last, to
- // reduce the risk of a race condition.)
- fTriggersAwaitingHandling |= eventTriggerId;
- }
最后在BasicTaskScheduler::SingleStep中执行该事件函数。
三.socket handler
先看下几个用到的结构体
- class HandlerDescriptor {
- HandlerDescriptor(HandlerDescriptor* nextHandler);
- virtual ~HandlerDescriptor();
- public:
- int socketNum;//套接字的序号;
- int conditionSet; //socket状态,有SOCKET_READABLE,SOCKET_WRITABLE,SOCKET_EXCEPTION三种;
- TaskScheduler::BackgroundHandlerProc* handlerProc;//事件执行的函数;
- void* clientData;//事件执行的函数的参数;
- private:
- // Descriptors are linked together in a doubly-linked list:
- friend class HandlerSet;
- friend class HandlerIterator;
- HandlerDescriptor* fNextHandler;//下一个节点;
- HandlerDescriptor* fPrevHandler;//上一个节点;
- };
- //Handlerset主要实现了一个HandlerDescriptort的双向链表,并实现了对链表的插入,查找,删除,移动的操作;
- class HandlerSet {
- public:
- HandlerSet();
- virtual ~HandlerSet();
- void assignHandler(int socketNum, int conditionSet, TaskScheduler::BackgroundHandlerProc* handlerProc, void* clientData);//插入;
- void clearHandler(int socketNum);//删除;
- void moveHandler(int oldSocketNum, int newSocketNum);//移动;
- private:
- HandlerDescriptor* lookupHandler(int socketNum);//查找;
- private:
- friend class HandlerIterator;
- HandlerDescriptor fHandlers;//HandlerDescriptort链表头部;
- };
- //主要实现在HandlerSet中的迭代容器;
- class HandlerIterator {
- public:
- HandlerIterator(HandlerSet& handlerSet);
- virtual ~HandlerIterator();
- HandlerDescriptor* next(); //返回set中的下一个HandlerDescriptor,并保存当前的查找位置;
- void reset();
- private:
- HandlerSet& fOurSet;
- HandlerDescriptor* fNextPtr;
- };
处理socket handler的函数:
- void BasicTaskScheduler
- ::setBackgroundHandling(int socketNum, int conditionSet, BackgroundHandlerProc* handlerProc, void* clientData) {
- if (socketNum < 0) return;
- FD_CLR((unsigned)socketNum, &fReadSet); //删除该套接字;
- FD_CLR((unsigned)socketNum, &fWriteSet); //删除该套接字;
- FD_CLR((unsigned)socketNum, &fExceptionSet); //删除该套接字;
- if (conditionSet == 0) { //将该套接字对应的节点从链表中删除;
- fHandlers->clearHandler(socketNum);
- if (socketNum+1 == fMaxNumSockets) {
- --fMaxNumSockets;
- }
- } else {
- fHandlers->assignHandler(socketNum, conditionSet, handlerProc, clientData);//将该节点加入双向链表;
- if (socketNum+1 > fMaxNumSockets) {
- fMaxNumSockets = socketNum+1;
- }
- if (conditionSet&SOCKET_READABLE) FD_SET((unsigned)socketNum, &fReadSet);//加入可读集合;
- if (conditionSet&SOCKET_WRITABLE) FD_SET((unsigned)socketNum, &fWriteSet);//加入可写集合;
- if (conditionSet&SOCKET_EXCEPTION) FD_SET((unsigned)socketNum, &fExceptionSet);//加入异常集合;
- }
- }
- void BasicTaskScheduler::moveSocketHandling(int oldSocketNum, int newSocketNum) {
- //先对套接字参数进行验证;
- if (oldSocketNum < 0 || newSocketNum < 0) return; // sanity check
- //根据老的套接字对应的集合,设置新套接字的集合;
- if (FD_ISSET(oldSocketNum, &fReadSet)) {FD_CLR((unsigned)oldSocketNum, &fReadSet); FD_SET((unsigned)newSocketNum, &fReadSet);}
- if (FD_ISSET(oldSocketNum, &fWriteSet)) {FD_CLR((unsigned)oldSocketNum, &fWriteSet); FD_SET((unsigned)newSocketNum, &fWriteSet);}
- if (FD_ISSET(oldSocketNum, &fExceptionSet)) {FD_CLR((unsigned)oldSocketNum, &fExceptionSet); FD_SET((unsigned)newSocketNum, &fExceptionSet);}
- //更新套接字;
- fHandlers->moveHandler(oldSocketNum, newSocketNum);
- if (oldSocketNum+1 == fMaxNumSockets) {
- --fMaxNumSockets;
- }
- if (newSocketNum+1 > fMaxNumSockets) {
- fMaxNumSockets = newSocketNum+1;
- }
- }
live555源码学习笔记之TaskScheduler的更多相关文章
- Spring源码学习笔记12——总结篇,IOC,Bean的生命周期,三大扩展点
Spring源码学习笔记12--总结篇,IOC,Bean的生命周期,三大扩展点 参考了Spring 官网文档 https://docs.spring.io/spring-framework/docs/ ...
- Underscore.js 源码学习笔记(下)
上接 Underscore.js 源码学习笔记(上) === 756 行开始 函数部分. var executeBound = function(sourceFunc, boundFunc, cont ...
- Underscore.js 源码学习笔记(上)
版本 Underscore.js 1.9.1 一共 1693 行.注释我就删了,太长了… 整体是一个 (function() {...}()); 这样的东西,我们应该知道这是一个 IIFE(立即执行 ...
- AXI_LITE源码学习笔记
AXI_LITE源码学习笔记 1. axi_awready信号的产生 准备接收写地址信号 // Implement axi_awready generation // axi_awready is a ...
- Hadoop源码学习笔记(6)——从ls命令一路解剖
Hadoop源码学习笔记(6) ——从ls命令一路解剖 Hadoop几个模块的程序我们大致有了点了解,现在我们得细看一下这个程序是如何处理命令的. 我们就从原头开始,然后一步步追查. 我们先选中ls命 ...
- Hadoop源码学习笔记(5) ——回顾DataNode和NameNode的类结构
Hadoop源码学习笔记(5) ——回顾DataNode和NameNode的类结构 之前我们简要的看过了DataNode的main函数以及整个类的大至,现在结合前面我们研究的线程和RPC,则可以进一步 ...
- Hadoop源码学习笔记(4) ——Socket到RPC调用
Hadoop源码学习笔记(4) ——Socket到RPC调用 Hadoop是一个分布式程序,分布在多台机器上运行,事必会涉及到网络编程.那这里如何让网络编程变得简单.透明的呢? 网络编程中,首先我们要 ...
- Hadoop源码学习笔记(3) ——初览DataNode及学习线程
Hadoop源码学习笔记(3) ——初览DataNode及学习线程 进入了main函数,我们走出了第一步,接下来看看再怎么走: public class DataNode extends Config ...
- Hadoop源码学习笔记(2) ——进入main函数打印包信息
Hadoop源码学习笔记(2) ——进入main函数打印包信息 找到了main函数,也建立了快速启动的方法,然后我们就进去看一看. 进入NameNode和DataNode的主函数后,发现形式差不多: ...
随机推荐
- VS、ReSharper 设置修改代码颜色、提高代码辨识度!附VS超实用快捷!
ReSharper 配置代码颜色 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- 心 ...
- 1px的实现方法
1.用伪元素实现 .border_1px_top{ position:relative;} .border_1px_top:before{ content: ""; positio ...
- PTA——数列求和
PTA 7-34 求分数序列前N项和 #include<stdio.h> int main() { int i,n; ,fm = ,sum = ; scanf("%d" ...
- 《DSP using MATLAB》Problem 5.34
第1小题 代码: %% ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ...
- jsp中引入jquery报错:Failed to load resource: the server responded with a status of 404 (Not Found)
问题描述: 今天自己在搭建spring.springMVC.hibernate框架,搭建完成后,在引入jquery时,发现jquery不管用.我的解决顺序是: 1.检查路径,发现路径没错,另外需要注意 ...
- Singer 学习五 docker 运行说明
介绍过一个工具knots ,方便Singer 可视化开发的工具,但是默认这个工具包含的tap 以及target 比较少(可以自己扩展) 同时这个工具就是基于docker 运行的 docker 运行的几 ...
- Gravitee.io api gateway 试用
以前写过几篇关于整体介绍的以及 使用docker 运行的简单说明,有了docker-compose 环境我们可以 方便的进行测试使用了. 环境准备 docker-compose 文件 versio ...
- Python __name__变量
原文: http://blog.csdn.net/u011511601/article/details/53504355 Python使用缩进对齐组织代码的执行,所有没有缩进的代码,都会在载入时自动执 ...
- FastAdmin Git 开发更新流程
更加简洁的流程 FastAdmin 使用 Git 更新的新用法 https://www.cnblogs.com/F4NNIU/p/9120365.html
- jumpserver修改默认管理员账号名
1.安装完毕jumpserver之后,默认管理员账号为admin 显然类似windows的administrator以及linux的root 把账号名改成别的 个人信息界面点击设置 修改为自己想要的用 ...