mirror of
https://github.com/boostorg/asio.git
synced 2026-01-28 06:42:08 +00:00
........ r43377 | chris_kohlhoff | 2008-02-23 09:43:54 +1100 (Sat, 23 Feb 2008) | 2 lines Use the correct vector of timer queues when dispatching timers. ........ r43437 | chris_kohlhoff | 2008-02-29 23:57:57 +1100 (Fri, 29 Feb 2008) | 2 lines Add missing tie(). ........ r43469 | chris_kohlhoff | 2008-03-04 00:21:05 +1100 (Tue, 04 Mar 2008) | 4 lines Disable use of CancelIo by default, due to the possibility of silent failure on some system configurations. Swallow error returned by CancelIoEx if there are no operations to be cancelled. ........ r43470 | chris_kohlhoff | 2008-03-04 00:27:06 +1100 (Tue, 04 Mar 2008) | 2 lines Add missing 'boost_' prefix to helper namespace. ........ r43471 | chris_kohlhoff | 2008-03-04 00:36:35 +1100 (Tue, 04 Mar 2008) | 2 lines Regenerate documentation. ........ r43472 | chris_kohlhoff | 2008-03-04 01:05:35 +1100 (Tue, 04 Mar 2008) | 1 line Update copyright notices. ........ r43473 | chris_kohlhoff | 2008-03-04 01:13:01 +1100 (Tue, 04 Mar 2008) | 2 lines Update copyright notices. ........ r43569 | chris_kohlhoff | 2008-03-13 00:25:49 +1100 (Thu, 13 Mar 2008) | 4 lines Revert to having the windows-bug workaround (short timeout on GetQueuedCompletionStatus) on all threads as there are still scenarios where threads can get stuck indefinitely. ........ [SVN r43571]
381 lines
13 KiB
Plaintext
381 lines
13 KiB
Plaintext
//
|
|
// Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
|
//
|
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
//
|
|
|
|
/**
|
|
\page tuttimer1 Timer.1 - Using a timer synchronously
|
|
|
|
This tutorial program introduces asio by showing how to perform a blocking
|
|
wait on a timer.
|
|
|
|
\dontinclude timer1/timer.cpp
|
|
\skip #include
|
|
|
|
We start by including the necessary header files.
|
|
|
|
All of the asio classes can be used by simply including the <tt>"asio.hpp"</tt>
|
|
header file.
|
|
|
|
\until asio.hpp
|
|
|
|
Since this example users timers, we need to include the appropriate
|
|
Boost.Date_Time header file for manipulating times.
|
|
|
|
\until posix_time.hpp
|
|
|
|
All programs that use asio need to have at least one boost::asio::io_service object.
|
|
This class provides access to I/O functionality. We declare an object of this
|
|
type first thing in the main function.
|
|
|
|
\until boost::asio::io_service
|
|
|
|
Next we declare an object of type boost::asio::deadline_timer. The core asio classes
|
|
that provide I/O functionality (or as in this case timer functionality) always
|
|
take a reference to an io_service as their first constructor argument. The
|
|
second argument to the constructor sets the timer to expire 5 seconds from now.
|
|
|
|
\until boost::asio::deadline_timer
|
|
|
|
In this simple example we perform a blocking wait on the timer.
|
|
That is, the call to boost::asio::deadline_timer::wait() will not return until the
|
|
timer has expired, 5 seconds after it was created (i.e. <b>not</b> from when the
|
|
wait starts).
|
|
|
|
A deadline timer is always in one of two states: "expired" or "not expired". If
|
|
the boost::asio::deadline_timer::wait() function is called on an expired timer, it
|
|
will return immediately.
|
|
|
|
\until wait
|
|
|
|
Finally we print the obligatory <tt>"Hello, world!"</tt>
|
|
message to show when the timer has expired.
|
|
|
|
\until }
|
|
|
|
See the \ref tuttimer1src "full source listing" \n
|
|
Return to the \ref index "tutorial index" \n
|
|
Next: \ref tuttimer2
|
|
|
|
*/
|
|
|
|
/**
|
|
\page tuttimer1src Source listing for Timer.1
|
|
\include timer1/timer.cpp
|
|
Return to \ref tuttimer1
|
|
*/
|
|
|
|
/**
|
|
\page tuttimer2 Timer.2 - Using a timer asynchronously
|
|
|
|
This tutorial program demonstrates how to use asio's asynchronous callback
|
|
functionality by modifying the program from tutorial Timer.1 to perform an
|
|
asynchronous wait on the timer.
|
|
|
|
\dontinclude timer2/timer.cpp
|
|
\skip #include
|
|
|
|
\until posix_time.hpp
|
|
|
|
Using asio's asynchronous functionality means having a callback
|
|
function that will be called when an asynchronous operation completes. In this
|
|
program we define a function called <tt>print</tt> to be called when the
|
|
asynchronous wait finishes.
|
|
|
|
\until boost::asio::deadline_timer
|
|
|
|
Next, instead of doing a blocking wait as in tutorial Timer.1,
|
|
we call the boost::asio::deadline_timer::async_wait() function to perform an
|
|
asynchronous wait. When calling this function we pass the <tt>print</tt>
|
|
callback handler that was defined above.
|
|
|
|
\skipline async_wait
|
|
|
|
Finally, we must call the boost::asio::io_service::run() member function
|
|
on the io_service object.
|
|
|
|
The asio library provides a guarantee that callback handlers will <b>only</b>
|
|
be called from threads that are currently calling boost::asio::io_service::run().
|
|
Therefore unless the boost::asio::io_service::run() function is called the callback for
|
|
the asynchronous wait completion will never be invoked.
|
|
|
|
The boost::asio::io_service::run() function will also continue to run while there is
|
|
still "work" to do. In this example, the work is the asynchronous wait on the
|
|
timer, so the call will not return until the timer has expired and the
|
|
callback has completed.
|
|
|
|
It is important to remember to give the io_service some work to do before
|
|
calling boost::asio::io_service::run(). For example, if we had omitted the above call
|
|
to boost::asio::deadline_timer::async_wait(), the io_service would not have had any
|
|
work to do, and consequently boost::asio::io_service::run() would have returned
|
|
immediately.
|
|
|
|
\skip run
|
|
\until }
|
|
|
|
See the \ref tuttimer2src "full source listing" \n
|
|
Return to the \ref index "tutorial index" \n
|
|
Previous: \ref tuttimer1 \n
|
|
Next: \ref tuttimer3
|
|
|
|
*/
|
|
|
|
/**
|
|
\page tuttimer2src Source listing for Timer.2
|
|
\include timer2/timer.cpp
|
|
Return to \ref tuttimer2
|
|
*/
|
|
|
|
/**
|
|
\page tuttimer3 Timer.3 - Binding arguments to a handler
|
|
|
|
In this tutorial we will modify the program from tutorial Timer.2 so that the
|
|
timer fires once a second. This will show how to pass additional parameters to
|
|
your handler function.
|
|
|
|
\dontinclude timer3/timer.cpp
|
|
\skip #include
|
|
|
|
\until posix_time.hpp
|
|
|
|
To implement a repeating timer using asio you need to change
|
|
the timer's expiry time in your callback function, and to then start a new
|
|
asynchronous wait. Obviously this means that the callback function will need
|
|
to be able to access the timer object. To this end we add two new parameters
|
|
to the <tt>print</tt> function:
|
|
|
|
\li A pointer to a timer object.
|
|
|
|
\li A counter so that we can stop the program when the timer fires for the
|
|
sixth time.
|
|
|
|
\until {
|
|
|
|
As mentioned above, this tutorial program uses a counter to
|
|
stop running when the timer fires for the sixth time. However you will observe
|
|
that there is no explicit call to ask the io_service to stop. Recall that in
|
|
tutorial Timer.2 we learnt that the boost::asio::io_service::run() function completes
|
|
when there is no more "work" to do. By not starting a new asynchronous wait on
|
|
the timer when <tt>count</tt> reaches 5, the io_service will run out of work and
|
|
stop running.
|
|
|
|
\until ++
|
|
|
|
Next we move the expiry time for the timer along by one second
|
|
from the previous expiry time. By calculating the new expiry time relative to
|
|
the old, we can ensure that the timer does not drift away from the
|
|
whole-second mark due to any delays in processing the handler.
|
|
|
|
\until expires_at
|
|
|
|
Then we start a new asynchronous wait on the timer. As you can
|
|
see, the boost::bind() function is used to associate the extra parameters
|
|
with your callback handler. The boost::asio::deadline_timer::async_wait() function
|
|
expects a handler function (or function object) with the signature
|
|
<tt>void(const boost::system::error_code&)</tt>. Binding the additional parameters
|
|
converts your <tt>print</tt> function into a function object that matches the
|
|
signature correctly.
|
|
|
|
See the <a href="http://www.boost.org/libs/bind/bind.html">Boost.Bind
|
|
documentation</a> for more information on how to use boost::bind().
|
|
|
|
In this example, the boost::asio::placeholders::error argument to boost::bind() is a
|
|
named placeholder for the error object passed to the handler. When initiating
|
|
the asynchronous operation, and if using boost::bind(), you must specify only
|
|
the arguments that match the handler's parameter list. In tutorial Timer.4 you
|
|
will see that this placeholder may be elided if the parameter is not needed by
|
|
the callback handler.
|
|
|
|
\until boost::asio::io_service
|
|
|
|
A new <tt>count</tt> variable is added so that we can stop the
|
|
program when the timer fires for the sixth time.
|
|
|
|
\until boost::asio::deadline_timer
|
|
|
|
As in Step 4, when making the call to
|
|
boost::asio::deadline_timer::async_wait() from <tt>main</tt> we bind the additional
|
|
parameters needed for the <tt>print</tt> function.
|
|
|
|
\until run
|
|
|
|
Finally, just to prove that the <tt>count</tt> variable was
|
|
being used in the <tt>print</tt> handler function, we will print out its new
|
|
value.
|
|
|
|
\until }
|
|
|
|
See the \ref tuttimer3src "full source listing" \n
|
|
Return to the \ref index "tutorial index" \n
|
|
Previous: \ref tuttimer2 \n
|
|
Next: \ref tuttimer4
|
|
|
|
*/
|
|
|
|
/**
|
|
\page tuttimer3src Source listing for Timer.3
|
|
\include timer3/timer.cpp
|
|
Return to \ref tuttimer3
|
|
*/
|
|
|
|
/**
|
|
\page tuttimer4 Timer.4 - Using a member function as a handler
|
|
|
|
In this tutorial we will see how to use a class member function as a callback
|
|
handler. The program should execute identically to the tutorial program from
|
|
tutorial Timer.3.
|
|
|
|
\dontinclude timer4/timer.cpp
|
|
\skip #include
|
|
|
|
\until posix_time.hpp
|
|
|
|
Instead of defining a free function <tt>print</tt> as the
|
|
callback handler, as we did in the earlier tutorial programs, we now define a
|
|
class called <tt>printer</tt>.
|
|
|
|
\until public
|
|
|
|
The constructor of this class will take a reference to the
|
|
io_service object and use it when initialising the <tt>timer_</tt> member. The
|
|
counter used to shut down the program is now also a member of the class.
|
|
|
|
\until {
|
|
|
|
The boost::bind() function works just as well with class
|
|
member functions as with free functions. Since all non-static class member
|
|
functions have an implicit <tt>this</tt> parameter, we need to bind
|
|
<tt>this</tt> to the function. As in tutorial Timer.3, boost::bind()
|
|
converts our callback handler (now a member function) into a function object
|
|
that can be invoked as though it has the signature <tt>void(const
|
|
boost::system::error_code&)</tt>.
|
|
|
|
You will note that the boost::asio::placeholders::error placeholder is not specified
|
|
here, as the <tt>print</tt> member function does not accept an error object as
|
|
a parameter.
|
|
|
|
\until }
|
|
|
|
In the class destructor we will print out the final value of
|
|
the counter.
|
|
|
|
\until }
|
|
|
|
The <tt>print</tt> member function is very similar to the
|
|
<tt>print</tt> function from tutorial Timer.3, except that it now operates on
|
|
the class data members instead of having the timer and counter passed in as
|
|
parameters.
|
|
|
|
\until };
|
|
|
|
The <tt>main</tt> function is much simpler than before, as it
|
|
now declares a local <tt>printer</tt> object before running the io_service as
|
|
normal.
|
|
|
|
\until }
|
|
|
|
See the \ref tuttimer4src "full source listing" \n
|
|
Return to the \ref index "tutorial index" \n
|
|
Previous: \ref tuttimer3 \n
|
|
Next: \ref tuttimer5 \n
|
|
|
|
*/
|
|
|
|
/**
|
|
\page tuttimer4src Source listing for Timer.4
|
|
\include timer4/timer.cpp
|
|
Return to \ref tuttimer4
|
|
*/
|
|
|
|
/**
|
|
\page tuttimer5 Timer.5 - Synchronising handlers in multithreaded programs
|
|
|
|
This tutorial demonstrates the use of the boost::asio::strand class to synchronise
|
|
callback handlers in a multithreaded program.
|
|
|
|
The previous four tutorials avoided the issue of handler synchronisation by
|
|
calling the boost::asio::io_service::run() function from one thread only. As you
|
|
already know, the asio library provides a guarantee that callback handlers will
|
|
<b>only</b> be called from threads that are currently calling
|
|
boost::asio::io_service::run(). Consequently, calling boost::asio::io_service::run() from
|
|
only one thread ensures that callback handlers cannot run concurrently.
|
|
|
|
The single threaded approach is usually the best place to start when
|
|
developing applications using asio. The downside is the limitations it places
|
|
on programs, particularly servers, including:
|
|
|
|
<ul>
|
|
<li>Poor responsiveness when handlers can take a long time to complete.</li>
|
|
<li>An inability to scale on multiprocessor systems.</li>
|
|
</ul>
|
|
|
|
If you find yourself running into these limitations, an alternative approach
|
|
is to have a pool of threads calling boost::asio::io_service::run(). However, as this
|
|
allows handlers to execute concurrently, we need a method of synchronisation
|
|
when handlers might be accessing a shared, thread-unsafe resource.
|
|
|
|
\dontinclude timer5/timer.cpp
|
|
\skip #include
|
|
|
|
\until posix_time.hpp
|
|
|
|
We start by defining a class called <tt>printer</tt>, similar
|
|
to the class in the previous tutorial. This class will extend the previous
|
|
tutorial by running two timers in parallel.
|
|
|
|
\until public
|
|
|
|
In addition to initialising a pair of boost::asio::deadline_timer members, the
|
|
constructor initialises the <tt>strand_</tt> member, an object of type
|
|
boost::asio::strand.
|
|
|
|
An boost::asio::strand guarantees that, for those handlers that are dispatched through
|
|
it, an executing handler will be allowed to complete before the next one is
|
|
started. This is guaranteed irrespective of the number of threads that are
|
|
calling boost::asio::io_service::run(). Of course, the handlers may still execute
|
|
concurrently with other handlers that were <b>not</b> dispatched through an
|
|
boost::asio::strand, or were dispatched through a different boost::asio::strand object.
|
|
|
|
\until {
|
|
|
|
When initiating the asynchronous operations, each callback handler is "wrapped"
|
|
using the boost::asio::strand object. The boost::asio::strand::wrap() function returns a new
|
|
handler that automatically dispatches its contained handler through the
|
|
boost::asio::strand object. By wrapping the handlers using the same boost::asio::strand, we
|
|
are ensuring that they cannot execute concurrently.
|
|
|
|
\until }
|
|
\until }
|
|
|
|
In a multithreaded program, the handlers for asynchronous
|
|
operations should be synchronised if they access shared resources. In this
|
|
tutorial, the shared resources used by the handlers (<tt>print1</tt> and
|
|
<tt>print2</tt>) are <tt>std::cout</tt> and the <tt>count_</tt> data member.
|
|
|
|
\until };
|
|
|
|
The <tt>main</tt> function now causes boost::asio::io_service::run() to
|
|
be called from two threads: the main thread and one additional thread. This is
|
|
accomplished using an boost::thread object.
|
|
|
|
Just as it would with a call from a single thread, concurrent calls to
|
|
boost::asio::io_service::run() will continue to execute while there is "work" left to
|
|
do. The background thread will not exit until all asynchronous operations have
|
|
completed.
|
|
|
|
\until }
|
|
|
|
See the \ref tuttimer5src "full source listing" \n
|
|
Return to the \ref index "tutorial index" \n
|
|
Previous: \ref tuttimer4 \n
|
|
|
|
*/
|
|
|
|
/**
|
|
\page tuttimer5src Source listing for Timer.5
|
|
\include timer5/timer.cpp
|
|
Return to \ref tuttimer5
|
|
*/
|