陳碩《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);
}

運行結果

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