Boost logo

Boost :

Subject: Re: [boost] Should pass boost::asio::io_service by raw pointer or smart pointer?
From: Richard Hodges (hodges.r_at_[hidden])
Date: 2018-12-20 06:08:10


Adding some lessons learned over the years to Vinnie's answer, because it's
not immediately obvious from the ASIO documentation...

You should normally think of an io_context as a global dependency of the
application. Create it in (or near) main() and pass a reference to it to
every component of your application that needs access to an io_context.

There are a couple of models for using ASIO:

* one io_service, one thread - initialise your io_service with an argument
of (1) to hint to ASIO that it can optimise for singfle-threaded code
* one io_service, N threads - default-initialise your io_service and use
strands to prevent contention in handlers.
* N threads with one io_service per thread - a very efficient model, which
will require you to manually load-balance connections by allocating them to
the io_context associated with the least current load. This should probably
not be attempted on your first try with ASIO.

There are some other things that are not always obvious:

* Many people keep shared_ptr's to sockets, deadline_timers and so on. This
is an error. All ASIO io objects are moveable. Store by value.

* SSL contexts are similar to io_contexts. There should be one (or perhaps
two if you're both accepting inbound connections and making outbound
connections) per application. Pass them by reference to your client/server
objects. Storing an ssl::context in an object makes it non-moveable and is
an error.

* Good ASIO development is aided by the principle of dependency injection.

* ASIO is easy to test - look at the methods io_context::poll(),
io_context::run_one(), io_context::stopped(), io_context::restart() and
io_context::strand::running_in_this_thread(). You can essentially
single-step ASIO in your unit tests. This is a strong reason for using
dependency injection (i.e. pass configuration data, contexts and executors
by reference).

* If you're multi-threading you'll either want to protect your objects with
mutexes (not so efficient) or strands (really efficient). However, don't
mix the models. If you're using strands then all access to your object from
outside should be via an async function which takes a closure and posts an
implementation of itself to the current object's strand. This ensures that
everything runs in the correct thread (which you can assert with
strand::running_in_this_thread() in your handler functions).

On Thu, 20 Dec 2018 at 11:14, Vinnie Falco via Boost <boost_at_[hidden]>
wrote:

> On Wed, Dec 19, 2018 at 4:30 PM hh h via Boost <boost_at_[hidden]>
> wrote:
> > In many examples and even implementations, programmers like to pass
> > boost::asio::io_service by raw pointer:
>
> Those examples are wrong. `io_context` should be passed by reference.
>
> > Will this cause issues to pass raw pointer for large size of classes,
> > should it be replaced by passing smart pointer:
>
> A smart pointer is less efficient than a pointer, all else equal. But
> you shouldn't be using any form of pointer, the io_context (legacy
> name: io_service) is passed by reference. All of the Beast and Asio
> examples demonstrate this:
>
> <https://github.com/boostorg/beast/tree/develop/example>
> <https://github.com/boostorg/asio/tree/develop/example>
>
> Regards
>
> _______________________________________________
> Unsubscribe & other changes:
> http://lists.boost.org/mailman/listinfo.cgi/boost
>

-- 
Richard Hodges
hodges.r_at_[hidden]
office: +442032898513
home: +376841522
mobile: +376380212 (this will be *expensive* outside Andorra!)
skype: madmongo
facebook: hodges.r

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk