把原書第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);
}
運行結果