• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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