Muduo source code analysis channel channel class

Introduction

Channel is the event distributor in muduo. It belongs to only one EventLoop. The Channel class stores the type of IO event and the corresponding callback function. Each channel is only responsible for one file descriptor, but it does not own this file descriptor. The channel plays a communication role between epoll and TcpConnection, so it is also called a channel. Other classes establish a channel communication relationship by calling setCallbcak of the channel.

The main functions of the Channel class:

  1. Encapsulate the file descriptor (which may be socket type, eventfd type, timefd type, signalfd type), and set callback functions for various events (such as read callback, write callback, close callback, etc.) through this class.
  2. You can set your own listening event type, and then update the poller object (epoll or poll) to perform operations on this class (such as add, modify, delete operations) based on this type.
  3. Trigger the callback function based on the event type you listen to.

Source code analysis

channel.h

///
///An I/O channel that can be selected
//This class does not have its own file descriptor
/// This file descriptor may be socket, eventfd, timerfd, or signalfd.

class Channel: noncopyable
{<!-- -->
 public:
  typedef std::function<void()> EventCallback; //Event callback
  typedef std::function<void(Timestamp)> ReadEventCallback; //Read event callback

  Channel(EventLoop* loop, int fd); //A channel belongs to a loop, and a loop can correspond to multiple channels
  ~Channel();

  void handleEvent(Timestamp receiveTime); //Handle events
  void setReadCallback(ReadEventCallback cb) //Set read callback
  {<!-- --> readCallback_ = std::move(cb); }
  void setWriteCallback(EventCallback cb) //Set write callback
  {<!-- --> writeCallback_ = std::move(cb); }
  void setCloseCallback(EventCallback cb) //Set the close callback
  {<!-- --> closeCallback_ = std::move(cb); }
  void setErrorCallback(EventCallback cb) //Set error callback
  {<!-- --> errorCallback_ = std::move(cb); }

  /// Bind this channel to the owner object managed by shared_ptr,
  ///Prevent the owner object managed by shared_ptr from being destroyed in handleEvent
  void tie(const std::shared_ptr<void> & amp;);

  int fd() const {<!-- --> return fd_; }
  int events() const {<!-- --> return events_; } //Return registered events
  void set_revents(int revt) {<!-- --> revents_ = revt; } // Set the event type to be monitored
  // int revents() const { return revents_; }
  bool isNoneEvent() const {<!-- --> return events_ == kNoneEvent; }

  void enableReading() {<!-- --> events_ |= kReadEvent; update(); } //Listen for read events
  void disableReading() {<!-- --> events_ & amp;= ~kReadEvent; update(); } //Cancel monitoring of read events
  void enableWriting() {<!-- --> events_ |= kWriteEvent; update(); } //Listen to write events
  void disableWriting() {<!-- --> events_ & amp;= ~kWriteEvent; update(); } //Cancel monitoring of writing events
  void disableAll() {<!-- --> events_ = kNoneEvent; update(); } //Do not listen to any events
  bool isWriting() const {<!-- --> return events_ & amp; kWriteEvent; } //Whether to monitor writing events
  bool isReading() const {<!-- --> return events_ & amp; kReadEvent; } //Whether to listen for read events

  // for Poller
  int index() {<!-- --> return index_; } //The subscript in the poller event array
  void set_index(int idx) {<!-- --> index_ = idx; }

  // for debug
  string reventsToString() const;
  string eventsToString() const;

  void doNotLogHup() {<!-- --> logHup_ = false; }

  EventLoop* ownerLoop() {<!-- --> return loop_; } //Return to the loop it belongs to
  void remove(); //Remove channel from loop

 private:
  static string eventsToString(int fd, int ev);

  void update(); //Control the listening array according to the status of index_
  void handleEventWithGuard(Timestamp receiveTime); //A processing for the monitored event

  static const int kNoneEvent; //No event
  static const int kReadEvent; //Readable event
  static const int kWriteEvent; //writable event

  EventLoop* loop_;
  const int fd_;
  int events_; //Event type to listen to
  int revents_; //The type of event being monitored
  int index_; // represents the sequence number in the poller event array
  bool logHup_;

  std::weak_ptr<void> tie_;
  bool tied_;
  bool eventHandling_; //Whether the event is being processed
  bool addedToLoop_; //Whether to add to loop
  ReadEventCallback readCallback_; //read callback
  EventCallback writeCallback_; //Write callback
  EventCallback closeCallback_; //Close callback
  EventCallback errorCallback_; //Error callback
};

channel.cc

// Copyright 2010, Shuo Chen. All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
 
// Author: Shuo Chen (chenshuo at chenshuo dot com)
 
#include "muduo/base/Logging.h"
#include "muduo/net/Channel.h"
#include "muduo/net/EventLoop.h"
 
#include <sstream>
 
#include <poll.h>
 
using namespace muduo;
using namespace muduo::net;
 
const int Channel::kNoneEvent = 0;
const int Channel::kReadEvent = POLLIN | POLLPRI;
const int Channel::kWriteEvent = POLLOUT;
 
//Channel (event distributor) only belongs to one EventLoop. The Channel class stores the type of IO event and the corresponding callback function. Each channel is only responsible for one file descriptor.
Channel::Channel(EventLoop* loop, int fd__)
  : loop_(loop), //The loop to which the channel belongs. A channel only belongs to one loop.
    fd_(fd__), //The file descriptor responsible for channel
    events_(0), //Registered events
    revents_(0), //Ready events set by poller
    index_(-1), //The index used by poller
    logHup_(true), //Whether to generate some logs
    tied_(false), for tie() method
    eventHandling_(false), //Handling flag of handevent
    addedToLoop_(false) //Whether the loop is listening
{<!-- -->
}
 
Channel::~Channel()
{<!-- -->
  assert(!eventHandling_);
  assert(!addedToLoop_);
  if (loop_->isInLoopThread())//one loop per thread, determine whether the thread where event_loop is located and the thread that is destroying the object are the same thread
  {<!-- -->
    assert(!loop_->hasChannel(this)); //The object does not belong to loop_ at this time and there is no such record in poller to be destroyed successfully
  }
}
 
void Channel::tie(const std::shared_ptr<void> & amp; obj)
{<!-- -->
  tie_ = obj;
  tied_ = true;
}
 
void Channel::update() //Control the monitoring status of the channel according to the status of index_
{<!-- -->
  addedToLoop_ = true;
  loop_->updateChannel(this);//The lowest level of this function is actually implemented by Poller->updateChannel(this)
}
 
void Channel::remove() //Same as above
{<!-- -->
  assert(isNoneEvent());
  addedToLoop_ = false;
  loop_->removeChannel(this);//Remove the object from the array object monitored by Poller
}
 
//Handle all events that occur. If alive, the bottom layer calls handleEventWithGuard
void Channel::handleEvent(Timestamp receiveTime) //When the event arrives, call handleEvent for processing
{<!-- -->
  std::shared_ptr<void> guard; //guard
  if (tied_)
  {<!-- -->
    guard = tie_.lock();
    if(guard)
    {<!-- -->
      handleEventWithGuard(receiveTime);//The actual processing function
    }
  }
  else
  {<!-- -->
    handleEventWithGuard(receiveTime);
  }
}
 
//Handle all events that occur
//EPOLLIN: Indicates that the corresponding file descriptor can be read;
//EPOLLOUT: Indicates that the corresponding file descriptor can be written;
//EPOLLPRI: Indicates that the corresponding file descriptor has urgent data to read.
//EPOLLERR: Indicates that an error occurred in the corresponding file descriptor;
//EPOLLHUP: Indicates that the corresponding file descriptor is hung up;
//EPOLLET: Indicates that an event occurs in the corresponding file descriptor;
void Channel::handleEventWithGuard(Timestamp receiveTime)
{<!-- -->
  eventHandling_ = true;
  LOG_TRACE << reventsToString();
  if ((revents_ & amp; POLLHUP) & amp; & amp; !(revents_ & amp; POLLIN)) //Determine the return event type
  {<!-- -->
    if (logHup_)
    {<!-- -->
      LOG_WARN << "fd = " << fd_ << " Channel::handle_event() POLLHUP";
    }
    if (closeCallback_) closeCallback_();
  }
 
  if (revents_ & amp; POLLNVAL) //Illegal file descriptor
  {<!-- -->
    LOG_WARN << "fd = " << fd_ << " Channel::handle_event() POLLNVAL";
  }
 
  if (revents_ & amp; (POLLERR | POLLNVAL))
  {<!-- -->
    if (errorCallback_) errorCallback_();
  }
  if (revents_ & amp; (POLLIN | POLLPRI | POLLRDHUP)) //POLLRDHUP is the peer closing connection event, such as shutdown, etc.
  {<!-- -->
    if (readCallback_) readCallback_(receiveTime);
  }
  if (revents_ & amp; POLLOUT)
  {<!-- -->
    if (writeCallback_) writeCallback_();
  }
  //Event processing is completed, set the event processing flag to false
  eventHandling_ = false;
}
 
//Convert revents to string to facilitate output debugging
string Channel::reventsToString() const
{<!-- -->
  return eventsToString(fd_, revents_);
}
 
//Convert events to string to facilitate output debugging
string Channel::eventsToString() const
{<!-- -->
  return eventsToString(fd_, events_);
}
 
//In fact, the implementation of converting events and revents into string type
string Channel::eventsToString(int fd, int ev)
{<!-- -->
  std::ostringstream oss;
  oss << fd << ": ";
  if (ev & POLLIN)
    oss << "IN ";
  if (ev & POLLPRI)
    oss << "PRI ";
  if (ev & POLLOUT)
    oss << "OUT ";
  if (ev & POLLHUP)
    oss << "HUP ";
  if (ev & POLLRDHUP)
    oss << "RDHUP ";
  if (ev & POLLERR)
    oss << "ERR ";
  if (ev & POLLNVAL)
    oss << "NVAL ";
 
  return oss.str();
}