陈硕《Linux多线程服务端编程》中的EventLoop Reactor 的 c++11 版本

把原书第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);
}

运行结果

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章