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