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的主要作用是定义一个变量来存放任意类型的数据,