1// 2// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) 3// 4// Distributed under the Boost Software License, Version 1.0. (See accompanying 5// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6// 7 8/** 9\page tuttimer1 Timer.1 - Using a timer synchronously 10 11This tutorial program introduces asio by showing how to perform a blocking 12wait on a timer. 13 14\dontinclude timer1/timer.cpp 15\skip #include 16 17We start by including the necessary header files. 18 19All of the asio classes can be used by simply including the <tt>"asio.hpp"</tt> 20header file. 21 22\until asio.hpp 23 24All programs that use asio need to have at least one I/O execution context, 25such as an boost::asio::io_context or boost::asio::thread_pool object. An I/O execution 26context provides access to I/O functionality. We declare an object of type 27boost::asio::io_context first thing in the main function. 28 29\until boost::asio::io_context 30 31Next we declare an object of type boost::asio::steady_timer. The core asio classes 32that provide I/O functionality (or as in this case timer functionality) always 33take a reference to an io_context as their first constructor argument. The 34second argument to the constructor sets the timer to expire 5 seconds from now. 35 36\until boost::asio::steady_timer 37 38In this simple example we perform a blocking wait on the timer. 39That is, the call to boost::asio::steady_timer::wait() will not return until the 40timer has expired, 5 seconds after it was created (i.e. <b>not</b> from when the 41wait starts). 42 43A timer is always in one of two states: "expired" or "not expired". If the 44boost::asio::steady_timer::wait() function is called on an expired timer, it will 45return immediately. 46 47\until wait 48 49Finally we print the obligatory <tt>"Hello, world!"</tt> 50message to show when the timer has expired. 51 52\until } 53 54See the \ref tuttimer1src "full source listing" \n 55Return to the \ref index "tutorial index" \n 56Next: \ref tuttimer2 57 58*/ 59 60/** 61\page tuttimer1src Source listing for Timer.1 62\include timer1/timer.cpp 63Return to \ref tuttimer1 64*/ 65 66/** 67\page tuttimer2 Timer.2 - Using a timer asynchronously 68 69This tutorial program demonstrates how to use asio's asynchronous callback 70functionality by modifying the program from tutorial Timer.1 to perform an 71asynchronous wait on the timer. 72 73\dontinclude timer2/timer.cpp 74\skip #include 75 76\until asio.hpp 77 78Using asio's asynchronous functionality means having a callback 79function that will be called when an asynchronous operation completes. In this 80program we define a function called <tt>print</tt> to be called when the 81asynchronous wait finishes. 82 83\until boost::asio::steady_timer 84 85Next, instead of doing a blocking wait as in tutorial Timer.1, 86we call the boost::asio::steady_timer::async_wait() function to perform an 87asynchronous wait. When calling this function we pass the <tt>print</tt> 88callback handler that was defined above. 89 90\skipline async_wait 91 92Finally, we must call the boost::asio::io_context::run() member function 93on the io_context object. 94 95The asio library provides a guarantee that callback handlers will <b>only</b> 96be called from threads that are currently calling boost::asio::io_context::run(). 97Therefore unless the boost::asio::io_context::run() function is called the callback for 98the asynchronous wait completion will never be invoked. 99 100The boost::asio::io_context::run() function will also continue to run while there is 101still "work" to do. In this example, the work is the asynchronous wait on the 102timer, so the call will not return until the timer has expired and the 103callback has completed. 104 105It is important to remember to give the io_context some work to do before 106calling boost::asio::io_context::run(). For example, if we had omitted the above call 107to boost::asio::steady_timer::async_wait(), the io_context would not have had any 108work to do, and consequently boost::asio::io_context::run() would have returned 109immediately. 110 111\skip run 112\until } 113 114See the \ref tuttimer2src "full source listing" \n 115Return to the \ref index "tutorial index" \n 116Previous: \ref tuttimer1 \n 117Next: \ref tuttimer3 118 119*/ 120 121/** 122\page tuttimer2src Source listing for Timer.2 123\include timer2/timer.cpp 124Return to \ref tuttimer2 125*/ 126 127/** 128\page tuttimer3 Timer.3 - Binding arguments to a handler 129 130In this tutorial we will modify the program from tutorial Timer.2 so that the 131timer fires once a second. This will show how to pass additional parameters to 132your handler function. 133 134\dontinclude timer3/timer.cpp 135\skip #include 136 137\until bind.hpp 138 139To implement a repeating timer using asio you need to change 140the timer's expiry time in your callback function, and to then start a new 141asynchronous wait. Obviously this means that the callback function will need 142to be able to access the timer object. To this end we add two new parameters 143to the <tt>print</tt> function: 144 145\li A pointer to a timer object. 146 147\li A counter so that we can stop the program when the timer fires for the 148sixth time. 149 150\until { 151 152As mentioned above, this tutorial program uses a counter to 153stop running when the timer fires for the sixth time. However you will observe 154that there is no explicit call to ask the io_context to stop. Recall that in 155tutorial Timer.2 we learnt that the boost::asio::io_context::run() function completes 156when there is no more "work" to do. By not starting a new asynchronous wait on 157the timer when <tt>count</tt> reaches 5, the io_context will run out of work and 158stop running. 159 160\until ++ 161 162Next we move the expiry time for the timer along by one second 163from the previous expiry time. By calculating the new expiry time relative to 164the old, we can ensure that the timer does not drift away from the 165whole-second mark due to any delays in processing the handler. 166 167\until expires_at 168 169Then we start a new asynchronous wait on the timer. As you can 170see, the boost::bind() function is used to associate the extra parameters 171with your callback handler. The boost::asio::steady_timer::async_wait() function 172expects a handler function (or function object) with the signature 173<tt>void(const boost::system::error_code&)</tt>. Binding the additional parameters 174converts your <tt>print</tt> function into a function object that matches the 175signature correctly. 176 177See the <a href="http://www.boost.org/libs/bind/bind.html">Boost.Bind 178documentation</a> for more information on how to use boost::bind(). 179 180In this example, the boost::asio::placeholders::error argument to boost::bind() is a 181named placeholder for the error object passed to the handler. When initiating 182the asynchronous operation, and if using boost::bind(), you must specify only 183the arguments that match the handler's parameter list. In tutorial Timer.4 you 184will see that this placeholder may be elided if the parameter is not needed by 185the callback handler. 186 187\until boost::asio::io_context 188 189A new <tt>count</tt> variable is added so that we can stop the 190program when the timer fires for the sixth time. 191 192\until boost::asio::steady_timer 193 194As in Step 4, when making the call to 195boost::asio::steady_timer::async_wait() from <tt>main</tt> we bind the additional 196parameters needed for the <tt>print</tt> function. 197 198\until run 199 200Finally, just to prove that the <tt>count</tt> variable was 201being used in the <tt>print</tt> handler function, we will print out its new 202value. 203 204\until } 205 206See the \ref tuttimer3src "full source listing" \n 207Return to the \ref index "tutorial index" \n 208Previous: \ref tuttimer2 \n 209Next: \ref tuttimer4 210 211*/ 212 213/** 214\page tuttimer3src Source listing for Timer.3 215\include timer3/timer.cpp 216Return to \ref tuttimer3 217*/ 218 219/** 220\page tuttimer4 Timer.4 - Using a member function as a handler 221 222In this tutorial we will see how to use a class member function as a callback 223handler. The program should execute identically to the tutorial program from 224tutorial Timer.3. 225 226\dontinclude timer4/timer.cpp 227\skip #include 228 229\until bind.hpp 230 231Instead of defining a free function <tt>print</tt> as the 232callback handler, as we did in the earlier tutorial programs, we now define a 233class called <tt>printer</tt>. 234 235\until public 236 237The constructor of this class will take a reference to the 238io_context object and use it when initialising the <tt>timer_</tt> member. The 239counter used to shut down the program is now also a member of the class. 240 241\until { 242 243The boost::bind() function works just as well with class 244member functions as with free functions. Since all non-static class member 245functions have an implicit <tt>this</tt> parameter, we need to bind 246<tt>this</tt> to the function. As in tutorial Timer.3, boost::bind() 247converts our callback handler (now a member function) into a function object 248that can be invoked as though it has the signature <tt>void(const 249boost::system::error_code&)</tt>. 250 251You will note that the boost::asio::placeholders::error placeholder is not specified 252here, as the <tt>print</tt> member function does not accept an error object as 253a parameter. 254 255\until } 256 257In the class destructor we will print out the final value of 258the counter. 259 260\until } 261 262The <tt>print</tt> member function is very similar to the 263<tt>print</tt> function from tutorial Timer.3, except that it now operates on 264the class data members instead of having the timer and counter passed in as 265parameters. 266 267\until }; 268 269The <tt>main</tt> function is much simpler than before, as it 270now declares a local <tt>printer</tt> object before running the io_context as 271normal. 272 273\until } 274 275See the \ref tuttimer4src "full source listing" \n 276Return to the \ref index "tutorial index" \n 277Previous: \ref tuttimer3 \n 278Next: \ref tuttimer5 \n 279 280*/ 281 282/** 283\page tuttimer4src Source listing for Timer.4 284\include timer4/timer.cpp 285Return to \ref tuttimer4 286*/ 287 288/** 289\page tuttimer5 Timer.5 - Synchronising handlers in multithreaded programs 290 291This tutorial demonstrates the use of the boost::asio::strand class template to 292synchronise callback handlers in a multithreaded program. 293 294The previous four tutorials avoided the issue of handler synchronisation by 295calling the boost::asio::io_context::run() function from one thread only. As you 296already know, the asio library provides a guarantee that callback handlers will 297<b>only</b> be called from threads that are currently calling 298boost::asio::io_context::run(). Consequently, calling boost::asio::io_context::run() from 299only one thread ensures that callback handlers cannot run concurrently. 300 301The single threaded approach is usually the best place to start when 302developing applications using asio. The downside is the limitations it places 303on programs, particularly servers, including: 304 305<ul> 306<li>Poor responsiveness when handlers can take a long time to complete.</li> 307<li>An inability to scale on multiprocessor systems.</li> 308</ul> 309 310If you find yourself running into these limitations, an alternative approach 311is to have a pool of threads calling boost::asio::io_context::run(). However, as this 312allows handlers to execute concurrently, we need a method of synchronisation 313when handlers might be accessing a shared, thread-unsafe resource. 314 315\dontinclude timer5/timer.cpp 316\skip #include 317 318\until bind.hpp 319 320We start by defining a class called <tt>printer</tt>, similar 321to the class in the previous tutorial. This class will extend the previous 322tutorial by running two timers in parallel. 323 324\until public 325 326In addition to initialising a pair of boost::asio::steady_timer members, the 327constructor initialises the <tt>strand_</tt> member, an object of type 328boost::asio::strand<boost::asio::io_context::executor_type>. 329 330The boost::asio::strand class template is an executor adapter that guarantees 331that, for those handlers that are dispatched through it, an executing handler 332will be allowed to complete before the next one is started. This is guaranteed 333irrespective of the number of threads that are calling 334boost::asio::io_context::run(). Of course, the handlers may still execute 335concurrently with other handlers that were <b>not</b> dispatched through an 336boost::asio::strand, or were dispatched through a different boost::asio::strand 337object. 338 339\until { 340 341When initiating the asynchronous operations, each callback handler is "bound" 342to an boost::asio::strand<boost::asio::io_context::executor_type> object. The 343boost::asio::bind_executor() function returns a new handler that automatically 344dispatches its contained handler through the boost::asio::strand object. By 345binding the handlers to the same boost::asio::strand, we are ensuring that they 346cannot execute concurrently. 347 348\until } 349\until } 350 351In a multithreaded program, the handlers for asynchronous 352operations should be synchronised if they access shared resources. In this 353tutorial, the shared resources used by the handlers (<tt>print1</tt> and 354<tt>print2</tt>) are <tt>std::cout</tt> and the <tt>count_</tt> data member. 355 356\until }; 357 358The <tt>main</tt> function now causes boost::asio::io_context::run() to 359be called from two threads: the main thread and one additional thread. This is 360accomplished using an boost::thread object. 361 362Just as it would with a call from a single thread, concurrent calls to 363boost::asio::io_context::run() will continue to execute while there is "work" left to 364do. The background thread will not exit until all asynchronous operations have 365completed. 366 367\until } 368 369See the \ref tuttimer5src "full source listing" \n 370Return to the \ref index "tutorial index" \n 371Previous: \ref tuttimer4 \n 372 373*/ 374 375/** 376\page tuttimer5src Source listing for Timer.5 377\include timer5/timer.cpp 378Return to \ref tuttimer5 379*/ 380