A guide to getting started with boost::asio
boost::asio provides a deadline_timer class that provides both synchronous and asynchronous interfaces. The docs page has a couple of good examples, so we can start right away with more advanced uses using what we already know about the boost::asio library.
In our first example, we will create a simple timer that expires in 5 seconds. There should be no surprises here as the docs go over this simple behavior.
What if we want a recurring timer? We could have the timer object global, but that might introduce some issues down the line as shared objects are not thread safe. This is where boost::bind comes to rescue once again! By making a shared_ptr to the timer object, we can use boost::bind and pass the timer to its own handler so we can keep it recurring. Here is that example.
As we can see, boost::bind allows us to do some pretty nifty things. The _1 parameter is an argument place holder. Since the TimerHandler function requires one parameter for the callback, we must reference this in the bind call. All in all, the _1 means "the first parameter, which will be supplied later". This blog has a nice post explaining this as well.
After running the program, we will see a timer that fires every 5 seconds. Great! Now we know how to make recurring timers in addition to the fire once type. Furthermore, we can utilize boost::bind to pass more parameters to the handler as needed. However, our timers will execute asynchronously so if we have more than one worker thread, it is possible that we execute a timer in one thread while we execute another event in another. Let us assume the timer handler and the work handler use the same shared object so we now have a non-thread safe design. How can we ensure a timer does not execute concurrently with a work handler?
The answer is with our friend strand. By using a strand object, we can post work through the strand as well as wrap the timer handler to be dispatched through it. As a result, we will get our serialized output and will not have to explicitly synchronize access to our shared object. Here is an example showing that.
It is vital to note how we must wrap the timer handler through the strand everywhere and not just the first time. If we forget that, the timer would no longer execute through the strand object and bugs can result. Running the program, we should see our first five work objects execute and then the timer thread. Since everything is serialized, the work objects have to complete in order first before the timer event fires. If we were to remove the strand wrap calls, then we would see the timer executing normally but the output would be messed up since we did not lock the std::cout object, and thus that shows us we would have multi-threaded bugs! For more useful information regarding the timers, check out the Time Travel article the boost::asio author has written.
At this point, we can see how bind, strand, and shared_ptr are invaluable components when paired with the boost::asio library in giving us the power and flexibility we need to program with. We will be making use of all of these features in covering the next aspect of the boost::asio library, which is the networking system.