MUDUO-10 Timer timer built from Epoll

Source: Internet
Author: User
Tags epoll

Mini-muduo Version Transfer Gate
Version 0.00 Building Muduo-1 Mini-muduo Introduction from Epoll
Version 0.01 builds the muduo-2 simplest epoll from Epoll
Version 0.02 adds the first class from the Epoll build muduo-3, by the way reactor
Version 0.03 from Epoll build muduo-4 join Channel
Version 0.04 builds muduo-5 from Epoll to join Acceptor and tcpconnection
Version 0.05 builds muduo-6 from Epoll to join EventLoop and Epoll
Version 0.06 from Epoll build muduo-7 join Imuduouser
Version 0.07 from Epoll build muduo-8 join send buffer and receive buffer
Version 0.08 build Muduo-9 from Epoll add onwritecomplate callback and buffer
Version 0.09 builds muduo-10 timer timer from Epoll
Version 0.11 building muduo-11 single-threaded reactor network model from Epoll
Version 0.12 building muduo-12 multithreaded code admission from Epoll
Version 0.13 build MUDUO-13 reactor + ThreadPool molding from Epoll

Mini-muduo v0.09 version, the timer timer was implemented. Mini-muduo complete and operational examples can be downloaded from GitHub, using the command git checkout v0.09 can switch to this version, online Browsing this version here.

This version is a milestone version, to this version, it can be said that a single threaded reactor mode network Library basic molding, the program in the class, the collaboration to complete the first network data. Later versions only add more worker threads (threads that consume CPU or need to wait for read/write) and multiple IO threads (IO multiplexing thread, which is the thread that runs Select/poll/epoll).

1 The introduction of timer in the original book is distributed in two places, <<7.8 timer >> and <<8.2 timerqueue timer >> In section 7.82, the author summarizes the reasons for choosing TIMERFD as the timer for multithreaded server programs:

1 Sleep (3)/alarm (2)/usleep (3) It is possible to use the SIGALRM signal in the implementation, processing signals in multithreaded programs is a very troublesome thing, should try to avoid

2 Nanosleep (2) and Clock_nanosleep (2) are thread-safe, but in a non-blocking network programming, it is absolutely impossible to wait a while for a thread to hang, so that the program loses its response. The correct approach is to register a time callback function.

3 Getitimer (2) and Timer_create (2) also use signals to deliver timeouts, which can also be troublesome in multithreaded programs.

4 Timerfd_create (2) turns time into a file descriptor that becomes scaled at the moment the timer times out, which makes it easy to fit into the Select (2)/poll (2) framework and handles IO time and timeout events in a uniform manner.

5 Traditional reactor uses the timeout of select (2)/poll (2)/epoll (4)/To implement timing functions, but the timing accuracy of poll (2)/and epoll_wait (2) is only milliseconds, far below timerfd_settime (2) The timing accuracy.

2 It is necessary to first see how the TIMERFD is working,

Note that the following paste code/line number is Mini-muduo, not muduo, of course, Muduo implementation principle is the same.

In our system, using TIMERFD as a timer, the following 5 functions are actually used, the first two are dedicated to the timer file description, and the next three can be used on a variety of file descriptors.

int timerfd_create (int clockid, int flags)//Create a timer file
int timerfd_settime (int UFD, int flags, const struct ITIMERSPEC * UTMR, struct itimerspec * otmr); Set the new timeout and start timing
int epoll_ctl (_EPOLLFD, Epoll_ctl_add, FD, &ev)//Add timer file descriptor to Epoll detect
int epoll_wait ( _EPOLLFD, _events, Max_events,-1); Wait for various file descriptor events
int close (int fd) on Epoll;//Release file descriptor
The order in which these 5 functions are listed is exactly the order in which the timer is actually used. First create a timer file descriptor through Timerfd_create, then set the timeout by Timerfd_settime, then add the timer file descriptor to the Epoll detection, and the program waits on the epoll_wait through a loop, Because there is no timer to sometimes cause blocking. Once the timer has arrived, the epoll_wait will return and we will be able to do the related processing. (in Muduo/mini-muduo, registration to the Epoll descriptor and receive epoll_wait () notifications are implemented through channel)

3 to see how users use Timer, the user to use the network library timer, must pass EventLoop. EventLoop has three interfaces exposing timer-related operations

int RunAt (Timestamp when, irun* Prun); Calls the function
int runafter (double delay, irun* Prun) at a specified time, or after a period of time, call function
int runevery (double interval, irun* prun) ; Call function
void Canceltimer (int timerfd) repeatedly at fixed intervals;//Close a timer

The return value of the first 3 functions int is used to uniquely determine the ID of a timer, and when a timer needs to be closed, pass the ID to the Canceltimer function. Irun is a callback interface, in which almost all callbacks are done by Irun. The first parameter of the runat is a timestamp object that represents the time, and the first parameter of Runafter and Runevery is in seconds.

4 detailed analysis of how the timer is implemented, in fact EventLoop only a timer file descriptor, when the user through the above 3 interface to eventloop all the timer added, actually work on the same timerfd, this is how to do it. Let's track the implementation of Eventloop::runat ()

int Eventloop::runat (Timestamp when, irun* prun)-
102 return     _ptimerqueue->addtimer (Prun, when , 0.0);
103}

Eventloop::runat (...) Directly called the Timerqueue::addtimer ()

int Timerqueue::addtimer (irun* prun, Timestamp when, double interval) timer*     ptimer = new Timer (whe N, Prun, interval); Memory leak!!!     _ploop->queueloop (_addtimerwrapper, ptimer);
 return     (int) Ptimer;
 71}
This creates a new timer object and then invokes Eventloop::queueloop (...), and the Queueloop method is performed asynchronously (there is currently only one thread, so only functions that are performed asynchronously). Timerqueue was executed asynchronously::d Oaddtimer (...) Method. Let's look at the Doaddtimer method.

Timerqueue::d oaddtimer (void* param)-timer* (Ptimer     ) = static_cast<timer*>;     bool earliestchanged = insert (Ptimer);                                 
 (     earliestchanged)         resettimerfd (_TIMERFD, Ptimer->getstamp ());     }                                                                      
 

Two methods are called here, one is insert (...), one is Resettimerfd (...), and the Insert (...) , the program inserts a Timer into a timerlist, which is a std::set<std::p air<timestamp, timer*>> it looks a little tedious, actually a time->timer Set of key-value pairs, the purpose of which is to store all the timers that are not expired, take the nearest timer out of the box whenever Timerfd arrive, and then modify TIMERFD to change the timer to the next nearest time in set. This enables the ability to manage multiple timers using only one TIMERFD. The return value of the insert is a Boolean, meaning whether the newly added timer is more early than all the timers in the set, and if so, you must modify the TIMERFD immediately to change the TIMERFD time to the nearest timer. If the newly added timer is not the first timer that occurs in set, you do not have to modify the TIMERFD.

163 bool Timerqueue::insert (timer* Ptimer) 164 {165 bool earliestchanged = FALSE; 166 Timestamp
when = Ptimer->getstamp ();
167 Timerlist::iterator it = _timers.begin ();     if (it = = _timers.end () | | < It->first) 169 {170 earliestchanged = true; 171} 172
Pair<timerlist::iterator, bool> result 173 = _timers.insert (Entry (when, Ptimer)); 174 if (!) (
Result.second)) 175 {176 cout << "_timers.insert () error" << Endl;
177} 178 179 return earliestchanged; 180} 
149 void timerqueue::resettimerfd (int timerfd, Timestamp stamp) (151 struct)     ;
152     struct Itimerspec oldValue;
153     bzero (&newvalue, sizeof (NewValue));
154     bzero (&oldvalue, sizeof (OldValue));     Newvalue.it_value = Howmuchtimefromnow (stamp);
156     INT ret =:: Timerfd_settime (TIMERFD, 0, &newvalue, &oldvalue);
157     if (ret)
158     {
159         cout << "timerfd_settime error" << Endl;
160     }
161}

Several implementation details:

Detail 1: Note the choice of timer containers, that is, the _timers member variables in Timerqueue, Muduo chose to use the two-fork search tree, that is, Std::map or Std::set, both two of the process, the author finally chose set, Because there may be multiple timers at the same point in time, it is not appropriate to use map directly because a pair<timestamp, timer*> represents a time, if directly with Map<timestamp, *timer> Only one timer* can be guaranteed at a point in time, which does not meet the requirements, so the author uses a slightly more complex type Set<pair<timestamp, timer*>> as a container for storing timers. The Mini-muduo is simplified in terms of return values and does not return a timerid like Muduo, but instead returns the int directly, which is the address of the timer object. Because in the same process, the object address is not the same, so it is easy to rough solve the same time multiple timer problems.

Detail 2: Insert operation of the timer, after using insert to set, automatically sorted according to timestamp, the first of iterator's begin is the earliest one in all timers. If you want to insert a new timer, there is an important sign that earliesttimerchanged, the important condition as a judge, if (it = S/s < It.begin.first) condition is satisfied, the symbol changed.

Detail 3: Note the operator overloads of timestamp

BOOL operator< (Timestamp L, Timestamp R),     L.microsecondssinceepoch () < R.microsecondssinceepoch (); 
 operator== bool (Timestamp L, Timestamp R) (     l.microsecondssinceepoch) = = R.microsecondssinceepoch ();
 65}

Details 4:timestamp::tostring () method, to print 64-bit integers, you can use the following method on the 32/64 platform

#include <inttypes.h>
//Cross-platform printing method
printf ("%" PRId64 "\ n", value);
Equivalent to 64 bits:
printf ("%" "ld" "\ n", value);
or 32-bit:
printf ("%" "LLD" "\ n", value);
Note that to use PRID64, but also to do a small processing, using macros to wrap #include<inttypes.h&gt, so the code in timestamp is written in the following, a little strange.
  5 #define __stdc_format_macros
  6 #include <inttypes.h>
  7 #undef __stdc_format_macros
For questions about __stdc_format_macros macros, you can read the following article http://blog.163.com/guixl_001/blog/static/4176410420121021111117987/

Details 5:interval and expiration's name should be from man timerfd_settime I explain
Detail 6: Modify the Irun interface, the original does not meet the requirements, because the call parameters can not be saved. At the same time, because the Irun interface has been modified, the runner class has been added to the Evenrloop to store callbacks and parameters, and the original _pendingfunctors can only store callbacks, and the parameters are not stored anywhere.
Detail 7: This version is not implemented for the time being because the canceltimers queue is not yet read Muduo.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.