This article analyzes the implementation of reactor mode, the key is three classes: Channel, Poller, EventLoop. event Distribution Class Channel
Channel is the selectable IO Channel, which registers and responds to IO events, including the events registered to Poller and its listening, and the callback function that the event has been invoked on.
Each channel object is responsible for only one FD event distribution, encapsulates a series of operations for the FD, and uses a callback function that includes four readable, writable, closed, and error handling.
First, given the loop to which channel belongs, and the FD to be processed, then register the events that need to be monitored on FD, if it is a common read-write event, you can call the interface function enablereading or enablewriting to register the event on the FD. Disable* is the destruction of the specified event, and then set*callback to set the callback when the event occurs.
When registering an event, the function calls the relationship, as follows: Channel::update ()->eventloop::updatechannel (channel*)->poller::updatechannel (Channel*), The event is eventually registered or modified to the listening event table of the poll system call.
Channel.h
#ifndef muduo_net_channel_h #define Muduo_net_channel_h #include <boost/function.hpp> #include <boost/ noncopyable.hpp> #include <boost/shared_ptr.hpp> #include <boost/weak_ptr.hpp> #include <muduo/
Base/timestamp.h> namespace Muduo {namespace Net {class EventLoop;
A selectable I/O channel.
This class doesn ' t own the file descriptor. The file descriptor could be a socket,///a eventfd, a timerfd, or a SIGNALFD/* Event distribution class, mainly including FD FD Monitor events, event callback functions/CLA
SS Channel:boost::noncopyable {public:/* Event callback function template */typedef boost::function<void () > Eventcallback;
/* Read operation callback function, need incoming time */typedef boost::function<void (Timestamp) > Readeventcallback;
* * A channel is responsible for only one FD, but channel does not own the FD * EventLoop call Poller Listener event collection, the Ready event element is channel * channel is not only the return ready event, but also can handle the event.
Channel (eventloop* loop, int fd);
~channel (); * * * Channel core * processing events, generally by the poller through the EventLoop to invoke * when the relevant FD event is ready channel::handleevent () to perform the corresponding event callback *such as readable event execution readcallback_ ()/void Handleevent (Timestamp receivetime);
/* Set four kinds of callback functions */void Setreadcallback (const readeventcallback& cb) {readcallback_ = cb;}
void Setwritecallback (const eventcallback& cb) {writecallback_ = cb;}
void Setclosecallback (const eventcallback& cb) {closecallback_ = cb;} void Seterrorcallback (const eventcallback& cb) {errorcallback_ = cb;} #ifdef __gxx_experimental_cxx0x__/* c++11
Version Right value semantics * * void Setreadcallback (readeventcallback&& cb) {readcallback_ = Std::move (CB);}
void Setwritecallback (eventcallback&& cb) {writecallback_ = Std::move (CB);}
void Setclosecallback (eventcallback&& cb) {closecallback_ = Std::move (CB);} void Seterrorcallback (eventcallback&& cb) {errorcallback_ = Std::move (CB);} #endif///Tie this channel to
The owner object managed by shared_ptr,///prevent the owner object being destroyed in Handleevent. void Tie (const BOOST::SHARED_PTr<void>&);
/* Returns the channel-Responsible fd*/int fd () const {return fd_;}
/* Returns FD registered event/INT events () const {return events_;} * * After poll or epoll_wait, call this function according to the return event of FD, set the Ready event type of FD * Handleevent decide which event callback function to execute according to the Ready event type (REVENTS_)/void set_
revents (int revt) {revents_ = revt;}//used by pollers//int revents () const {return revents_;}
* To determine whether FD does not have incident monitoring/BOOL Isnoneevent () const {return events_ = = knoneevent;}
/* Update through EventLoop to updated Epoll in the monitoring of FD/* FD registration readable event/void enablereading () {events_ |= kreadevent; update ();}
/* Destroy read event/void Disablereading () {events_ &= ~kreadevent; update ();}
/* FD registered writable event/void Enablewriting () {events_ |= kwriteevent; update ();}
/* Destroy write Event/void Disablewriting () {events_ &= ~kwriteevent; update ();}
/* Stop listening for all events/void Disableall () {events_ = knoneevent; update ();}
/* Whether the read/write event is registered/BOOL Iswriting () const {return events_ & kwriteevent;} BOOL Isreading () const {RETUrn Events_ & kreadevent;
//For Poller//do not understand int index () {return index_}
void Set_index (int idx) {index_ = idx;}
For debug string reventstostring () const;
String eventstostring () const;
void Donotloghup () {loghup_ = false;}
/* Returns the EventLoop pointer holding this channel/eventloop* Ownerloop () {return loop_;}
/* Remove the channel from the EventLoop/void remove ();
Private:static string eventstostring (int fd, int ev);
/* by calling Loop_->updatechannel () to register or change this FD in the Epoll monitoring of the event/void update ();
void Handleeventwithguard (Timestamp receivetime); static const int knoneevent; No event static const int kreadevent; Readable event static const int kwriteevent; Writable event eventloop* Loop_; The EventLoop const int FD_ to which this channel belongs; This channel is responsible for the file descriptor, channel does not own FD int events_; FD registered event int revents_; The Ready event type int index_ returned by poll;
Subscript used by Poller used by Poller. BOOL Loghup_; Whether to generate some logs boost::weak_ptr<void> tie_;
BOOL Tied_; BOOL Eventhandling_;
Whether the event is being processed bool addedtoloop_; /* Four callback functions, using boost provided function template * * Readeventcallback readcallback_; Read Event callback function Eventcallback Writecallback_; Write event callback function Eventcallback Closecallback_; Closes the event callback function Eventcallback Errorcallback_;
Error event callback function}; }} #endif//Muduo_net_channel_h
channel.cc
/* Event/const int channel::knoneevent = 0; const int channel::kreadevent = Pollin |
Pollpri;
const int channel::kwriteevent = Pollout; Channel::channel (eventloop* loop, int fd__): Loop_ (Loop), Fd_ (fd__), Events_ (0), Revents_ (0), Index_ (-1 ), Loghup_ (True), Tied_ (false), Eventhandling_ (false), Addedtoloop_ (false) {} Channel::~channel () {as
SERT (!eventhandling_);
ASSERT (!ADDEDTOLOOP_);
if (Loop_->isinloopthread ()) {Assert (!loop_->haschannel (this));
}/* Change the event that the FD listens to in Epoll by calling the function in the loop/void Channel::update () {addedtoloop_ = true;
/* loop to invoke the function in Poller to implement/Loop_->updatechannel (this);
}/* is similar to above, removing fd*/void Channel::remove () {assert (Isnoneevent ()) from Epoll/poll by EventLoop;
Addedtoloop_ = false;
/* EventLoop will call the function in Poller/Loop_->removechannel (this);
} void Channel::handleevent (Timestamp receivetime) {boost::shared_ptr<void> guard;
if (tied_) {guard = Tie_.lock ();
if (guard) {Handleeventwithguard (receivetime);
} else {Handleeventwithguard (receivetime);
}/* Handle various events/void Channel::handleeventwithguard (Timestamp receivetime) {eventhandling_ = true;
Log_trace << reventstostring (); if (Revents_ & Pollhup) &&! ( Revents_ & Pollin)) {if (loghup_) {Log_warn << "FD =" << fd_ << "Channel::handle
_event () Pollhup ";
} if (Closecallback_) Closecallback_ (); } if (Revents_ & pollnval) {Log_warn << "FD =" << fd_ << "channel::handle_event () POLLNV
AL "; } if (Revents_ & Pollerr |
Pollnval)//Error event handling {if (Errorcallback_) Errorcallback_ (); } if (Revents_ & Pollin | Pollpri |
POLLRDHUP)//readable {if (readcallback_) Readcallback_ (receivetime);
} if (Revents_ & pollout)//writable {if (Writecallback_) Writecallback_ ();
} Eventhandling_ = false; }
IO Multiplexing class Poller
The Poller class is the base class for IO multiplexing classes, and Muduo supports both the poll and epoll two IO multiplexing mechanisms, which have two Pollpoller and Epollpoller two subclasses, with poll and Epoll implementations internally. Its responsibility is only IO multiplexing, event distribution to Channel completed, life and EventLoop as long.
Take Epoll for poller implementation, basically is the Epoll function encapsulation, poll function call epoll_wait to listen to the registered file descriptor, will return the ready event to mount the Activechannels array, can also control the channel of the events in the Add and delete Change.
Poller.h
#ifndef muduo_net_poller_h #define Muduo_net_poller_h #include <map> #include <vector> #include <boost/
noncopyable.hpp> #include <muduo/base/Timestamp.h> #include <muduo/net/EventLoop.h> namespace Muduo {
Namespace net {class Channel;
Base class for IO multiplexing//////This class doesn ' t own Channel the objects.
Class Poller:boost::noncopyable//does not own Channel {public:typedef std::vector<channel*> channellist;
/* Used to return the set of Ready events/Poller (eventloop* loop);
Virtual ~poller ();
Polls the I/O events.
Must is called in the loop thread.
/* Poller core functions, to add the Ready event to the activechannels * * Virtual Timestamp poll (int timeoutms, channellist* activechannels) = 0;
Changes the interested I/O events.
Must is called in the loop thread. /* Update the Monitor event for FD * Channel::update ()->eventloop::updatechannel (channel* Channel)->poller::updatechannel (Channel*
CHANNEL) * * * virtual void Updatechannel (channel* channel) = 0;
Remove the channel when it destructs.
Must is called in the loop thread. /* Remove FD from Poll/epoll stop listening for this FD * Eventloop::removechannel (channel*)->poller::removechannel (channel*)/virtual
void Removechannel (channel* Channel) = 0;
* To determine whether the Poll//epoll model listens to the Channel corresponding FD * * * virtual bool Haschannel (channel* Channel) const;
/*/static poller* Newdefaultpoller (eventloop* loop);
/* Assertion ensures that there is no cross thread/void Assertinloopthread () const {ownerloop_->assertinloopthread (); } Protected: * * Record the corresponding relationship between FD and channel * The epoll of the bottom of each monitoring the FD, according to the mapping relationship to find the corresponding Channel/typedef std::map<int, C
Hannel*> Channelmap;
Channelmap channels_;//Save Epoll Listening to the FD, and its corresponding channel pointer private:/* This Poller object belongs to EventLoop/eventloop* ownerloop_;
}; }} #endif//Muduo_net_poller_h
Epoll Implementation of Poller: poller/epollpoller.h
#ifndef muduo_net_poller_epollpoller_h #define MUDUO_NET_POLLER_EPOLLPOLLER_H #include <muduo/net/Poller.h>
Include <vector> struct epoll_event;
Namespace Muduo {namespace Net {//////IO multiplexing with Epoll (4).///class Epollpoller:public Poller {public:
Epollpoller (eventloop* loop);
Virtual ~epollpoller (); /* Internal call epoll_wait, initialize the corresponding channel, add to Activechannels/virtual Timestamp poll (int Timeoutms, channellist* activechannels
);
virtual void Updatechannel (channel* Channel);
virtual void Removechannel (channel* Channel);
private:static const int kiniteventlistsize = 16;
static const char* operationtostring (int op); /* Fills the active event returned by epoll_wait to Activechannels/void fillactivechannels (int numevents, channellist*
activechannels) const;
/* Change channel, call epoll_ctl/void update (int operation, channel* channel);
typedef std::vector<struct Epoll_event> EventList; int epollfd_; EPOLLFD evenTlist Events_;
Epoll event Array}; }} #endif//Muduo_net_poller_epollpoller_h
EventLoop class
The EventLoop class is the core of the reactor pattern, one thread an event loop, i.e. one loop per Thread,eventloop object's lifecycle is usually as long as the thread it belongs to. Its primary function is to run an event loop, wait for an event to occur, and then invoke the callback to handle the event that occurred. Eventloop::loop ()-> poller::p oll () populate the set of Ready events activechannels, and then traverse the container to execute channel for each channel::handleevent () Completes the corresponding Ready event callback.
EventLoop.h
#ifndef muduo_net_eventloop_h #define MUDUO_NET_EVENTLOOP_H #include <vector> #include <boost/any.hpp> # Include <boost/function.hpp> #include <boost/noncopyable.hpp> #include <boost/scoped_ptr.hpp> # Include <muduo/base/Mutex.h> #include <muduo/base/CurrentThread.h> #include <muduo/base/timestamp.h > #include <muduo/net/Callbacks.h> #include <muduo/net/TimerId.h> namespace Muduo {namespace Net {class Channel; Forward declaration, the event distributor is used primarily for event registration and callback class Poller;
IO multiplexing class, listening to the event set, namely Epoll/poll function class Timerqueue;
Reactor, at most one per thread.
This is a interface class, so don ' t expose too much details. /* EventLoop is not a copy of the Muduo most of the classes are not copied */class Eventloop:boost::noncopyable {public:typedef boost::function< ; void () > functor;
callback function EventLoop (); ~eventloop ();
Force Out-line Dtor, for Scoped_ptr.
Loops forever. Must is called in the same thread as creationof the object. * * IO thread created the EventLoop object, which is the core interface of this class * used to start the event loop * Eventloop::loop ()->poller::P oll () The set of events to be ready * and then through Chan
Nel::handleevent () Execute Ready event callback/void loop ();
Quits Loop. This isn't 100% thread safe, if you call through a raw pointer,///better to call through Shared_ptr<even
Tloop> for 100% safety.
Terminate event loop void Quit ();
Time when poll returns, usually means data arrival.
Timestamp pollreturntime () const {return pollreturntime_;}
int64_t iteration () const {return iteration_;}
void Runinloop (const functor& CB);
void Queueinloop (const functor& CB);
size_t queuesize () const;
#ifdef __gxx_experimental_cxx0x__ void Runinloop (functor&& CB);
void Queueinloop (functor&& CB);
#endif//Timers////At an absolute point in time to perform a timed callback Timerid runAt (const timestamp& times, const timercallback& CB); //Relative time execution timed callback Timerid Runafter (double delay, const timercallback&CB);
Every interval performs a timed callback Timerid runevery (double interval, const timercallback& CB);
//delete a timer void cancel (Timerid timerid);
#ifdef __gxx_experimental_cxx0x__ timerid runAt (const timestamp& time, timercallback&& CB);
Timerid Runafter (double delay, timercallback&& CB);
Timerid runevery (double interval, timercallback&& CB);
#endif//Internal usage//wake io thread void wakeup ();
Update an Event Distributor//Call Poller->updatechannel (channel) to complete the FD registration event and event callback function void Updatechannel (channel* channel) to the event collection;
Deletes an event distributor void Removechannel (channel* Channel);
BOOL Haschannel (channel* Channel);
pid_t threadId () const {return threadid_;} void Assertinloopthread () {if (!isinloopthread ())//If the running thread does not own the eventloop then exits, guaranteeing one loop per thread {Abortno
Tinloopthread ();
}/* Determines whether the current thread is the thread that owns this eventloop/BOOL Isinloopthread () const {return threadid_ = = Currentthread::tid ();} BOOL Callingpendingfunctors() const {return callingpendingfunctors_;}
BOOL Eventhandling () const {return eventhandling_;}
void SetContext (const boost::any& context) {Context_ = context;}
Const boost::any& GetContext () const {return context_;}
boost::any* Getmutablecontext () {return &context_;}
/* Returns the EventLoop object for this thread/static eventloop* geteventloopofcurrentthread ();
Private:/* In the EventLoop thread does not have the termination of/void Abortnotinloopthread (); /* Wakeupfd_ readable event callback/void Handleread ();
Waked up/* Execute queue pendingfunctors user task callback/void Dopendingfunctors (); void Printactivechannels () const;
DEBUG typedef std::vector<channel*> Channellist; BOOL Looping_; /* Atomic *///Operation logo bool Quit_; /* Atomic and shared between threads, okay on x86, I guess. *///exit cycle sign BOOL Eventhandling_; * * Atomic/bool Callingpendingfunctors_;
/* Atomic *///is performing a user task callback int64_t iteration_; Const pid_t THREADID_;
EventLoop's subordinate thread ID Timestamp pollreturntime_; BoOst::scoped_ptr<poller> Poller_; Multiplexing class Poller boost::scoped_ptr<timerqueue> timerqueue_;//timer queues are used for storing timer int wakeupfd_; EVENTFD returns the EVENTFD used to awaken the thread//unlike in Timerqueue where EventLoop is located, which is a internal class,//We don ' t expose Channel
to client. Wakeupfd_ read events on the WAKEUPCHANNEL_//when readable indicates the need to wake eventloop the thread to perform a user callback boost::scoped_ptr<channel> Wakeupchannel
_;
Boost::any Context_; Scratch variables channellist activechannels_; An active set of events, similar to the epoll set of events channel* Currentactivechannel_;
Current Active events mutable Mutexlock mutex_; Std::vector<functor> Pendingfunctors_;
@GuardedBy mutex_//Storage user task callback}; }} #endif//Muduo_net_eventloop_h
Here's a look at the implementation of the loop () and Quit ():
/* main loop, listening for events collection, executing the handler function of the Ready event/
void Eventloop::loop ()
{
assert (!looping_);
Assertinloopthread ();
Looping_ = true;