Muduo network programming example 3: Timer

Source: Internet
Author: User
Tags julian day signal handler usleep

Time in the program
The processing of time in the program is a big problem. I plan to write a separate article to fully discuss this issue. This article is tentatively named "Date and time in the program" Chapter 2 timing and timing ", and" Date and time in the program "Chapter 1 date calculation" in a series, four articles are expected in this series.

In this blog, I will briefly talk about the content directly related to programming, and leave more in-depth content to the date and time topic article mentioned above.

In general Server programming, common time-related tasks include:

Obtains the current time and calculates the time interval;
Time zone conversion and date calculation; convert the local time of New York to the local time of Shanghai; what is the day of the month and the day of the week after 100th? And so on.
Scheduled operations, such as executing a task at a scheduled time or executing a task after a delay.
Among them, 2nd items seem complicated and are actually the simplest. The Calculation of date is based on the Julian Day Number, and the time zone conversion is based on the tz database. The only trouble is when the database is busy, but it can also be solved using the tz database. These operations are pure functions and it is easy to use a unit test to verify the correctness of the Code. Note that using tzset/localtime_r for time zone conversion may cause problems in multi-threaded environments. My solution is to write a TimeZone class to avoid global impact, in the future, we will discuss the topics on date and time. This document does not consider the time zone, which is UTC time.

1st and 3rd are really troublesome. On the one hand, Linux has a plug-in that shows how to choose the dazzling time-related functions and struct in the program? On the other hand, the clock in the computer is not an ideal timer, and it may drift or jump. Finally, the relationship between the universal UTC time and the leap second also makes the scheduled task complex and subtle. Of course, operations related to the current system time also make unit testing difficult.

Linux time functions
Linux timing function, used to obtain the current time:

Time (2)/time_t (seconds)
Ftime (3)/struct timeb (MS)
Gettimeofday (2)/struct timeval (microsecond)
Clock_gettime (2)/struct timespec (nanoseconds)
Gmtime/localtime/timegm/mktime/strftime/struct tm (these are irrelevant to the current time)
Scheduled functions are used to wait a program for a period of time or Schedule Tasks:

Sleep
Alarm
Usleep
Nanosleep
Clock_nanosleep
Getitimer/setitimer
Timer_create/timer_settime/timer_gettime/timer_delete
Timerfd_create/timerfd_gettime/timerfd_settime
My trade-off is as follows:

(Timing) Only gettimeofday is used to obtain the current time.
(Timing) only use timerfd _ * series functions to process timing.
Gettimeofday: (this is also the main design consideration of muduo: Timestamp class)

The precision of time is too low, ftime has been deprecated, and clock_gettime has the highest precision, but the overhead of system calling is greater than gettimeofday.
On the x86-64 platform, gettimeofday is not a system call, but in the user State implementation (search vsyscall), there is no context switching and fall into the kernel overhead.
The gettimeofday resolution (resolution) is 1 microsecond, enough to meet the needs of daily timing. Muduo: Timestamp uses an int64_t to represent the number of microseconds from Epoch to the present, and its range is up to 0.3 million years.
Why timerfd _ * is selected:

Sleep/alarm/usleep may use SIGALRM in implementation. processing signals in multi-threaded programs is quite troublesome and should be avoided as much as possible. (Recently I will write a blog to talk about "multithreading, RAII, fork () and signal ")
Nanosleep and clock_nanosleep are thread-safe, but in non-blocking network programming, it is absolutely impossible to wait for a period of time by suspending the thread, and the program will lose response. The correct method is to register a time callback function.
Getitimer and timer_create also use signals to deliver time-out, which may also cause trouble in multi-threaded programs. Timer_create can specify whether the signal receiver is a process or a thread, which is an improvement. However, what the signal handler can do is limited.
Timerfd_create converts the time into a file descriptor. The "file" becomes readable when the timer times out, so that it can be easily integrated into the select/poll framework, handling IO events and timeout events in a unified manner is also the strength of the Reactor mode. I also talked about this idea in "inspiration for new Linux system calls" published a year ago. Now I have implemented this idea in the muduo network library.
The traditional Reactor uses the timeout of select/poll/epoll to implement the timing function. However, the timing precision of poll and epoll is only millisecond, far lower than the timing precision of timerfd_settime.
It must be noted that in a Linux non-real-time multi-task operating system, fully accurate and controllable timing and timing in user mode cannot be achieved, because the current task may be switched out at any time, this is especially evident when the CPU load is high. However, our program can improve the time precision as much as possible. When necessary, we can control the CPU load to improve the reliability of time operations, and the program will be executed as expected at 99.99%. This may be more economical than switching to a real-time operating system and re-writing and testing code.

The issue of Time Precision (accuracy) is not exactly the same as that of resolution. The impact and response of time jump and leap second are not discussed here.

Muduo timer Interface
Muduo EventLoop has three timer functions:

1: typedef boost: function <void ()> TimerCallback; 2: 3: // 4: // Reactor, at most one per thread. 5: // 6: // This is an interface class, so dont expose too much details. 7: class EventLoop: boost: noncopyable 8: {9: public: 10 ://... 11: 12: // timers 13: 14: // 15: TimerId runAt (const Timestamp & time, const TimerCallback & cb); 16: 17: // 18: /// Runs callback after @ c delay seconds. 19: // Safe to call from other threads. 20: TimerId runAfter (double delay, const TimerCallback & cb); 21: 22: // 23: // Runs callback every @ c interval seconds. 24: // Safe to call from other threads. 25: TimerId runEvery (double interval, const TimerCallback & cb); 26: 27: // Cancels the timer. 28: // Safe to call from other threads. 29: // void cancel (TimerId timerId); 30: 31 ://... 32 :};
RunAt calls TimerCallback at the specified time
Call TimerCallback for a period of time, such as runAfter.
RunEvery repeatedly calls TimerCallback at a fixed interval
Cancel cancels timer, which is not implemented currently
The callback function occurs in the thread where the EventLoop object is located, and is in the same thread as network event functions such as onMessage () onConnection.

Muduo's TimerQueue uses the simplest implementation (Linked List) to manage the timer, which is less efficient than the common binary heap approach, if the program contains a large number (more than 10) using a timer that is triggered repeatedly may be worth considering using a more advanced implementation. I have not used so many timers in a program, and I am not planning to optimize TimerQueue for the moment.

Example of Boost. Asio Timer
In the Boost. Asio tutorial, Timer and Daytime are used as examples to introduce the basic use of asio. daytime has been introduced in the previous article "Example 1". Here we will focus on Timer. Asio has five Timer examples. muduo re-implements four of them and expands the 5th examples.

Muduo does not support blocking timing and has no code.
For non-blocking timing, see examples/asio/tutorial/timer2
To pass Parameters in TimerCallback, see examples/asio/tutorial/timer3.
Use the member function as TimerCallback. For details, see examples/asio/tutorial/timer4.
Callback in multiple threads and use mutex to protect shared variables. For details, see examples/asio/tutorial/timer5.
Callback in multiple threads to narrow down the critical section and remove the code that does not need to be mutually exclusive. For details, see examples/asio/tutorial/timer6.
To save space, only timer4 is listed here:

1: # include 2: 3: # include 4: # include 5: # include 6: 7: class Printer: boost: noncopyable 8: {9: public: 10: printer (muduo: net: EventLoop * loop) 11: loop _ (loop), 12: count _ (0) 13: {14: loop _-> runAfter (1, boost: bind (& Printer: print, this); 15:} 16: 17 :~ Printer () 18: {19: std: cout <"Final count is" <count _ <""; 20:} 21: 22: void print () 23: {24: if (count _ <5) 25: {26: std: cout <count _ <""; 27: ++ count _; 28: 29: loop _-> runAfter (1, boost: bind (& Printer: print, this); 30:} 31: else 32: {33: & n

Related Article

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.