[asio] writing composed operations example

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

[asio] writing composed operations example

Boost - Users mailing list
Hi everyone,

as a long time asio user I often had needs to wrap up repeating patterns of async calls for re-use. Normally I went for some kind of specialized object that wraps smaller pieces but I recently came across a series of examples which try to explain how own composed operations may be created. 

Now, looking at the examples they are of course way over my head but since own composed operations would be very much exactly what I need I thought I'd give it a shot. But as I now look at this example here: https://www.boost.org/doc/libs/1_71_0/doc/html/boost_asio/example/cpp11/operations/composed_8.cpp I notice something that I hope somebody can explain.

In the coroutine implementation it says:
delay_timer_->expires_after(std::chrono::seconds(1));
yield delay_timer_->async_wait(std::move(self));
if (error)
    break;

yield boost::asio::async_write(socket_,
    boost::asio::buffer(*encoded_message_), std::move(self));
I'm puzzled about the double std::move() of self. In the comments above it says that self is a reference to an intermediate completion handler. How can this be moved twice?
Can anyone give me hint about what this means?

I would also appreciate any hints in general about own composed operations. Perhaps there are other approaches that are a bit less intimidating?

Cheers,
Stephan

_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|

Re: [asio] writing composed operations example

Boost - Users mailing list

Hello Stephan,

 

I have use stackless coroutines a little bit, and I think, I can explain.

 

> I'm puzzled about the double std::move() of self. In the comments above it says that self is a reference to an intermediate completion handler. How can this be moved twice?

> Can anyone give me hint about what this means?

 

The line

yield delay_timer_->async_wait(std::move(self));

 

terminates the function. If the async operation is complete, the function gets called again and continues at the next statement, but this time with the new, moved self.

 

So the next line

yield boost::asio::async_write(…, …, std::move(self));

 

can move self a 2nd time. When the 2nd async operation completes and execution continues on the next line, the statement

                self.complete(error);

 

finally operates on the 3rd instance of self.

 

I hope this helps,

 

Mario

 


_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|

Re: [asio] writing composed operations example

Boost - Users mailing list
In reply to this post by Boost - Users mailing list
On Mon, 2 Sep 2019 at 10:30, Stephan Menzel via Boost-users
<[hidden email]> wrote:
> I'm puzzled about the double std::move() of self. In the comments above it says that self is a reference to an intermediate completion handler. How can this be moved twice?
> Can anyone give me hint about what this means?

There is anything in particular from composed_8.cpp that you find
confusing? The multiple moving thing is no different than in
composed_6.cpp or composed_7.cpp.
For the explanation I will mention line numbers, it may be easier to
look at https://github.com/boostorg/asio/blob/boost-1.71.0/example/cpp11/operations/composed_8.cpp

* In line 150 an async_write_messages_implementation object is created: objectA
* In line 89 objectA is moved into objectB.
  async_wait stores objectB. This should not surprise you, even if
boost::asio::async_compose hides the fact a bit, async_write_messages
is doing the same. Where is the lambda created in line 167 being
stored? It's being stored by async_write_messages. async_compose is
creating the class able to store such a lambda, and you are passing
the lambda to it in line 153 (as "token").
* After line 89 line 90 is NOT executed, the "yield" makes it leave
the "reenter" body. And since there is nothing else after that
operator() returns. objectA gets out of scope and destroyed here.
* After one second the wait finishes. And it executes
objectB.operator(). Once it reaches the "reenter" the magic of
boost::asio::coroutine (it's just a switch) makes it skip to line 90.
* In line 94 objectB is moved into objectC and by the same logic than
before objectB gets destroyed.
* Once async_write does its job objectC.operator() will be called
again, the reenter will continue from line 95 and finish the job.

So no object is moved multiple times, it's a different object each time.
Notice that's why in line 54 and 60 those objects are being created
with unique_ptr. If encoded_message_ were part of
async_write_messages_implementation it would be moved away, and the
async_write from line 93 could be writing the contents of a moved-from
std::string (it's undefined behaviour what exactly it would do).


> I would also appreciate any hints in general about own composed operations. Perhaps there are other approaches that are a bit less intimidating?

Once you get used to it it's not really that intimidating. All the
composed operations follow the same pattern and other than the
"operator()" it's a lot of repetitive boilerplate, quite boring
actually.

I know Boost.Beast is described as a "Portable HTTP, WebSocket, and
network operations using only C++11 and Boost.Asio" library and you
have no reason to look into it unless you need to HTTP/WebSocket. But
if you do you will find nowadays it's much more than that, it's also a
generic library on top of ASIO, slightly higher lever with very useful
helpers... and more user-friendly documentation.

If you look at https://www.boost.org/doc/libs/1_71_0/libs/beast/doc/html/index.html
you will find a whole "Writing Composed Operations" section in the
documentation.

And look at https://www.boost.org/doc/libs/1_71_0/libs/beast/doc/html/beast/ref/boost__beast__stable_async_base.html
What I said about "encoded_message_" having to be a pointer. The
example from the ASIO docs is actually slightly wrong (i.e.
simplified), that std::string should ideally be allocated with the
handler-associated allocator. "Allocators? I did once hear somebody
say the word, I don't know anything else about them" are you saying?
Don't worry, you just need to use Beast allocate_stable() and the
correct allocator will be used.
_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|

Re: [asio] writing composed operations example

Boost - Users mailing list
Hello Christian and Mario, thanks for your responses.

I think I'm much closer to an implementation now.

Am Mo., 2. Sept. 2019 um 13:57 Uhr schrieb Cristian Morales Vega <[hidden email]>:

> I would also appreciate any hints in general about own composed operations. Perhaps there are other approaches that are a bit less intimidating?

Once you get used to it it's not really that intimidating. All the
composed operations follow the same pattern and other than the
"operator()" it's a lot of repetitive boilerplate, quite boring
actually.

Yes, as I was moving forward this turnes out to be the problem now. Essentially, most of the operations I have in mind combine tasks with different completion token types. For example async_resolve() and async_connect().
Once I saw how operator() basically acts as a handler for all the async ops within this question became the most interesting one.


If you look at https://www.boost.org/doc/libs/1_71_0/libs/beast/doc/html/index.html
you will find a whole "Writing Composed Operations" section in the
documentation.

I did have a look at Vince's tutorial but tbh I wanted so see the problems that would be simplified first before jump right ahead.

I guess that's where I'm now ;-)

Cheers,
Stephan


_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users