Discussion:
[asio-users] strands owned by objects posting themselves
Amir Taaki
2012-06-16 04:58:12 UTC
Permalink
Hi,

Typically in asio we post member callbacks of objects using shared_from_this() to ensure that the object doesn't become destroyed before the posted handler executes:

strand_.post(std::bind(&myobject::foo, shared_from_this(), 110, xyz));

Christopher Kohlhoff's boostcon talk on boost::asio, he discussed two common patterns used in asio. One of those was "the buck stops here". In This pattern might be used for the interface of a class which posts the work to another thread, serialising access to internal shared data through a strand.

class something
  : public std::enable_shared_from_this<something>
{
public:
   void foo(int x)
   {
        strand_.post(std::bind(&something::do_foo, x));
   }
private:
    void do_foo(int x)
    {
        // do something non-threadsafe shared_state_ member

    }

    io_service::strand strand_;
    data shared_state_;

};

Now if you have 2 object posting between each other's strands, then you get a problem when an io_service for one strand goes out of scope but not for the other (a strand pointing to a destructed io_service). So then you keep the io_service alive with the strand.

class async_strand
{
 // ...
private:
    typedef std::shared_ptr<io_service> io_service_ptr;
    io_service_ptr service_;
    io_service::strand strand_;

};

But now we have created a circular reference whereby something is posting itself to an object that it owns. The destructor of the class 'something' will not get called.

What are the typical ways of designing programs that own strands? All the examples I've seen are very simple with a single io_service that always outlives the strand encapsulating the class members. But that model doesn't hold with multiple strands from different io_service's that are posting to each other.
Marat Abrarov
2012-06-16 05:36:25 UTC
Permalink
Hi, Amir.
Post by Amir Taaki
What are the typical ways of designing programs that own strands? All the examples I've seen are very
simple with a single io_service that always outlives the strand encapsulating the class members. But
that model doesn't hold with multiple strands from different io_service's that are posting to each
other.
https://sourceforge.net/projects/asio-samples/

Project echo_server implements 2 thread pools each based on its own instance of asio::io_service:
1. thread pool for session management (creation, asyn_accept, registration, control);
2. thread pool for sessions' activity (IO, state management).

Project asio-samples often uses "the buck stops here" and when it's come to use 2 different instances of
asio::io_service the boost::weak_ptr (and the right destruction order - see ma::echo::server::qt::io_service_chain class
at qt_echo_project) helps.

Regards,
Marat Abrarov.
Amir Taaki
2012-06-16 17:41:04 UTC
Permalink
I'm looking at the example for your echo server. The session_manager takes an io_service& - this is a bug if the parent io_service can go out of scope before the strand does.

It doesn't address the problem I was asking:

- the io_service can be destroyed before the strand, so you store a std::shared_ptr<io_service> to keep the io_service alive.
- objects post a shared_from_this() to keep themselves alive for the duration of that piece of work.
- the strand (which io_service always outlives) keeps a refcounted pointer to the object which owns it because of the posted work.
- this creates a loop in the reference counted pointers (io_service and our object)
- objects are never destroyed.



----- Original Message -----
From: Marat Abrarov <***@mail.ru>
To: 'Amir Taaki' <***@yahoo.com>; asio-***@lists.sourceforge.net
Cc:
Sent: Saturday, June 16, 2012 7:36 AM
Subject: RE: [asio-users] strands owned by objects posting themselves

Hi, Amir.
Post by Amir Taaki
What are the typical ways of designing programs that own strands? All the examples I've seen are very
simple with a single io_service that always outlives the strand encapsulating the class members. But
that model doesn't hold with multiple strands from different io_service's that are posting to each
other.
https://sourceforge.net/projects/asio-samples/

Project echo_server implements 2 thread pools each based on its own instance of asio::io_service:
1. thread pool for session management (creation, asyn_accept, registration, control);
2. thread pool for sessions' activity (IO, state management).

Project asio-samples often uses "the buck stops here" and when it's come to use 2 different instances of
asio::io_service the boost::weak_ptr (and the right destruction order - see ma::echo::server::qt::io_service_chain class
at qt_echo_project) helps.

Regards,
Marat Abrarov.
Marat Abrarov
2012-06-16 18:22:22 UTC
Permalink
Post by Amir Taaki
I'm looking at the example for your echo server. The session_manager takes an io_service& - this is a
bug if the parent io_service can go out of scope before the strand does.
If the parent io_service goes out of scope than it is a bug - current design is based on the idea that io_service
instance has to outlive all other objects tied with it (like session/session_manager).

The problem you asked has no solution. The only thing that can break shared_ptr-loop is weak_ptr.

Regards,
Marat Abrarov.
Amir Taaki
2012-06-17 11:40:01 UTC
Permalink
Post by Marat Abrarov
If the parent io_service goes out of scope than it is a bug - current design is based on the idea that io_service
instance has to outlive all other objects tied with it (like session/session_manager).
How is that a bug? Is there is no way to guarantee that the lifetime of an io_service outlives the strands and objects without keeping a shared_ptr reference to it?



----- Original Message -----
From: Marat Abrarov <***@mail.ru>
To: asio-***@lists.sourceforge.net
Cc:
Sent: Saturday, June 16, 2012 8:22 PM
Subject: Re: [asio-users] strands owned by objects posting themselves
Post by Marat Abrarov
I'm looking at the example for your echo server. The session_manager takes an io_service& - this is a
bug if the parent io_service can go out of scope before the strand does.
If the parent io_service goes out of scope than it is a bug - current design is based on the idea that io_service
instance has to outlive all other objects tied with it (like session/session_manager).

The problem you asked has no solution. The only thing that can break shared_ptr-loop is weak_ptr.

Regards,
Marat Abrarov.



------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and
threat landscape has changed and how IT managers can respond. Discussions
will include endpoint security, mobile security and the latest in malware
threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
Marat Abrarov
2012-06-17 13:27:13 UTC
Permalink
Post by Amir Taaki
Post by Marat Abrarov
instance has to outlive all other objects tied with it (like session/session_manager).
How is that a bug? Is there is no way to guarantee that the lifetime of an io_service outlives the
strands and objects without keeping a shared_ptr reference to it?
In this code:
~~~~~~~~~~~
{
asio::io_service ios;
session_manager_ptr sm(session_manager::create(ios));
...
}
~~~~~~~~~~~
ios has to outlive sm. This is by design (for every class in asio-samples, that takes asio::io_service& as parameter of
constructor). To follow this (to guarantee life scope of ios) is the duty of programmer using session_manager class.

So this code is broken:
~~~~~~~~~~~
session_manager_ptr buggy_sm;
{
asio::io_service ios;
session_manager_ptr sm(session_manager::create(ios));
buggy_sm = sm;
...
}
~~~~~~~~~~~
and this is the fault of programmer that wrote such code. Not my fault.

Because of echo_server uses 2 different instances of asio::io_service ma::echo::server::session_manager class works with
both of them:
1) io_service_ - for internal needs;
2) session_io_service_ - by calling ma::echo::server::session::async_XXX() (async_XXX() uses session_io_service_ through
asio::io_service::strand).

In this case I use session_manager_weak_ptr for callbacks from session to session_manager and restrict life scopes of
used instances of asio::io_service - session_manager::session_io_service_ has to outlive session_manager::io_service_
(see asio_samples/src/echo_server/main.cpp: 153).

So I have 2 different instances of asio::io_service with dependent life scopes. To build such dependency only by means
of documentation isn't safe enough. This is the reason I use ma::echo::server::qt::io_service_chain class in
qt_echo_server project. io_service_chain class destroys instances of asio::io_service in the right order. But if
somebody writes buggy code like I've written above then io_service_chain doesn't help him.

Regards,
Marat Abrarov.

Loading...