Timer module overview
sylar implements the timer function based on epoll. Because the accuracy of epoll is milliseconds, the accuracy of its timer is also milliseconds. The timer has the design based on time wheel and time heap. In sylar, it adopts the design of minimum heap. For the specific explanation of timer, please refer to my previous reading notes:
[reading] Linux high performance server programming - Chapter 11.
timers usually contain at least two members: timeout and task callback functions. If you use a linked list as a container to connect all timers in series, each timer also includes a pointer member pointing to the next timer (one-way linked list). The timer design based on time stack takes the timeout value of the timer with the smallest timeout time among all timers as the heart beat interval. In this way, once the heart beat function tick is called, the timer with the minimum timeout time must expire, so as to process the timer. Repeat in turn to achieve accurate timing.
in sylar, all timers are sorted according to the absolute time point of timeout (system time + timeout time). Each time, take out the time point closest to the current time, calculate the waiting time for timeout, and then wait. When the timeout time expires, click to obtain the current absolute value, and all timers in the minimum heap of the timer that are less than this time point will be called back.
Design of sylar timer
although the timer is also an important part of the whole system, such as IO scheduling, Hook, judging timeout, etc., the timer module is used, but the overall implementation of the timer by sylar is relatively simple.
sylar defines two classes in the timer module: timer class and TimerManager timer management class. Timer timer class includes timer absolute timeout, callback function, corresponding management class pointer and some reset, refresh and cancel methods. The TimerManager timer management class manages timers and implements the minimum heap structure through std::set (the elements in the set are sorted).
Timer class
// timer class Timer : public std::enable_shared_from_this<Timer> { friend class TimerManager; public: typedef std::shared_ptr<Timer> ptr; // Cancel timer bool cancel(); // Refresh timer execution time bool refesh(); // Reset timer time bool reset(uint64_t ms, bool from_now); private: // Constructor Timer(uint64_t ms, std::function<void()> cb, bool recurring, TimerManager* manager); // next: timestamp of execution Timer(uint64_t next); private: // Cycle timer bool m_recurring = false; // Execution cycle uint64_t m_ms = 0; // Precise execution time uint64_t m_next = 0; // Callback function std::function<void()> m_cb; // Timer manager TimerManager* m_manager = nullptr; private: // Timer comparison function struct Comparator { // Compare the size of the smart pointer of the timer (sorted by execution time) bool operator()(const Timer::ptr& lhs, const Timer::ptr& rhs) const; }; };
the detailed time of the whole Timer class is relatively simple, mainly in the aspect of imitation functions. Because you are still a little unfamiliar with C + +, you need to learn it separately here. Its function is to compare two Timer class objects according to the absolute timeout.
Functor, also known as Function Object, is a class that can exercise function functions. The syntax of the Functor is almost the same as that of our ordinary function calls, but as the class of the Functor, the operator() operator must be overloaded. Because calling an imitation function is actually calling the overloaded operator() operator through a class object.
next, let's take a look at the specific implementation of the imitation function:
bool Timer::Comparator::operator()(const Timer::ptr& lhs, const Timer::ptr& rhs)const{ if(!lhs && !rhs){ return false; } if(!lhs){ return true; } if(!rhs){ return false; } if(lhs->m_next < rhs->m_next){ return true; } if(rhs->m_next < lhs->m_next){ return false; } return lhs.get() < rhs.get(); }
Timer management class
the timer management class mainly maintains a timer set STD:: set < timer:: PTR, timer:: comparator > M_ timers; Of course, some related methods are also provided, such as notifying IOManager to update epoll_wait timeout, detect whether a teacher has occurred, create a timer, etc.
// Timer manager class TimerManager{ friend class Timer; public: typedef RWMutex RWMutexType; // Constructor TimerManager(); // Destructor virtual ~TimerManager(); // Add timer Timer::ptr addTimer(uint64_t ms, std::function<void()> cb, bool recurring = false); // Add condition timer Timer::ptr addConditionTimer(uint64_t ms, std::function<void()> cb, std::weak_ptr<void> weak_cond, bool recurring = false); // Time interval to the last timer execution uint64_t getNextTimer(); // Get the callback function list of the timer to be executed void listExpiredCb(std::vector<std::function<void()> >& cbs); // Is there a timer bool hasTimer(); protected: // This function is executed when a timer is inserted into the timer header virtual void onTimerInsertedAtFront() = 0; // Add timer to manager void addTimer(Timer::ptr val, RWMutexType::WriteLock& lock); private: // Check whether the server time has been adjusted bool detectClockRollover(uint64_t now_ms); private: // mutex RWMutexType m_mutex; // Timer set std::set<Timer::ptr, Timer::Comparator> m_timers; // Trigger onITimerInsertedAtFront bool m_tickled = false; // Last execution time uint64_t m_previouseTime = 0; };
the implementation of the Timer management class is also easy to read, but it is recommended to read it again in combination with the previous idle, because the initial IOManager does not combine the Timer module, but the subsequent sylar integrates the Timer module and IOManager through epoll_ On the one hand, wait detects whether an event is triggered, and on the other hand, it determines whether it is timeout. At the same time, sylar supports conditional timers and binds variables when creating timers. When the Timer is triggered, judge whether the variable is valid. If it is invalid, click the Timer to cancel the trigger.
summary
I think the Timer module is easier to understand than the previous collaborative scheduling, mainly because I know the design of its Timer, and I will try to change it to the form of time wheel later.