WebServer parsing timer Timer class (1)

Premise:This WebServer version is not the one on Niuke.com, this version is https://github.com/linyacool/WebServer.git, using the Reactor model

At the same time, this is my personal understanding and personal analysis when I was studying, so my analysis may be wrong, and I welcome corrections and exchanges.

The main function of the Timer class

1.TimerNode uses the HttpData class, and the HttpData class uses the Channel class. First, the functions of the HttpData class and the Channel class (the specific contents of these two classes will not be expanded into details here)

HttpData class: Mainly used for actual operation processing of connection, reading, and writing.

Channel class: Mainly used for callback. The Channel callback is bound to the actual processing function in the HttpData class. When the read, write, and connection events in the Channel occur, it is called The actual processing functions in the HttpData class are used to read, write, and connect.

2.The relationship between the HttpData class and the Timer class:

The HttpData object is bound to a Timer object. When it times out, it is deleted from the priority queue.

At the same time, the Timer object is also bound to an HttpData object to determine the specific HttpData object bound to the TimerNode node.

The specific functions and detailed analysis of each function of the Timer class

The Timer.h file mainly defines three functions:

1. Time node structure TimerNode

1.1 Update time function update()

1.2 Is the time node valid?

1.3 Clear time node clearReq()

1.4 Delete setDelete()

1.5 Whether to delete isDelete()

1.6 Get the expiration time getExpTime()

2. Comparison of time nodes struct TimerCmp

3. Time node management type TimerManager

3.1 Add time node class addTimer() to HttpData

3.2 Handling the expiration time handleExpiredEvent()

A TimerNode needs to be bound to an HttpData class, and there is also a TiemrNode in an HttpData class to perform timeout deletion operations

#include <unistd.h>
#include <deque>
#include <memory>
#include <queue>
#include "HttpData.h"

class HttpData;//This is a forward declaration, indicating that HttpData contains the Timer class, and the Timer class also contains the HttpData class, in order to solve the circular dependency problem.

//Time node class
classTimerNode
{
public:
    // Each HttpData has a timernode object to manage event timeouts.
    TimerNode(std::shared_ptr<HttpData> requestDate, int timeout);
    ~TimerNode();
    TimerNode(TimerNode &tn);
    void update(int timeout);//Update expiration time
    bool isValid();//Determine whether it is valid
    void clearReq();//Clear the node
    //delete
    void setDeleted() { deleted_ = true; }
    // Determine whether to delete. Setting const here means that the value of the member variable cannot be changed.
    bool isDeleted() const
    {
        return deleted_;
    }
    // Get the expiration time
    size_t getExpTime() const { return expiredTime_; }

private:
    bool deleted_; // Identifier to determine whether this timer has been deleted
    size_t expiredTime_; // Expiration time
    std::shared_ptr<HttpData> SPHttpData;
};


/* A structure that compares the expiration times of a and b. Use the getExpTime() function in these two parameters to compare the expiration times of the two TimerNode shared pointers. Returns true if a's expiration time is greater than b's expiration time; otherwise, returns false. This is a commonly used method in priority queues. In class TimerManager, std::priority_queue<SPTimerNode, std::deque<SPTimerNode>, TimerCmp> timerNodeQueue; uses */
struct TimerCmp
{
    bool operator()(std::shared_ptr<TimerNode> & amp;a, std::shared_ptr<TimerNode> & amp;b)
    {
        return a->getExpTime() > b->getExpTime();
    }
};



//Management class of TimerNode node, mainly adding, deleting, and creating a priority queue
class TimerManager
{
public:
    TimerManager();
    ~TimerManager();
//Add Timer to the HttpData class
    void addTimer(std::shared_ptr<HttpData> SPHttpData, int timeout);
    void handleExpiredEvent();

private:
//Statement that SPTimerNode is equivalent to std::shared_ptr<TimerNode>
    typedef std::shared_ptr<TimerNode> SPTimerNode;
    /*This line of code declares a priority queue named timerNodeQueue, whose element type is SPTimerNode. The priority queue uses std::deque as its underlying container and uses the custom comparison function TimerCmp to compare elements. */
    std::priority_queue<SPTimerNode, std::deque<SPTimerNode>, TimerCmp> timerNodeQueue;
};

Timer.cpp

TimerNode::TimerNode(std::shared_ptr<HttpData> requestDate, int timeout) : deleted_(false), SPHttpData(requestDate)
{
    struct timeval now;
    /*gettimeofday(&now,NULL) is a function call used to get the current time. Where &now is a pointer to a timeval structure, and NULL is a pointer to a time zone structure. The gettimeofday function is used to get the current time and date, including seconds and microseconds. */
    gettimeofday(&now, NULL);
    /*expiredTime_ is set to a relative time point, that is, the time point after timeout milliseconds have passed from the current time.
    First, it gets the current time in seconds, tv_sec, and microseconds, tv_usec, from the now variable. It then takes the current number of seconds modulo 10000, probably to prevent going out of range. Then multiply the number of seconds modulo by 1000 to get the number of milliseconds. After that, divide the number of microseconds by 1000, which also gives you the number of milliseconds. */
    expiredTime_ = (((now.tv_sec % 10000) * 1000) + (now.tv_usec / 1000)) + timeout;
}

/*When TimerNode is destroyed in this place, HttpData will not be deleted (unless there is no other shared pointer pointing to TimerNode), but channel_ will be deleted from epoll*/
TimerNode::~TimerNode()
{
    // The implementation of this handleClose() function is in HttpData. There are three main implementations: the connection status is changed to disconnection, ensuring that the HttpData object will not be accidentally released during the execution of the handleClose method and the channel_ in the loop is removed.
    if(SPHttpData)
        SPHttpData->handleClose();
}

TimerNode::TimerNode(TimerNode & amp;tn) : SPHttpData(tn.SPHttpData), expiredTime_(0)
{
}

/*The result obtained is the new expiration time expiredTime_*/
void TimerNode::update(int timeout)
{
    struct timeval now;
    gettimeofday(&now, NULL);
    expiredTime_ = ((now.tv_sec % 10000) * 1000 + (now.tv_usec / 1000)) + timeout;
}

/* Check whether the time is valid, the logic is as follows:
1. Get the current time tmp
2. If now < expiration time return true
3. If now > expiration time return false*/
bool TimerNode::isValid()
{
    struct timeval now;
    gettimeofday(&now, NULL);
    size_t temp = ((now.tv_sec % 10000) * 1000 + (now.tv_usec / 1000));
    if (temp < expiredTime_)
    {
        return true;
    }
    else
    {
        this->setDeleted();
        return false;
    }
}

/*Clear the associated SPHttpData object and set the status of TimerNode to deleted. */
void TimerNode::clearReq()
{
    /*The shared pointer is clear. If there is no other place to call SPHttpData, then it will be destroyed*/
    SPHttpData.reset();
    this->setDeleted();
}

TimerManager::TimerManager()
{
}

TimerManager::~TimerManager() {}

void TimerManager::addTimer(std::shared_ptr<HttpData> SPHttpData, int timeout)
{
    // First build a timerNode, add it to the priority queue, and then associate it with HttpData
    SPTimerNode new_node = std::make_shared<TimerNode>(SPHttpData, timeout);
    timerNodeQueue.push(new_node);
    SPHttpData->linkTimer(new_node);//The implementation of this function is in the HttpData class
}

/*1. Delete items marked as delete. If it is not the first one marked as delete, it will not be deleted immediately. It will wait until delete times out before deleting.
Reason: The first benefit is that there is no need to traverse the priority queue, which saves time. The second benefit is to give the timeout a tolerable time, that is, the set timeout is the lower limit for deletion (it does not delete it immediately as soon as the timeout reaches the time). , if the monitored request appears again in the next request after timeout,
There is no need to re-apply for the RequestData node, so that the previous RequestData can be reused, reducing the time of one delete and one new.
2. Delete the timeout
*/
void TimerManager::handleExpiredEvent()
{
    while (!timerNodeQueue.empty())
    {
        SPTimerNode ptimer_now = timerNodeQueue.top();
        if (ptimer_now->isDeleted())
        {
            timerNodeQueue.pop();
        }
        else if (ptimer_now->isValid() == false)
        {
            timerNodeQueue.pop();
        }
        else
        {
            break;
        }
    }
}