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;
  }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值