EventLoop

EventLoop事件循环(反应器Reactor),每个线程只能有一个EventLoop实体,它负责IO和定时器时间的分派。

int createEventfd()
{
  int evtfd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
  if (evtfd < 0)
  {
    LOG_SYSERR << "Failed in eventfd";
    abort();
  }
  return evtfd;
}

EventLoop中涉及的eventfd部分,在之前的博客中有说明,这里不再分析了。

#pragma GCC diagnostic ignored "-Wold-style-cast"
class IgnoreSigPipe
{
 public:
  IgnoreSigPipe()
  {
    ::signal(SIGPIPE, SIG_IGN);
    // LOG_TRACE << "Ignore SIGPIPE";
  }
};
#pragma GCC diagnostic error "-Wold-style-cast"

IgnoreSigPipe initObj;

使用全局变量设置SIGPIPE,SIGPIPE的默认行为是终止进程,这意味着如果对方断开连接而本地继续写入的话,会造成服务进程意外退出。

EventLoop::EventLoop()
  : looping_(false),
    quit_(false),
    eventHandling_(false),
    callingPendingFunctors_(false),
    iteration_(0),
    threadId_(CurrentThread::tid()),
    poller_(Poller::newDefaultPoller(this)),
    timerQueue_(new TimerQueue(this)),
    wakeupFd_(createEventfd()),
    wakeupChannel_(new Channel(this, wakeupFd_)),
    currentActiveChannel_(NULL)
{
  LOG_DEBUG << "EventLoop created " << this << " in thread " << threadId_;
  if (t_loopInThisThread)
  {
    LOG_FATAL << "Another EventLoop " << t_loopInThisThread
              << " exists in this thread " << threadId_;
  }
  else
  {
    t_loopInThisThread = this;
  }
  wakeupChannel_->setReadCallback(
      std::bind(&EventLoop::handleRead, this));
  // we are always reading the wakeupfd
  wakeupChannel_->enableReading();
}

EventLoop::~EventLoop()
{
  LOG_DEBUG << "EventLoop " << this << " of thread " << threadId_
            << " destructs in thread " << CurrentThread::tid();
  wakeupChannel_->disableAll();
  wakeupChannel_->remove();
  ::close(wakeupFd_);
  t_loopInThisThread = NULL;
}

在构造函数中,初始化了很多变量,其中CurrentThread、Poller、TimerQueue、Channel之前的博客分析过,所以不再详细说明。

在构造函数中,有一个关于t_loopInThisThread的判断,是为了防止在一个线程中创建多个EventLoop实例。之后设置了wakeupChannel_可读回调函数,并添加到事件循环中,这样可以在需要时唤醒EventLoop。在之后的分析中会将该EventLoop实例和创建该实例的线程绑定,即创建EventLoop实例的线程就是该实例所在线程或所属线程。

void EventLoop::loop()
{
  assert(!looping_);
  assertInLoopThread();
  looping_ = true;
  quit_ = false;  // FIXME: what if someone calls quit() before loop() ?
  LOG_TRACE << "EventLoop " << this << " start looping";

  while (!quit_)
  {
    activeChannels_.clear();
    pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);
    ++iteration_;
    if (Logger::logLevel() <= Logger::TRACE)
    {
      printActiveChannels();
    }
    // TODO sort channel by priority
    eventHandling_ = true;
    for (Channel* channel : activeChannels_)
    {
      currentActiveChannel_ = channel;
      currentActiveChannel_->handleEvent(pollReturnTime_);
    }
    currentActiveChannel_ = NULL;
    eventHandling_ = false;
    doPendingFunctors();
  }

  LOG_TRACE << "EventLoop " << this << " stop looping";
  looping_ = false;
}

EventLoop::loop(),事件循环。EventLoop::loop()调用Poller::poll()等待事件发生,当有事件发生时,获取发生事件的Channels,并调用Channel的handleEvent处理事件。在处理完所有发生的事件后,调用EventLoop::doPendingFunctors()处理其他任务。

void EventLoop::doPendingFunctors()
{
  std::vector<Functor> functors;
  callingPendingFunctors_ = true;

  {
  MutexLockGuard lock(mutex_);
  functors.swap(pendingFunctors_);
  }

  for (const Functor& functor : functors)
  {
    functor();
  }
  callingPendingFunctors_ = false;
}

这些其他任务是什么呢??  可以看EventLoop::runInLoop(),在该函数中,会判断是否是在EventLoop实例所属线程调用的,如果不是,会将要执行的任务传递到EventLoop实例所属线程,而方法就是通过将任务放到pendingFunctors_中。这些从其他线程传递过来的任务,就是刚才这些其他任务。这样通过传递任务的方式,保证任务是线程安全的。

void EventLoop::runInLoop(Functor cb)
{
  if (isInLoopThread())
  {
    cb();
  }
  else
  {
    queueInLoop(std::move(cb));
  }
}

void EventLoop::queueInLoop(Functor cb)
{
  {
  MutexLockGuard lock(mutex_);
  pendingFunctors_.push_back(std::move(cb));
  }

  if (!isInLoopThread() || callingPendingFunctors_)
  {
    wakeup();
  }
}

再看下EventLoop::queueInLoop(),在EventLoop::queueInLoop()的最后会判断两个条件。是不是EventLoop实例所属线程,如果不是,那么需要通过wakeup()唤醒需要执行任务的那个线程。或者是EventLoop实例所属线程,但是callingPendingFunctors_为真,也要唤醒,为什么呢??什么时候为真呢?看上面的EventLoop::doPendingFunctors(),只有在执行EventLoop::doPendingFunctors()时为真,也就是说在EventLoop::doPendingFunctors()执行functor()时由functor()调用了EventLoop::runInLoop(),如果这时候不唤醒,当执行完EventLoop::doPendingFunctors()后,下一步又要阻塞在Poller::poll(),如果没有其他事件发生,可能阻塞很长时间,那么调用EventLoop::runInLoop()想要执行的任务很长时间都无法执行。

上面的EventLoop::runInLoop()的功能就是把要执行的任务放到EventLoop实例所属线程执行。

TimerId EventLoop::runAt(Timestamp time, TimerCallback cb)
{
  return timerQueue_->addTimer(std::move(cb), time, 0.0);
}

TimerId EventLoop::runAfter(double delay, TimerCallback cb)
{
  Timestamp time(addTime(Timestamp::now(), delay));
  return runAt(time, std::move(cb));
}

TimerId EventLoop::runEvery(double interval, TimerCallback cb)
{
  Timestamp time(addTime(Timestamp::now(), interval));
  return timerQueue_->addTimer(std::move(cb), time, interval);
}

void EventLoop::cancel(TimerId timerId)
{
  return timerQueue_->cancel(timerId);
}

这是EventLoop封装了定时器功能,以更容易使用的方式对外提供,根据名称就很容易理解功能,关于TimerQueue之前专门分析过了。这里不再详细说明了。

void EventLoop::updateChannel(Channel* channel)
{
  assert(channel->ownerLoop() == this);
  assertInLoopThread();
  poller_->updateChannel(channel);
}

void EventLoop::removeChannel(Channel* channel)
{
  assert(channel->ownerLoop() == this);
  assertInLoopThread();
  if (eventHandling_)
  {
    assert(currentActiveChannel_ == channel ||
        std::find(activeChannels_.begin(), activeChannels_.end(), channel) == activeChannels_.end());
  }
  poller_->removeChannel(channel);
}

bool EventLoop::hasChannel(Channel* channel)
{
  assert(channel->ownerLoop() == this);
  assertInLoopThread();
  return poller_->hasChannel(channel);
}

这部分在讲Channel的博客中提过了,这里也不再细说了。

  void setContext(const boost::any& context)
  { context_ = context; }

  const boost::any& getContext() const
  { return context_; }

  boost::any* getMutableContext()
  { return &context_; }

可以由用户随意使用。boost::any的主要作用是定义一个变量来存放任意类型的数据,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值