blob: 7c5a0a099a67753e668832307e1d83866b767fa6 [file] [log] [blame]
//
// Copyright (c) 2003-2016 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 uses 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 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 asio::io_service
Next we declare an object of type 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 asio::deadline_timer
In this simple example we perform a blocking wait on the timer.
That is, the call to 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 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 asio::deadline_timer
Next, instead of doing a blocking wait as in tutorial Timer.1,
we call the 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 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 asio::io_service::run().
Therefore unless the asio::io_service::run() function is called the callback for
the asynchronous wait completion will never be invoked.
The 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 asio::io_service::run(). For example, if we had omitted the above call
to asio::deadline_timer::async_wait(), the io_service would not have had any
work to do, and consequently 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 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 \ref boost_bind function is used to associate the extra parameters
with your callback handler. The asio::deadline_timer::async_wait() function
expects a handler function (or function object) with the signature
<tt>void(const asio::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 \ref boost_bind.
In this example, the asio::placeholders::error argument to \ref boost_bind is a
named placeholder for the error object passed to the handler. When initiating
the asynchronous operation, and if using \ref 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 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 asio::deadline_timer
As in Step 4, when making the call to
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 \ref 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, \ref 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
asio::error_code&)</tt>.
You will note that the 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 asio::io_service::strand class to
synchronise callback handlers in a multithreaded program.
The previous four tutorials avoided the issue of handler synchronisation by
calling the 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
asio::io_service::run(). Consequently, calling 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 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 asio::deadline_timer members, the
constructor initialises the <tt>strand_</tt> member, an object of type
asio::io_service::strand.
An asio::io_service::strand is an executor that 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 asio::io_service::run(). Of course, the
handlers may still execute concurrently with other handlers that were
<b>not</b> dispatched through an asio::io_service::strand, or were dispatched
through a different asio::io_service::strand object.
\until {
When initiating the asynchronous operations, each callback handler is "bound"
to an asio::io_service::strand object. The
asio::io_service::strand::bind_executor() function returns a new handler that
automatically dispatches its contained handler through the
asio::io_service::strand object. By binding the handlers to the same
asio::io_service::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 asio::io_service::run() to
be called from two threads: the main thread and one additional thread. This is
accomplished using an asio::thread object.
Just as it would with a call from a single thread, concurrent calls to
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
*/