把原书第8章的例子《设计Reactor——如何从头开始编写非阻塞网络库》
地址: https://github.com/chenshuo/recipes/tree/master/reactor
用 c++11 搞了一遍,并且简化到全部代码放在一个文件中,方便察看。
步骤0:什么都不做的 event loop 事件循环
EventLoop, assertInLoopThread()
这个步骤里,作者只想强调如何保证每个线程中只能创建一个EventLoop对象,而且如何保证此对象中的loop函数只能在本线程中被调用。
test1: poll 5秒钟,创建两个线程,每个线程都有一个 EventLoop 对象
#include <iostream>
#include <thread>
#include <cassert>
#include <poll.h>
#include <unistd.h>
class EventLoop;
__thread EventLoop* t_loopInThisThread = nullptr;
class EventLoop
{
public:
EventLoop() : looping_(false), threadId_(std::this_thread::get_id())
{
std::cout << "EventLoop created " << this << " in thread " << threadId_ << std::endl;
if (t_loopInThisThread) {
std::cout << "Another EventLoop " << t_loopInThisThread << " exists in this thread " << threadId_ << std::endl;
abort();
} else {
t_loopInThisThread = this;
}
}
~EventLoop()
{
assert(!looping_);
t_loopInThisThread = nullptr;
}
static EventLoop* getEventLoopOfCurrentThread()
{
return t_loopInThisThread;
}
void loop()
{
assert(!looping_);
assertInLoopThread();
looping_ = true;
::poll(NULL, 0, 5*1000);
std::cout << "EventLoop " << this << " stop looping in thread " << std::this_thread::get_id() << std::endl;
looping_ = false;
}
void assertInLoopThread()
{
if (!isInLoopThread()) {
abortNotInLoopThread();
}
}
bool isInLoopThread() const
{
return threadId_ == std::this_thread::get_id();
}
private:
EventLoop(const EventLoop&) = delete;
EventLoop& operator=(const EventLoop&) = delete;
void abortNotInLoopThread()
{
std::cout << "EventLoop::abortNotInLoopThread - EventLoop " << this
<< " was created in threadId_ = " << threadId_
<< ", current thread id = " << std::this_thread::get_id() << std::endl;
abort();
}
bool looping_;
const std::thread::id threadId_;
};
void threadFunc()
{
std::cout << "threadFunc(): pid = " << getpid() << ", tid = " << std::this_thread::get_id() << std::endl;
EventLoop loop;
loop.loop();
}
int main()
{
std::cout << "main(): pid = " << getpid() << ", tid = " << std::this_thread::get_id() << std::endl;
EventLoop loop;
std::thread t(threadFunc);
loop.loop();
t.join();
}
运行结果
test2: 当从其它线程访问EventLoop时调用abort()退出
#include <iostream>
#include <thread>
#include <cassert>
#include <poll.h>
#include <unistd.h>
class EventLoop;
__thread EventLoop* t_loopInThisThread = nullptr;
class EventLoop
{
public:
EventLoop() : looping_(false), threadId_(std::this_thread::get_id())
{
std::cout << "EventLoop created " << this << " in thread " << threadId_ << std::endl;
if (t_loopInThisThread) {
std::cout << "Another EventLoop " << t_loopInThisThread << " exists in this thread " << threadId_ << std::endl;
abort();
} else {
t_loopInThisThread = this;
}
}
~EventLoop()
{
assert(!looping_);
t_loopInThisThread = nullptr;
}
static EventLoop* getEventLoopOfCurrentThread()
{
return t_loopInThisThread;
}
void loop()
{
assert(!looping_);
assertInLoopThread();
looping_ = true;
::poll(NULL, 0, 5*1000);
std::cout << "EventLoop " << this << " stop looping in thread " << std::this_thread::get_id() << std::endl;
looping_ = false;
}
void assertInLoopThread()
{
if (!isInLoopThread()) {
abortNotInLoopThread();
}
}
bool isInLoopThread() const
{
return threadId_ == std::this_thread::get_id();
}
private:
EventLoop(const EventLoop&) = delete;
EventLoop& operator=(const EventLoop&) = delete;
void abortNotInLoopThread()
{
std::cout << "EventLoop::abortNotInLoopThread - EventLoop " << this
<< " was created in threadId_ = " << threadId_
<< ", current thread id = " << std::this_thread::get_id() << std::endl;
abort();
}
bool looping_;
const std::thread::id threadId_;
};
EventLoop* g_loop;
void threadFunc()
{
g_loop->loop();
}
int main()
{
EventLoop loop;
g_loop = &loop;
std::thread t(threadFunc);
t.join();
}
运行结果
步骤1:Reactor原型
Channel, Poller, updateChannel()
test1: 永不退出的poll, 创建2个线程,每个线程中都有一个EventLoop
本例子中,EventLoop对象只创建了一个 Poller 对象(在初始化列表中 new 的)存放在智能指针 poller_ 内,还没有涉及 Channel 的对象操作。所以只是对空的 pollfds_ 进行 poll 系统调用,实际上就是什么描述符都没有观察,当然也不会poll到任何事件。
#include <iostream>
#include <thread>
#include <cassert>
#include <poll.h>
#include <unistd.h>
#include <functional>
#include <vector>
#include <chrono>
#include <map>
class EventLoop;
__thread EventLoop* t_loopInThisThread = nullptr;
const int kPollTimeMs = 10000;
class Channel;
class Poller
{
public:
typedef std::vector<Channel*> ChannelList;
Poller(EventLoop* loop): ownerLoop_(loop)
{
}
~Poller()
{
}
/// Polls the I/O events.
/// Must be called in the loop thread.
std::chrono::system_clock::time_point poll(int timeoutMs, ChannelList* activeChannels)
{
// XXX pollfds_ shouldn't change
int numEvents = ::poll(&*pollfds_.begin(), pollfds_.size(), timeoutMs);
std::chrono::system_clock::time_point now(std::chrono::system_clock::now());
if (numEvents > 0) {
std::cout << numEvents << " events happended" << std::endl;
fillActiveChannels(numEvents, activeChannels);
} else if (numEvents == 0) {
std::cout << " nothing happended" << std::endl;
} else {
std::cerr << "error: Poller::poll()" << std::endl;
}
return now;
}
/// Changes the interested I/O events.
/// Must be called in the loop thread.
void updateChannel(Channel* channel);
void assertInLoopThread();
private:
Poller(const Poller&) = delete;
Poller& operator=(const Poller&) = delete;
void fillActiveChannels(int numEvents,
ChannelList* activeChannels) const;
typedef std::vector<struct pollfd> PollFdList;
typedef std::map<int, Channel*> ChannelMap;
EventLoop* ownerLoop_;
PollFdList pollfds_;
ChannelMap channels_;
};
class EventLoop
{
public:
EventLoop() : looping_(false), quit_(false), threadId_(std::this_thread::get_id()), poller_(new Poller(this))
{
std::cout << "EventLoop created " << this << " in thread " << threadId_ << std::endl;
if (t_loopInThisThread) {
std::cout << "Another EventLoop " << t_loopInThisThread << " exists in this thread " << threadId_ << std::endl;
abort();
} else {
t_loopInThisThread = this;
}
}
~EventLoop()
{
assert(!looping_);
t_loopInThisThread = nullptr;
}
static EventLoop* getEventLoopOfCurrentThread()
{
return t_loopInThisThread;
}
void loop();
void quit()
{
quit_ = true;
// wakeup();
}
// internal use only
void updateChannel(Channel* channel);
void assertInLoopThread()
{
if (!isInLoopThread()) {
abortNotInLoopThread();
}
}
bool isInLoopThread() const
{
return threadId_ == std::this_thread::get_id();
}
private:
EventLoop(const EventLoop&) = delete;
EventLoop& operator=(const EventLoop&) = delete;
void abortNotInLoopThread()
{
std::cout << "EventLoop::abortNotInLoopThread - EventLoop " << this
<< " was created in threadId_ = " << threadId_
<< ", current thread id = " << std::this_thread::get_id() << std::endl;
abort();
}
typedef std::vector<Channel*> ChannelList;
bool looping_;
bool quit_; /* atomic */
const std::thread::id threadId_;
std::unique_ptr<Poller> poller_;
ChannelList activeChannels_;
};
class Channel
{
public:
typedef std::function<void()> EventCallback;
Channel(EventLoop* loop, int fdArg) : loop_(loop), fd_(fdArg), events_(0), revents_(0), index_(-1)
{
}
void handleEvent()
{
if (revents_ & POLLNVAL) {
std::cout << "Channel::handle_event() POLLNVAL" << std::endl;
}
if (revents_ & (POLLERR | POLLNVAL)) {
if (errorCallback_) errorCallback_();
}
if (revents_ & (POLLIN | POLLPRI | POLLRDHUP)) {
if (readCallback_) readCallback_();
}
if (revents_ & POLLOUT) {
if (writeCallback_) writeCallback_();
}
}
void setReadCallback(const EventCallback& cb)
{
readCallback_ = cb;
}
void setWriteCallback(const EventCallback& cb)
{
writeCallback_ = cb;
}
void setErrorCallback(const EventCallback& cb)
{
errorCallback_ = cb;
}
int fd() const
{
return fd_;
}
int events() const
{
return events_;
}
void set_revents(int revt)
{
revents_ = revt;
}
bool isNoneEvent() const
{
return events_ == kNoneEvent;
}
void enableReading()
{
events_ |= kReadEvent;
update();
}
// void enableWriting() { events_ |= kWriteEvent; update(); }
// void disableWriting() { events_ &= ~kWriteEvent; update(); }
// void disableAll() { events_ = kNoneEvent; update(); }
// for Poller
int index()
{
return index_;
}
void set_index(int idx)
{
index_ = idx;
}
EventLoop* ownerLoop()
{
return loop_;
}
private:
Channel(const Channel&) = delete;
Channel& operator=(const Channel&) = delete;
void update()
{
loop_->updateChannel(this);
}
static const int kNoneEvent = 0;
static const int kReadEvent = POLLIN | POLLPRI;
static const int kWriteEvent = POLLOUT;
EventLoop* loop_;
const int fd_;
int events_;
int revents_;
int index_; // used by Poller.
EventCallback readCallback_;
EventCallback writeCallback_;
EventCallback errorCallback_;
};
void EventLoop::loop()
{
assert(!looping_);
assertInLoopThread();
looping_ = true;
quit_ = false;
while (!quit_) {
activeChannels_.clear();
poller_->poll(kPollTimeMs, &activeChannels_);
for (ChannelList::iterator it = activeChannels_.begin();
it != activeChannels_.end(); ++it) {
(*it)->handleEvent();
}
}
std::cout << "EventLoop " << this << " stop looping in thread " << std::this_thread::get_id() << std::endl;
looping_ = false;
}
void EventLoop::updateChannel(Channel* channel)
{
assert(channel->ownerLoop() == this);
assertInLoopThread();
poller_->updateChannel(channel);
}
void Poller::updateChannel(Channel* channel)
{
assertInLoopThread();
std::cout << "fd = " << channel->fd() << " events = " << channel->events() << std::endl;
if (channel->index() < 0) {
// a new one, add to pollfds_
assert(channels_.find(channel->fd()) == channels_.end());
struct pollfd pfd;
pfd.fd = channel->fd();
pfd.events = static_cast<short>(channel->events());
pfd.revents = 0;
pollfds_.push_back(pfd);
int idx = static_cast<int>(pollfds_.size())-1;
channel->set_index(idx);
channels_[pfd.fd] = channel;
} else {
// update existing one
assert(channels_.find(channel->fd()) != channels_.end());
assert(channels_[channel->fd()] == channel);
int idx = channel->index();
assert(0 <= idx && idx < static_cast<int>(pollfds_.size()));
struct pollfd& pfd = pollfds_[idx];
assert(pfd.fd == channel->fd() || pfd.fd == -1);
pfd.events = static_cast<short>(channel->events());
pfd.revents = 0;
if (channel->isNoneEvent()) {
// ignore this pollfd
pfd.fd = -1;
}
}
}
void Poller::assertInLoopThread()
{
ownerLoop_->assertInLoopThread();
}
void Poller::fillActiveChannels(int numEvents,
ChannelList* activeChannels) const
{
for (PollFdList::const_iterator pfd = pollfds_.begin();
pfd != pollfds_.end() && numEvents > 0; ++pfd) {
if (pfd->revents > 0) {
--numEvents;
ChannelMap::const_iterator ch = channels_.find(pfd->fd);
assert(ch != channels_.end());
Channel* channel = ch->second;
assert(channel->fd() == pfd->fd);
channel->set_revents(pfd->revents);
// pfd->revents = 0;
activeChannels->push_back(channel);
}
}
}
//--test1---
void threadFunc()
{
std::cout << "threadFunc(): pid = " << getpid() << ", tid = " << std::this_thread::get_id() << std::endl;
EventLoop loop;
loop.loop();
}
int main()
{
std::cout << "main(): pid = " << getpid() << ", tid = " << std::this_thread::get_id() << std::endl;
EventLoop loop;
std::thread t(threadFunc);
loop.loop();
t.join();
pthread_exit(NULL);
}
运行结果
test2: 当从其它线程访问EventLoop时调用abort()退出
#include <iostream>
#include <thread>
#include <cassert>
#include <poll.h>
#include <unistd.h>
#include <functional>
#include <vector>
#include <chrono>
#include <map>
class EventLoop;
__thread EventLoop* t_loopInThisThread = nullptr;
const int kPollTimeMs = 10000;
class Channel;
class Poller
{
public:
typedef std::vector<Channel*> ChannelList;
Poller(EventLoop* loop): ownerLoop_(loop)
{
}
~Poller()
{
}
/// Polls the I/O events.
/// Must be called in the loop thread.
std::chrono::system_clock::time_point poll(int timeoutMs, ChannelList* activeChannels)
{
// XXX pollfds_ shouldn't change
int numEvents = ::poll(&*pollfds_.begin(), pollfds_.size(), timeoutMs);
std::chrono::system_clock::time_point now(std::chrono::system_clock::now());
if (numEvents > 0) {
std::cout << numEvents << " events happended" << std::endl;
fillActiveChannels(numEvents, activeChannels);
} else if (numEvents == 0) {
std::cout << " nothing happended" << std::endl;
} else {
std::cerr << "error: Poller::poll()" << std::endl;
}
return now;
}
/// Changes the interested I/O events.
/// Must be called in the loop thread.
void updateChannel(Channel* channel);
void assertInLoopThread();
private:
Poller(const Poller&) = delete;
Poller& operator=(const Poller&) = delete;
void fillActiveChannels(int numEvents,
ChannelList* activeChannels) const;
typedef std::vector<struct pollfd> PollFdList;
typedef std::map<int, Channel*> ChannelMap;
EventLoop* ownerLoop_;
PollFdList pollfds_;
ChannelMap channels_;
};
class EventLoop
{
public:
EventLoop() : looping_(false), quit_(false), threadId_(std::this_thread::get_id()), poller_(new Poller(this))
{
std::cout << "EventLoop created " << this << " in thread " << threadId_ << std::endl;
if (t_loopInThisThread) {
std::cout << "Another EventLoop " << t_loopInThisThread << " exists in this thread " << threadId_ << std::endl;
abort();
} else {
t_loopInThisThread = this;
}
}
~EventLoop()
{
assert(!looping_);
t_loopInThisThread = nullptr;
}
static EventLoop* getEventLoopOfCurrentThread()
{
return t_loopInThisThread;
}
void loop();
void quit()
{
quit_ = true;
// wakeup();
}
// internal use only
void updateChannel(Channel* channel);
void assertInLoopThread()
{
if (!isInLoopThread()) {
abortNotInLoopThread();
}
}
bool isInLoopThread() const
{
return threadId_ == std::this_thread::get_id();
}
private:
EventLoop(const EventLoop&) = delete;
EventLoop& operator=(const EventLoop&) = delete;
void abortNotInLoopThread()
{
std::cout << "EventLoop::abortNotInLoopThread - EventLoop " << this
<< " was created in threadId_ = " << threadId_
<< ", current thread id = " << std::this_thread::get_id() << std::endl;
abort();
}
typedef std::vector<Channel*> ChannelList;
bool looping_;
bool quit_; /* atomic */
const std::thread::id threadId_;
std::unique_ptr<Poller> poller_;
ChannelList activeChannels_;
};
class Channel
{
public:
typedef std::function<void()> EventCallback;
Channel(EventLoop* loop, int fdArg) : loop_(loop), fd_(fdArg), events_(0), revents_(0), index_(-1)
{
}
void handleEvent()
{
if (revents_ & POLLNVAL) {
std::cout << "Channel::handle_event() POLLNVAL" << std::endl;
}
if (revents_ & (POLLERR | POLLNVAL)) {
if (errorCallback_) errorCallback_();
}
if (revents_ & (POLLIN | POLLPRI | POLLRDHUP)) {
if (readCallback_) readCallback_();
}
if (revents_ & POLLOUT) {
if (writeCallback_) writeCallback_();
}
}
void setReadCallback(const EventCallback& cb)
{
readCallback_ = cb;
}
void setWriteCallback(const EventCallback& cb)
{
writeCallback_ = cb;
}
void setErrorCallback(const EventCallback& cb)
{
errorCallback_ = cb;
}
int fd() const
{
return fd_;
}
int events() const
{
return events_;
}
void set_revents(int revt)
{
revents_ = revt;
}
bool isNoneEvent() const
{
return events_ == kNoneEvent;
}
void enableReading()
{
events_ |= kReadEvent;
update();
}
// void enableWriting() { events_ |= kWriteEvent; update(); }
// void disableWriting() { events_ &= ~kWriteEvent; update(); }
// void disableAll() { events_ = kNoneEvent; update(); }
// for Poller
int index()
{
return index_;
}
void set_index(int idx)
{
index_ = idx;
}
EventLoop* ownerLoop()
{
return loop_;
}
private:
Channel(const Channel&) = delete;
Channel& operator=(const Channel&) = delete;
void update()
{
loop_->updateChannel(this);
}
static const int kNoneEvent = 0;
static const int kReadEvent = POLLIN | POLLPRI;
static const int kWriteEvent = POLLOUT;
EventLoop* loop_;
const int fd_;
int events_;
int revents_;
int index_; // used by Poller.
EventCallback readCallback_;
EventCallback writeCallback_;
EventCallback errorCallback_;
};
void EventLoop::loop()
{
assert(!looping_);
assertInLoopThread();
looping_ = true;
quit_ = false;
while (!quit_) {
activeChannels_.clear();
poller_->poll(kPollTimeMs, &activeChannels_);
for (ChannelList::iterator it = activeChannels_.begin();
it != activeChannels_.end(); ++it) {
(*it)->handleEvent();
}
}
std::cout << "EventLoop " << this << " stop looping in thread " << std::this_thread::get_id() << std::endl;
looping_ = false;
}
void EventLoop::updateChannel(Channel* channel)
{
assert(channel->ownerLoop() == this);
assertInLoopThread();
poller_->updateChannel(channel);
}
void Poller::updateChannel(Channel* channel)
{
assertInLoopThread();
std::cout << "fd = " << channel->fd() << " events = " << channel->events() << std::endl;
if (channel->index() < 0) {
// a new one, add to pollfds_
assert(channels_.find(channel->fd()) == channels_.end());
struct pollfd pfd;
pfd.fd = channel->fd();
pfd.events = static_cast<short>(channel->events());
pfd.revents = 0;
pollfds_.push_back(pfd);
int idx = static_cast<int>(pollfds_.size())-1;
channel->set_index(idx);
channels_[pfd.fd] = channel;
} else {
// update existing one
assert(channels_.find(channel->fd()) != channels_.end());
assert(channels_[channel->fd()] == channel);
int idx = channel->index();
assert(0 <= idx && idx < static_cast<int>(pollfds_.size()));
struct pollfd& pfd = pollfds_[idx];
assert(pfd.fd == channel->fd() || pfd.fd == -1);
pfd.events = static_cast<short>(channel->events());
pfd.revents = 0;
if (channel->isNoneEvent()) {
// ignore this pollfd
pfd.fd = -1;
}
}
}
void Poller::assertInLoopThread()
{
ownerLoop_->assertInLoopThread();
}
void Poller::fillActiveChannels(int numEvents,
ChannelList* activeChannels) const
{
for (PollFdList::const_iterator pfd = pollfds_.begin();
pfd != pollfds_.end() && numEvents > 0; ++pfd) {
if (pfd->revents > 0) {
--numEvents;
ChannelMap::const_iterator ch = channels_.find(pfd->fd);
assert(ch != channels_.end());
Channel* channel = ch->second;
assert(channel->fd() == pfd->fd);
channel->set_revents(pfd->revents);
// pfd->revents = 0;
activeChannels->push_back(channel);
}
}
}
//--test2---
// main 线程中创建一个 EventLoop 对象,然后再创建一个新的线程并且调用此对象的loop()成员函数,程序将会abort()
EventLoop* g_loop;
void threadFunc()
{
g_loop->loop();
}
int main()
{
EventLoop loop;
g_loop = &loop;
std::thread t(threadFunc);
t.join();
}
运行结果
test3: poll一个会在5秒钟超时的timerfd描述符
#include <iostream>
#include <thread>
#include <cassert>
#include <poll.h>
#include <unistd.h>
#include <functional>
#include <vector>
#include <chrono>
#include <map>
#include <sys/timerfd.h>
#include <cstring>
class EventLoop;
__thread EventLoop* t_loopInThisThread = nullptr;
const int kPollTimeMs = 10000;
class Channel;
class Poller
{
public:
typedef std::vector<Channel*> ChannelList;
Poller(EventLoop* loop): ownerLoop_(loop)
{
}
~Poller()
{
}
/// Polls the I/O events.
/// Must be called in the loop thread.
std::chrono::system_clock::time_point poll(int timeoutMs, ChannelList* activeChannels)
{
// XXX pollfds_ shouldn't change
int numEvents = ::poll(&*pollfds_.begin(), pollfds_.size(), timeoutMs);
std::chrono::system_clock::time_point now(std::chrono::system_clock::now());
if (numEvents > 0) {
std::cout << numEvents << " events happended" << std::endl;
fillActiveChannels(numEvents, activeChannels);
} else if (numEvents == 0) {
std::cout << " nothing happended" << std::endl;
} else {
std::cerr << "error: Poller::poll()" << std::endl;
}
return now;
}
/// Changes the interested I/O events.
/// Must be called in the loop thread.
void updateChannel(Channel* channel);
void assertInLoopThread();
private:
Poller(const Poller&) = delete;
Poller& operator=(const Poller&) = delete;
void fillActiveChannels(int numEvents,
ChannelList* activeChannels) const;
typedef std::vector<struct pollfd> PollFdList;
typedef std::map<int, Channel*> ChannelMap;
EventLoop* ownerLoop_;
PollFdList pollfds_;
ChannelMap channels_;
};
class EventLoop
{
public:
EventLoop() : looping_(false), quit_(false), threadId_(std::this_thread::get_id()), poller_(new Poller(this))
{
std::cout << "EventLoop created " << this << " in thread " << threadId_ << std::endl;
if (t_loopInThisThread) {
std::cout << "Another EventLoop " << t_loopInThisThread << " exists in this thread " << threadId_ << std::endl;
abort();
} else {
t_loopInThisThread = this;
}
}
~EventLoop()
{
assert(!looping_);
t_loopInThisThread = nullptr;
}
static EventLoop* getEventLoopOfCurrentThread()
{
return t_loopInThisThread;
}
void loop();
void quit()
{
quit_ = true;
// wakeup();
}
// internal use only
void updateChannel(Channel* channel);
void assertInLoopThread()
{
if (!isInLoopThread()) {
abortNotInLoopThread();
}
}
bool isInLoopThread() const
{
return threadId_ == std::this_thread::get_id();
}
private:
EventLoop(const EventLoop&) = delete;
EventLoop& operator=(const EventLoop&) = delete;
void abortNotInLoopThread()
{
std::cout << "EventLoop::abortNotInLoopThread - EventLoop " << this
<< " was created in threadId_ = " << threadId_
<< ", current thread id = " << std::this_thread::get_id() << std::endl;
abort();
}
typedef std::vector<Channel*> ChannelList;
bool looping_;
bool quit_; /* atomic */
const std::thread::id threadId_;
std::unique_ptr<Poller> poller_;
ChannelList activeChannels_;
};
class Channel
{
public:
typedef std::function<void()> EventCallback;
Channel(EventLoop* loop, int fdArg) : loop_(loop), fd_(fdArg), events_(0), revents_(0), index_(-1)
{
}
void handleEvent()
{
if (revents_ & POLLNVAL) {
std::cout << "Channel::handle_event() POLLNVAL" << std::endl;
}
if (revents_ & (POLLERR | POLLNVAL)) {
if (errorCallback_) errorCallback_();
}
if (revents_ & (POLLIN | POLLPRI | POLLRDHUP)) {
if (readCallback_) readCallback_();
}
if (revents_ & POLLOUT) {
if (writeCallback_) writeCallback_();
}
}
void setReadCallback(const EventCallback& cb)
{
readCallback_ = cb;
}
void setWriteCallback(const EventCallback& cb)
{
writeCallback_ = cb;
}
void setErrorCallback(const EventCallback& cb)
{
errorCallback_ = cb;
}
int fd() const
{
return fd_;
}
int events() const
{
return events_;
}
void set_revents(int revt)
{
revents_ = revt;
}
bool isNoneEvent() const
{
return events_ == kNoneEvent;
}
void enableReading()
{
events_ |= kReadEvent;
update();
}
// void enableWriting() { events_ |= kWriteEvent; update(); }
// void disableWriting() { events_ &= ~kWriteEvent; update(); }
// void disableAll() { events_ = kNoneEvent; update(); }
// for Poller
int index()
{
return index_;
}
void set_index(int idx)
{
index_ = idx;
}
EventLoop* ownerLoop()
{
return loop_;
}
private:
Channel(const Channel&) = delete;
Channel& operator=(const Channel&) = delete;
void update()
{
loop_->updateChannel(this);
}
static const int kNoneEvent = 0;
static const int kReadEvent = POLLIN | POLLPRI;
static const int kWriteEvent = POLLOUT;
EventLoop* loop_;
const int fd_;
int events_;
int revents_;
int index_; // used by Poller.
EventCallback readCallback_;
EventCallback writeCallback_;
EventCallback errorCallback_;
};
void EventLoop::loop()
{
assert(!looping_);
assertInLoopThread();
looping_ = true;
quit_ = false;
while (!quit_) {
activeChannels_.clear();
poller_->poll(kPollTimeMs, &activeChannels_);
for (ChannelList::iterator it = activeChannels_.begin();
it != activeChannels_.end(); ++it) {
(*it)->handleEvent();
}
}
std::cout << "EventLoop " << this << " stop looping in thread " << std::this_thread::get_id() << std::endl;
looping_ = false;
}
void EventLoop::updateChannel(Channel* channel)
{
assert(channel->ownerLoop() == this);
assertInLoopThread();
poller_->updateChannel(channel);
}
void Poller::updateChannel(Channel* channel)
{
assertInLoopThread();
std::cout << "fd = " << channel->fd() << " events = " << channel->events() << std::endl;
if (channel->index() < 0) {
// a new one, add to pollfds_
assert(channels_.find(channel->fd()) == channels_.end());
struct pollfd pfd;
pfd.fd = channel->fd();
pfd.events = static_cast<short>(channel->events());
pfd.revents = 0;
pollfds_.push_back(pfd);
int idx = static_cast<int>(pollfds_.size())-1;
channel->set_index(idx);
channels_[pfd.fd] = channel;
} else {
// update existing one
assert(channels_.find(channel->fd()) != channels_.end());
assert(channels_[channel->fd()] == channel);
int idx = channel->index();
assert(0 <= idx && idx < static_cast<int>(pollfds_.size()));
struct pollfd& pfd = pollfds_[idx];
assert(pfd.fd == channel->fd() || pfd.fd == -1);
pfd.events = static_cast<short>(channel->events());
pfd.revents = 0;
if (channel->isNoneEvent()) {
// ignore this pollfd
pfd.fd = -1;
}
}
}
void Poller::assertInLoopThread()
{
ownerLoop_->assertInLoopThread();
}
void Poller::fillActiveChannels(int numEvents,
ChannelList* activeChannels) const
{
for (PollFdList::const_iterator pfd = pollfds_.begin();
pfd != pollfds_.end() && numEvents > 0; ++pfd) {
if (pfd->revents > 0) {
--numEvents;
ChannelMap::const_iterator ch = channels_.find(pfd->fd);
assert(ch != channels_.end());
Channel* channel = ch->second;
assert(channel->fd() == pfd->fd);
channel->set_revents(pfd->revents);
// pfd->revents = 0;
activeChannels->push_back(channel);
}
}
}
// ---test3---
EventLoop* g_loop;
// 事件回调函数
void timeout()
{
std::cout << "Timeout!" << std::endl;
g_loop->quit(); // 让 EventLoop 对象退出事件循环
}
int main()
{
EventLoop loop;
g_loop = &loop;
// 创建一个 timerfd 文件描述符
int timerfd = ::timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
// 创建一个Channel对象,用一个EventLoop对象和timerfd描述符初始化,这个Channel对象只属于这个EventLoop对象,只负责这一个文件描述符的IO事件
Channel channel(&loop, timerfd);
// 为Channel对象注册一个Read的事件回调函数timeout()
channel.setReadCallback(timeout);
// 修改Channel对象的events_成员,使 POLLIN | POLLPRI 事件纳入观察范围,以便于Poller在实际执行poll操作时关注到文件描述符的这两种状态变化
channel.enableReading();
// timerfd 文件描述符超时设定为5秒钟
struct itimerspec howlong;
bzero(&howlong, sizeof howlong);
howlong.it_value.tv_sec = 5;
::timerfd_settime(timerfd, 0, &howlong, NULL);
loop.loop();
::close(timerfd);
}
运行结果