在muduo的EventLoop里面有一个poller和一堆的channel,其实这个poller是一个抽象基类,子类有poll和epoll。大抵是二者其实都有有点,可以兼顾吧。来看看其中巧夺天工的设计实现。
纯虚基类 poller
#pragma once
#include "nocopyable.hpp"
#include "timestamp.hpp"
#include<vector>
#include<unordered_map>
class Channel;
class EventLoop;
//muduo中多路事件分发器的核心模块
class Poller:nocpoyable{
public:
using ChannelList = std::vector<Channel*>;
explicit Poller(EventLoop *loop);
virtual ~Poller();
virtual timestamp poll(int timeoutMS,ChannelList* activeChannels) = 0;
virtual void updateChannel(Channel* channel) = 0;
virtual void removeChannel(Channel* channel) = 0;
bool hasChannel(Channel* channel) const;
//获取当前循环中默认的poller
static Poller* newDefaultPoller(EventLoop* loop);
protected:
//key:sockfd value:Channel通道类型
using ChannelMap = std::unordered_map<int,Channel*>;
ChannelMap channelmap_;
private:
EventLoop *ownnerloop_; //定义poller所属事件循环
};
#include "poller.hpp"
#include "channel.hpp"
Poller::Poller(EventLoop* loop)
: ownnerloop_(loop)
{
}
Poller::~Poller() = default;
bool Poller::hasChannel(Channel* channel) const
{
auto it = channelmap_.find(channel->fd());
return it != channelmap_.end() && it->second == channel;
}
//获取当前循环中默认的poller
// static Poller* newDefaultPoller(EventLoop* loop);
/*
这个函数不能在这里实现,这个函数是要返回一个具体的子类的对象的
*/
#include "poller.hpp"
Poller* Poller::newDefaultPoller(EventLoop* loop)
{
if (::getenv("MUDUO_USE_POLL")) //环境变量
{
return nullptr; //生成poll的实例
}
else
{
return nullptr; //生成epoll的实例
}
}
子类:EpollPoller
#include"poller.hpp"
#include"timestamp.hpp"
#include<vector>
#include<sys/epoll.h>
/*
epoll_create
epoll_ctl
epoll_wait
*/
class EpollPoller:public Poller{
public:
EpollPoller(EventLoop* loop); //epoll_create
~EpollPoller() override; //覆盖基类,如果基类对应名称不是虚函数则无法这么使用
timestamp poll(int timeoutMS,ChannelList* activeChannels) override; //epoll_wait
void updateChannel(Channel* channel) override; //epoll_ctl
void removeChannel(Channel* channel) override; //epoll_ctl
private:
//填写活跃链接
void fillActiveChannels(int numEvents,ChannelList* activeChannels) const;
//更新channel通道
void update(int operation,Channel* channel);
using EventList = std::vector<epoll_event>;
static const int kINitEventListSize = 16; //给epollevent一个初始长度
int epollfd_;
EventList events_;
};
#include"epollpoller.hpp"
#include"logger.hpp"
#include"channel.hpp"
#include<errno.h>
#include<unistd.h>
#include<memory.h>
//标识channel跟epoll的关系
const int kNew = -1;
const int kAdded = 1;
const int kDeleted = 2;
EpollPoller::EpollPoller(EventLoop* loop)
:Poller(loop),
epollfd_(::epoll_create1(EPOLL_CLOEXEC)),
events_(kINitEventListSize)
{
if(epollfd_<0){
LOG_FATAL("epoll_create error:%d\n",errno);
}
}
EpollPoller::~EpollPoller(){
::close(epollfd_);
}
//通过epoll_wait,将有事件的channel通过传出参数传递给EventLoop
timestamp EpollPoller::poll(int timeoutMS,ChannelList* activeChannels){
LOG_DEBUG("func = %s, fd total count = %d\n",__FUNCITON__, channelmap_.size());
int numEvents = ::epoll_wait(epollfd_,&*events_.begin(),events_.size(),timeoutMS);
int saveErrno = errno;
timestamp now(timestamp::now());
if(numEvents > 0){ //如果有事件发生
LOG_DEBUG("%d events happened \n",numEvents);
fillActiveChannels(numEvents,activeChannels);
//如果塞满了,说明需要扩容了
if(numEvents == events_.size()){
//需要手动扩容么?
//难道刚开始是有分配了内存还是咋?不一直是自动扩容吗?
//而且vector的扩容的话,1.5倍怕是要好一些吧
events_.resize(events_.size()*2);
}
}
else if(numEvents == 0){ //超时
LOG_DEBUG("%s timeout\n",__FUNCTION__);
}
else{ //出问题
if(saveErrno != EINTR){
errno = saveErrno;
LOG_ERROR("EpollPoller::poll err!\n");
}
}
return now;
}
void EpollPoller::updateChannel(Channel* channel){
int index = channel->index();
int fd = channel->fd();
LOG_INFO("func = %s, fd = %d, events = %d,index = %d\n",__FUNCTION__, fd,channel->events(),index);
//如果还没将channel添加到poller中,或已被删除过
if(index == kNew || index == kDeleted){
if(index == kNew){
channelmap_[fd] = channel;
}
channel->set_index(kAdded);
update(EPOLL_CTL_ADD,channel);
}
else{ //channel已经在poller上注册过了
if (channel->isNoneEvent()){ //如果没有感兴趣的事件了
update(EPOLL_CTL_DEL, channel);
channel->set_index(kDeleted);
}
else{
update(EPOLL_CTL_MOD, channel);
}
}
}
void EpollPoller::removeChannel(Channel* channel){
int index = channel->index();
int fd = channel->fd();
LOG_INFO("func = %s, fd = %d\n",__FUNCTION__, fd);
channelmap_.erase(fd);
if(index == kAdded){
update(EPOLL_CTL_DEL,channel);
}
channel->set_index(kNew);
}
void EpollPoller::fillActiveChannels(int numEvents,ChannelList* activeChannels) const{
//for(Channel* channel:activeChannels){
//这样居然不行了!!!
//eventloop 即将拿到它的poller返回的所有发生事件列表
for(int i = 0;i<numEvents;++i){
Channel* channel = static_cast<Channel*>(events_[i].data.ptr);
channel->set_revents(events_[i].events);
activeChannels->push_back(channel);
}
}
void EpollPoller::update(int operation,Channel* channel){
epoll_event event;
int fd = channel->fd();
memset(&event,0,sizeof(event));
event.events = channel->events();
event.data.ptr = channel;
event.data.fd = fd;
if(::epoll_ctl(epollfd_,operation,fd,&event)<0){
if (operation == EPOLL_CTL_DEL){
LOG_ERROR("epoll_ctl del error:%d\n",errno);
}
else{
LOG_FATAL("epoll_ctl add/mod error:%d\n",errno);
}
}
}