Discussion:
[asio-users] *this as coroutine completion handler
Jim Minter
2013-08-26 15:49:34 UTC
Permalink
Hi all,

I've cobbled together a simple TCP echo service using boost 1.54 asio
and stackless coroutines; source code below. It works for me (Fedora
19, stock gcc 4.8), but I was wondering if anyone can shed light on the
following questions?

Where incoming connections are accepted in do_accept::operator(), I can
use *this as the async completion handler:

yield acceptor->async_accept(new_connection->socket(), *this);

However, this tactic requires that the do_accept class is
copy-constructible, and indeed I can see that the value of 'this' varies
at different points during execution of the operator() coroutine.

In echo_server::operator(), using *this as the completion handler would
cause problems since incoming data is read into one instance of the
class, and is not available when being written later on in the
coroutine. Here I've used:

yield s.async_read_some(buffer(c),
bind(mem_fn(&echo_server::operator()), shared_from_this(), _1, _2));
yield async_write(s, buffer(c, n),
bind(mem_fn(&echo_server::operator()), shared_from_this(), _1, _2));

I'm wondering about the differences in these approaches. Although in
some ways the *this approach looks more readable it seems suboptimal
that the coroutine class must be copy-constructible and confusing that
there should be multiple instances of it in memory. Am I missing a
point: is this a fundamental requirement of coroutine implementation?
If so, should I ditch the boost::bind approach and put my data buffer
behind a shared_ptr?

Many thanks,

Jim Minter

== 8< ==

#include <boost/asio.hpp>
#include <boost/asio/yield.hpp>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>


using namespace boost::asio;
using boost::asio::ip::tcp;
using boost::system::error_code;
using boost::bind;
using boost::enable_shared_from_this;
using boost::mem_fn;
using boost::shared_ptr;


class echo_server : public coroutine,
public enable_shared_from_this<echo_server> {
echo_server(io_service &io_service)
: s(io_service) {
}

public:
static shared_ptr<echo_server> create(io_service &io_service) {
return shared_ptr<echo_server>(new echo_server(io_service));
}

tcp::socket &socket() {
return s;
}

void operator()(error_code ec = error_code(), size_t n = 0) {
if(!ec) reenter(this) {
while(1) {
yield s.async_read_some(buffer(c),
bind(mem_fn(&echo_server::operator()), shared_from_this(), _1, _2));
yield async_write(s, buffer(c, n),
bind(mem_fn(&echo_server::operator()), shared_from_this(), _1, _2));
}
}
}

private:
tcp::socket s;
char c[1024];
};


class do_accept : public coroutine {
public:
do_accept(io_service &io_service)
: acceptor(new tcp::acceptor(io_service, tcp::endpoint(tcp::v4(),
7))) {
}

void operator()(error_code ec = error_code(), size_t = 0) {
if(!ec) reenter(this) {
while(1) {
new_connection = echo_server::create(acceptor->get_io_service());
yield acceptor->async_accept(new_connection->socket(), *this);
fork (*new_connection)();
}
}
}

private:
shared_ptr<tcp::acceptor> acceptor;
shared_ptr<echo_server> new_connection;
};


int
main() {
try {
io_service io_service;
(do_accept(io_service))();
io_service.run();

} catch(std::exception &e) {
fprintf(stderr, "%s\n", e.what());
}

return 0;
}
Gruenke,Matt
2013-08-26 16:07:56 UTC
Permalink
Eek! Isn't your original instance of do_accept out of scope by the time io_service.run() is called? If boost::function<> didn't copy it, you'd be in a world of hurt by the time your completion handler got called, because the original value of 'this' would be invalid.

If you instead created a named variable of type do_accept and separately called its operator(), then you could use 'this' instead of shared_from_this(), since you'd know 'this' would remain valid for the duration of io_service.run(). I'm not saying there's anything wrong with shared_from_this(), but I think it's important to understand what it does and why you're using it.


Matt


-----Original Message-----
From: Jim Minter [mailto:***@minter.co.uk]
Sent: August 26, 2013 11:50
To: asio-***@lists.sourceforge.net
Subject: [asio-users] *this as coroutine completion handler

Hi all,

I've cobbled together a simple TCP echo service using boost 1.54 asio and stackless coroutines; source code below. It works for me (Fedora 19, stock gcc 4.8), but I was wondering if anyone can shed light on the following questions?

Where incoming connections are accepted in do_accept::operator(), I can use *this as the async completion handler:

yield acceptor->async_accept(new_connection->socket(), *this);

However, this tactic requires that the do_accept class is copy-constructible, and indeed I can see that the value of 'this' varies at different points during execution of the operator() coroutine.

In echo_server::operator(), using *this as the completion handler would cause problems since incoming data is read into one instance of the class, and is not available when being written later on in the coroutine. Here I've used:

yield s.async_read_some(buffer(c),
bind(mem_fn(&echo_server::operator()), shared_from_this(), _1, _2));
yield async_write(s, buffer(c, n),
bind(mem_fn(&echo_server::operator()), shared_from_this(), _1, _2));

I'm wondering about the differences in these approaches. Although in some ways the *this approach looks more readable it seems suboptimal that the coroutine class must be copy-constructible and confusing that there should be multiple instances of it in memory. Am I missing a
point: is this a fundamental requirement of coroutine implementation?
If so, should I ditch the boost::bind approach and put my data buffer behind a shared_ptr?

Many thanks,

Jim Minter

== 8< ==

#include <boost/asio.hpp>
#include <boost/asio/yield.hpp>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>


using namespace boost::asio;
using boost::asio::ip::tcp;
using boost::system::error_code;
using boost::bind;
using boost::enable_shared_from_this;
using boost::mem_fn;
using boost::shared_ptr;


class echo_server : public coroutine,
public enable_shared_from_this<echo_server> {
echo_server(io_service &io_service)
: s(io_service) {
}

public:
static shared_ptr<echo_server> create(io_service &io_service) {
return shared_ptr<echo_server>(new echo_server(io_service));
}

tcp::socket &socket() {
return s;
}

void operator()(error_code ec = error_code(), size_t n = 0) {
if(!ec) reenter(this) {
while(1) {
yield s.async_read_some(buffer(c),
bind(mem_fn(&echo_server::operator()), shared_from_this(), _1, _2));
yield async_write(s, buffer(c, n),
bind(mem_fn(&echo_server::operator()), shared_from_this(), _1, _2));
}
}
}

private:
tcp::socket s;
char c[1024];
};


class do_accept : public coroutine {
public:
do_accept(io_service &io_service)
: acceptor(new tcp::acceptor(io_service, tcp::endpoint(tcp::v4(),
7))) {
}

void operator()(error_code ec = error_code(), size_t = 0) {
if(!ec) reenter(this) {
while(1) {
new_connection = echo_server::create(acceptor->get_io_service());
yield acceptor->async_accept(new_connection->socket(), *this);
fork (*new_connection)();
}
}
}

private:
shared_ptr<tcp::acceptor> acceptor;
shared_ptr<echo_server> new_connection; };


int
main() {
try {
io_service io_service;
(do_accept(io_service))();
io_service.run();

} catch(std::exception &e) {
fprintf(stderr, "%s\n", e.what());
}

return 0;
}

------------------------------------------------------------------------------
Introducing Performance Central, a new site from SourceForge and AppDynamics. Performance Central is your source for news, insights, analysis and resources for efficient Application Performance Management.
Visit us today!
http://pubads.g.doubleclick.net/gampad/clk?id=48897511&iu=/4140/ostg.clktrk
_______________________________________________
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

________________________________

This e-mail contains privileged and confidential information intended for the use of the addressees named above. If you are not the intended recipient of this e-mail, you are hereby notified that you must not disseminate, copy or take any action in respect of any information contained in it. If you have received this e-mail in error, please notify the sender immediately by e-mail and immediately destroy this e-mail and its attachments.
Loading...