live555源码分析(一)基础环境

  废话不多说,先拿调度器开刀。

  BasicTaskScheduler(继承自BasicTaskScheduler0)是live555实现的一个基础调度器类,其成员doEventLoop函数用来处理循环事件,看源码:

void BasicTaskScheduler0::doEventLoop(char volatile* watchVariable) {
  // Repeatedly loop, handling readble sockets and timed events:
  while (1) {
    if (watchVariable != NULL && *watchVariable != 0) break;
    SingleStep();
  }
}

  从架构设计上来说,具体处理由子类实现的虚函数SingleStep完成,BasicTaskScheduler::SingleStep首先检查select有无需要处理的事件,类中还有个延时队列(fDelayQueue),实现一个粗略的单次定时任务,所以select的最大等待时间不能大于延时任务的超时时间。

  DelayInterval const& timeToDelay = fDelayQueue.timeToNextAlarm();
  struct timeval tv_timeToDelay;
  tv_timeToDelay.tv_sec = timeToDelay.seconds();
  tv_timeToDelay.tv_usec = timeToDelay.useconds();
  …………
  int selectResult = select(fMaxNumSockets, &readSet, &writeSet, &exceptionSet, &tv_timeToDelay);
  …………
  fDelayQueue.handleAlarm();

  类内有处理socket事件的集合(fHandlers),通过迭代器找到与select返回相匹配第一个处理项,并调用其处理函数,查找方法上先从上一次成功执行项的下一项开始逐步遍历到末尾,如果没找到,重头到尾再来一回,这样做算是把部分优化和实现简单综合考虑的结果。

  // Call the handler function for one readable socket:
  HandlerIterator iter(*fHandlers);
  HandlerDescriptor* handler;
  // To ensure forward progress through the handlers, begin past the last
  // socket number that we handled:
  if (fLastHandledSocketNum >= 0) {
    while ((handler = iter.next()) != NULL) {
      if (handler->socketNum == fLastHandledSocketNum) break;
    }
    if (handler == NULL) {
      fLastHandledSocketNum = -1;
      iter.reset(); // start from the beginning instead
    }
  }

  while ((handler = iter.next()) != NULL) {
    int sock = handler->socketNum; // alias
    int resultConditionSet = 0;
    if (FD_ISSET(sock, &readSet) && FD_ISSET(sock, &fReadSet)/*sanity check*/)     
      resultConditionSet |= SOCKET_READABLE;
    if (FD_ISSET(sock, &writeSet) && FD_ISSET(sock, &fWriteSet)/*sanity check*/) 
      resultConditionSet |= SOCKET_WRITABLE;
    if (FD_ISSET(sock, &exceptionSet) && FD_ISSET(sock, &fExceptionSet)/*sanity check*/) 
      resultConditionSet |= SOCKET_EXCEPTION;
    if ((resultConditionSet&handler->conditionSet) != 0 && handler->handlerProc != NULL) 
    {
      fLastHandledSocketNum = sock;
      // Note: we set "fLastHandledSocketNum" before calling the handler,
      // in case the handler calls "doEventLoop()" reentrantly.
      (*handler->handlerProc)(handler->clientData, resultConditionSet);
      break;
    }
  }

  if (handler == NULL && fLastHandledSocketNum >= 0) {
    // We didn't call a handler, but we didn't get to check all of them,
    // so try again from the beginning:
    iter.reset();
    while ((handler = iter.next()) != NULL) {
      int sock = handler->socketNum; // alias
      int resultConditionSet = 0;
      if (FD_ISSET(sock, &readSet) && FD_ISSET(sock, &fReadSet)/*sanity check*/) 
        resultConditionSet |= SOCKET_READABLE;
      if (FD_ISSET(sock, &writeSet) && FD_ISSET(sock, &fWriteSet)/*sanity check*/) 
        resultConditionSet |= SOCKET_WRITABLE;
      if (FD_ISSET(sock, &exceptionSet) && FD_ISSET(sock, &fExceptionSet)/*sanity check*/)
        resultConditionSet |= SOCKET_EXCEPTION;
      if ((resultConditionSet&handler->conditionSet) != 0 && handler->handlerProc != NULL) {
	    fLastHandledSocketNum = sock;
	    // Note: we set "fLastHandledSocketNum" before calling the handler,
        // in case the handler calls "doEventLoop()" reentrantly.
	    (*handler->handlerProc)(handler->clientData, resultConditionSet);
	    break;
      }
    }
    if (handler == NULL) fLastHandledSocketNum = -1;//because we didn't call a handler
  }

  接着检查是否有触发事件信号(fTriggersAwaitingHandling,32位掩码),调度器提供了createEventTrigger注册事件函数,triggerEvent用来触发事件执行。fTriggeredEventHandlers、fTriggeredEventClientDatas数组中保存着这些函数及参数。查找过程中对单次同一事件连续触发作了一点优化处理。

  // Also handle any newly-triggered event (Note that we do this *after* calling a socket handler,
  // in case the triggered event handler modifies The set of readable sockets.)
  if (fTriggersAwaitingHandling != 0) {
    if (fTriggersAwaitingHandling == fLastUsedTriggerMask) {
      // Common-case optimization for a single event trigger:
      fTriggersAwaitingHandling &=~ fLastUsedTriggerMask;
      if (fTriggeredEventHandlers[fLastUsedTriggerNum] != NULL) {
	    (*fTriggeredEventHandlers[fLastUsedTriggerNum])(fTriggeredEventClientDatas[fLastUsedTriggerNum]);
      }
    } else {
      // Look for an event trigger that needs handling (making sure that we make forward progress through all possible triggers):
      unsigned i = fLastUsedTriggerNum;
      EventTriggerId mask = fLastUsedTriggerMask;

      do {
	    i = (i+1)%MAX_NUM_EVENT_TRIGGERS;
	    mask >>= 1;
	    if (mask == 0) mask = 0x80000000;

	    if ((fTriggersAwaitingHandling&mask) != 0) {
	      fTriggersAwaitingHandling &=~ mask;
	      if (fTriggeredEventHandlers[i] != NULL) {
	        (*fTriggeredEventHandlers[i])(fTriggeredEventClientDatas[i]);
	      }

	      fLastUsedTriggerMask = mask;
	      fLastUsedTriggerNum = i;
	      break;
	    }
      } while (i != fLastUsedTriggerNum);
    }
  }

  最后介绍一下使用环境类BasicUsageEnvironment,主要用途给环境输出一些错误信息,live555的默认实现向stderr终端打印。如果准备写个有UI的程序,可以重载子类处理显示这些信息。环境类构造时需要传递保存调器度,这样通过环境类也能引用到调度器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值