TimerQueue

int createTimerfd()
{
  int timerfd = ::timerfd_create(CLOCK_MONOTONIC,
                                 TFD_NONBLOCK | TFD_CLOEXEC);
  if (timerfd < 0)
  {
    LOG_SYSFATAL << "Failed in timerfd_create";
  }
  return timerfd;
}

struct timespec howMuchTimeFromNow(Timestamp when)
{
  int64_t microseconds = when.microSecondsSinceEpoch()
                         - Timestamp::now().microSecondsSinceEpoch();
  if (microseconds < 100)
  {
    microseconds = 100;
  }
  struct timespec ts;
  ts.tv_sec = static_cast<time_t>(
      microseconds / Timestamp::kMicroSecondsPerSecond);
  ts.tv_nsec = static_cast<long>(
      (microseconds % Timestamp::kMicroSecondsPerSecond) * 1000);
  return ts;
}

void readTimerfd(int timerfd, Timestamp now)
{
  uint64_t howmany;
  ssize_t n = ::read(timerfd, &howmany, sizeof howmany);
  LOG_TRACE << "TimerQueue::handleRead() " << howmany << " at " << now.toString();
  if (n != sizeof howmany)
  {
    LOG_ERROR << "TimerQueue::handleRead() reads " << n << " bytes instead of 8";
  }
}

void resetTimerfd(int timerfd, Timestamp expiration)
{
  // wake up loop by timerfd_settime()
  struct itimerspec newValue;
  struct itimerspec oldValue;
  memZero(&newValue, sizeof newValue);
  memZero(&oldValue, sizeof oldValue);
  newValue.it_value = howMuchTimeFromNow(expiration);
  int ret = ::timerfd_settime(timerfd, 0, &newValue, &oldValue);
  if (ret)
  {
    LOG_SYSERR << "timerfd_settime()";
  }
}

这几个函数使用的timerfd系统调用可以参看之前的博客timerfd 。createTimerfd()就是创建一个定时器,howMuchTimeFromNow()获取when参数与当前时间的差别,在resetTimerfd函数中使用。readTimerfd()读定时器;resetTimerfd()重新设置定时器的超时时间,expiration是绝对时间,所以通过howMuchTimeFromNow()获取相对时间。

TimerQueue::TimerQueue(EventLoop* loop)
  : loop_(loop),
    timerfd_(createTimerfd()),
    timerfdChannel_(loop, timerfd_),
    timers_(),
    callingExpiredTimers_(false)
{
  timerfdChannel_.setReadCallback(
      std::bind(&TimerQueue::handleRead, this));
  // we are always reading the timerfd, we disarm it with timerfd_settime.
  timerfdChannel_.enableReading();
}

TimerQueue::~TimerQueue()
{
  timerfdChannel_.disableAll();
  timerfdChannel_.remove();
  ::close(timerfd_);
  // do not remove channel, since we're in EventLoop::dtor();
  for (const Entry& timer : timers_)
  {
    delete timer.second;
  }
}

TimerId TimerQueue::addTimer(TimerCallback cb,
                             Timestamp when,
                             double interval)
{
  Timer* timer = new Timer(std::move(cb), when, interval);
  loop_->runInLoop(
      std::bind(&TimerQueue::addTimerInLoop, this, timer));
  return TimerId(timer, timer->sequence());
}

void TimerQueue::cancel(TimerId timerId)
{
  loop_->runInLoop(
      std::bind(&TimerQueue::cancelInLoop, this, timerId));
}

void TimerQueue::addTimerInLoop(Timer* timer)
{
  loop_->assertInLoopThread();
  bool earliestChanged = insert(timer);

  if (earliestChanged)
  {
    resetTimerfd(timerfd_, timer->expiration());
  }
}

void TimerQueue::cancelInLoop(TimerId timerId)
{
  loop_->assertInLoopThread();
  assert(timers_.size() == activeTimers_.size());
  ActiveTimer timer(timerId.timer_, timerId.sequence_);
  ActiveTimerSet::iterator it = activeTimers_.find(timer);
  if (it != activeTimers_.end())
  {
    size_t n = timers_.erase(Entry(it->first->expiration(), it->first));
    assert(n == 1); (void)n;
    delete it->first; // FIXME: no delete please
    activeTimers_.erase(it);
  }
  else if (callingExpiredTimers_)
  {
    cancelingTimers_.insert(timer);
  }
  assert(timers_.size() == activeTimers_.size());
}

void TimerQueue::handleRead()
{
  loop_->assertInLoopThread();
  Timestamp now(Timestamp::now());
  readTimerfd(timerfd_, now);

  std::vector<Entry> expired = getExpired(now);

  callingExpiredTimers_ = true;
  cancelingTimers_.clear();
  // safe to callback outside critical section
  for (const Entry& it : expired)
  {
    it.second->run();
  }
  callingExpiredTimers_ = false;

  reset(expired, now);
}

std::vector<TimerQueue::Entry> TimerQueue::getExpired(Timestamp now)
{
  assert(timers_.size() == activeTimers_.size());
  std::vector<Entry> expired;
  Entry sentry(now, reinterpret_cast<Timer*>(UINTPTR_MAX));
  TimerList::iterator end = timers_.lower_bound(sentry);
  assert(end == timers_.end() || now < end->first);
  std::copy(timers_.begin(), end, back_inserter(expired));
  timers_.erase(timers_.begin(), end);

  for (const Entry& it : expired)
  {
    ActiveTimer timer(it.second, it.second->sequence());
    size_t n = activeTimers_.erase(timer);
    assert(n == 1); (void)n;
  }

  assert(timers_.size() == activeTimers_.size());
  return expired;
}

void TimerQueue::reset(const std::vector<Entry>& expired, Timestamp now)
{
  Timestamp nextExpire;

  for (const Entry& it : expired)
  {
    ActiveTimer timer(it.second, it.second->sequence());
    if (it.second->repeat()
        && cancelingTimers_.find(timer) == cancelingTimers_.end())
    {
      it.second->restart(now);
      insert(it.second);
    }
    else
    {
      // FIXME move to a free list
      delete it.second; // FIXME: no delete please
    }
  }

  if (!timers_.empty())
  {
    nextExpire = timers_.begin()->second->expiration();
  }

  if (nextExpire.valid())
  {
    resetTimerfd(timerfd_, nextExpire);
  }
}

bool TimerQueue::insert(Timer* timer)
{
  loop_->assertInLoopThread();
  assert(timers_.size() == activeTimers_.size());
  bool earliestChanged = false;
  Timestamp when = timer->expiration();
  TimerList::iterator it = timers_.begin();
  if (it == timers_.end() || when < it->first)
  {
    earliestChanged = true;
  }
  {
    std::pair<TimerList::iterator, bool> result
      = timers_.insert(Entry(when, timer));
    assert(result.second); (void)result;
  }
  {
    std::pair<ActiveTimerSet::iterator, bool> result
      = activeTimers_.insert(ActiveTimer(timer, timer->sequence()));
    assert(result.second); (void)result;
  }

  assert(timers_.size() == activeTimers_.size());
  return earliestChanged;
}

TimerQueue是一个定时器管理类。timerfdChannel_是事件分发器,在定时器可读时会调用设置的回调函数TimerQueue::handleRead()。timers_是定时器列表,但实际上timers_的定时功能都是通过createTimerfd创建的定时器实现的。也就是说timers_本身只包含时间,没有定时功能。但之后仍然会把timers_作为定时器来分析。

timers_是一个std::set<Entry>类型的对象,Entry是个std::pair<Timestamp, Timer*>类型,这个类型比较有趣,因为Timestamp作为一个时间戳,是有可能出现重复的,所以只使用时间戳无法处理重复的情况,std::pair<Timestamp, Timer*>增加了一个Timer类型的指针,该指针一定是唯一的,这样就可以避免重复,还可以通过时间戳进行排序,还有一点,在TimerQueue::getExpired()中作者使用Entry sentry(now, reinterpret_cast<Timer*>(UINTPTR_MAX))来表示当前的一个最大值,可以取出到期的所有定时器。

TimerQueue::addTimer()增加定时器,loop_->runInLoop的意思就是把TimerQueue::addTimerInLoop()函数放到TimerQueue对象所属EventLoop所在的线程去执行,这样就不会出现多线程问题。TimerQueue::addTimerInLoop()中调用insert实现插入定时器列表,并且返回一个bool类型,表示新添加的这个定时器是否比之前定时器列表中的定时器到期时间更早,如果更早,需要通过resetTimerfd()更新到期时间。因为timerfd_所指代的定时器的到期时间就是timers_中最早到期的时间。

在TimerQueue::insert()中,首先判断新添加的定时器是否为更早到期的定时器,然后插入timers_中,并且还要加入activeTimers_表示当前存活的定时器。

TimerQueue::cancel()取消某个定时器,使用的参数就是上面的TimerQueue::addTimer()返回的信息,这个函数跟TimerQueue::addTimer()一样,也是通过loop_->runInLoop来避免多线程问题,TimerQueue::cancelInLoop()中,首先在当前存活的定时器中查找,如果存在,就直接删除了。但是有一种可能性,就是该定时器已经不再activeTimers_中了,但是它仍然有可能继续存活,为了避免产生这种情况,把定时器放到cancelingTimers_中,表示要删除。

上面说定时器不在activeTimers_中了,但是它仍然有可能继续存活。为什么呢??将要删除的定时器加入cancelingTimers_的条件是callingExpiredTimers_为真,那么什么时候callingExpiredTimers_为真呢?在TimerQueue::handleRead()中获取了到期的定时器并准备处理定时器时为真,也就说在callingExpiredTimers_为真期间,it.second->run()中的回调函数执行了取消定时器的操作。而在这个期间,由于前面先调用了TimerQueue::getExpired()函数。该函数会获取所有的到期定时器。并将所有过期的定时器都从activeTimers_中删除了。那么在it.second->run()中执行的删除操作如果删除的是到期定时器则会失败,因为该定时器已经不在activeTimers_中了。但是这些到期的定时器是可以复活的。在TimerQueue::handleRead()的最后调用TimerQueue::reset()。在TimerQueue::reset()中,会判断到期的定时器是否为重复的定时器,如果这时候没有cancelingTimers_,TimerQueue::reset()会将之前明明应该删除的重复定时器复活。这就是为什么需要cancelingTimers_的原因。

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值