Discussion:
[asio-users] async_read overwrites buffer even while the handler is running?
Art Tevs
2011-11-10 10:24:22 UTC
Permalink
Hi folks,

I am not sure if there is something completely broken in my
implementation or I just don't quit get something about boost's asio.


Following happens:

sender A sends with async_write() data over the socket (32bytes).
receiver B receives this data into a buffer with async_read(
buffer(&_buf[0], _buf.size()), transfer_all(), handler(...) ). Now
while receiver B handles the data in the handler, sender A send new
data chunk again (32 bytes).
Now using the VisualStudio debugger, I was able to see that the buffer
on the receiver side get altered after the second send of A.
It means, even before receiver B was able to consume the data, it get
already overwritten by the new data...

What is going on here??? Does somebody encountered such behavior
before? Shouldn't the buffer be only altered before handler is called
and not while handler is running. Is there any other way to overcome
this problem?

I am using Boost 1.47.

Best regards and thank you in advance,
art
Igor R
2011-11-10 10:46:29 UTC
Permalink
Post by Art Tevs
sender A sends with async_write() data over the socket (32bytes).
receiver B receives this data into a buffer with async_read(
buffer(&_buf[0], _buf.size()),  transfer_all(), handler(...) ). Now
while receiver B handles the data in the handler, sender A send new
data chunk again (32 bytes).
Now using the VisualStudio debugger, I was able to see that the buffer
on the receiver side get altered after the second send of A.
It means, even before receiver B was able to consume the data, it get
already overwritten by the new data...
Note that several async_read's should never be issued simultaniously,
i.e. never call next async_read *before* the previous call is
completed.
Typically you call async_read at the end of the previous async_read's
completion handler -- or in any case not before the completion handler
handles the relevant buffers.
Art Tevs
2011-11-10 12:08:37 UTC
Permalink
Hi Igor,

hmm, strange actually right, there are second call to the async_read
operation, before even the handler of the first one is executed.
However this read is happening inside of another handler executed as
data has been written to the socket.

So in other words there is an async_read on the socket. Then there is
a write to the socket with async_write and write's handler is
executed. Inside of this handler I call async_read again.
Although this all happens in the io_service thread, there is still a
problem. I suppose, I need somehow to post the async_read operation to
the handler queue of io_service, instead of calling it directly.

cheers,
art
Post by Igor R
Post by Art Tevs
sender A sends with async_write() data over the socket (32bytes).
receiver B receives this data into a buffer with async_read(
buffer(&_buf[0], _buf.size()),  transfer_all(), handler(...) ). Now
while receiver B handles the data in the handler, sender A send new
data chunk again (32 bytes).
Now using the VisualStudio debugger, I was able to see that the buffer
on the receiver side get altered after the second send of A.
It means, even before receiver B was able to consume the data, it get
already overwritten by the new data...
Note that several async_read's should never be issued simultaniously,
i.e. never call next async_read *before* the previous call is
completed.
Typically you call async_read at the end of the previous async_read's
completion handler -- or in any case not before the completion handler
handles the relevant buffers.
------------------------------------------------------------------------------
RSA(R) Conference 2012
Save $700 by Nov 18
Register now
http://p.sf.net/sfu/rsa-sfdev2dev1
_______________________________________________
asio-users mailing list
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
Igor R
2011-11-10 13:29:37 UTC
Permalink
Post by Art Tevs
hmm, strange actually right, there are second call to the async_read
operation, before even the handler of the first one is executed.
However this read is happening inside of another handler executed as
data has been written to the socket.
So in other words there is an async_read on the socket. Then there is
a write to the socket with async_write and write's handler is
executed. Inside of this handler I call async_read again.
Could you write some pseudo-code/sketch to illustrate the order of the
actions better?
IUUC, it looks like this:
main
{
async_read();
async_write();
}

write_hanlder
{
async_read();
}

If the above interpretation is correct, you certainly get 2
async_read's running in parallel, which shouldn't occur.
Post by Art Tevs
Although this all happens in the io_service thread, there is still a
problem. I suppose, I need somehow to post the async_read operation to
the handler queue of io_service, instead of calling it directly.
If you post async_read from write_handler it won't help, because you
still can't guarantee that 2 async_read's won't be in progress. You
just have to call async_read from read_handler.
Art Tevs
2011-11-10 13:46:26 UTC
Permalink
Hi Igor,

yes, you sketched it right. Exactly this was happening in my case.

I think, however, there is no such rule of thumb that async_read
operations should be scheduled from read handlers only, right?
Since otherwise I would not be able pursue any protocol where one have
to read a response from the socket back as soon as you wrote something
to it.
In such scenario cancel() of the parallel read/write operation might
be required, which is in other case not well portable (some troubles
with it in my other thread on the list).
Or there must be some kind of state-based implementation, to make sure
that you never have to cancel any operation (except when timeout, but
here a close() might be also ok).

cheers,
art
Post by Igor R
Post by Art Tevs
hmm, strange actually right, there are second call to the async_read
operation, before even the handler of the first one is executed.
However this read is happening inside of another handler executed as
data has been written to the socket.
So in other words there is an async_read on the socket. Then there is
a write to the socket with async_write and write's handler is
executed. Inside of this handler I call async_read again.
Could you write some pseudo-code/sketch to illustrate the order of the
actions better?
main
{
 async_read();
 async_write();
}
write_hanlder
{
 async_read();
}
If the above interpretation is correct, you certainly get 2
async_read's running in parallel, which shouldn't occur.
Post by Art Tevs
Although this all happens in the io_service thread, there is still a
problem. I suppose, I need somehow to post the async_read operation to
the handler queue of io_service, instead of calling it directly.
If you post async_read from write_handler it won't help, because you
still can't guarantee that 2 async_read's won't be in progress. You
just have to call async_read from read_handler.
------------------------------------------------------------------------------
RSA(R) Conference 2012
Save $700 by Nov 18
Register now
http://p.sf.net/sfu/rsa-sfdev2dev1
_______________________________________________
asio-users mailing list
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
Igor R
2011-11-10 18:54:12 UTC
Permalink
Hi,
Post by Art Tevs
yes, you sketched it right. Exactly this was happening in my case.
I think, however, there is no such rule of thumb that async_read
operations should be scheduled from read handlers only, right?
Well, I'd say it *is* rule of thumb, but not the "iron rule" :). I.e.
you don't have to call async_xxx from the completion handler, but
usually it would be the most safe and convenient way. If you take a
look at the asio examples, in most of cases it's done this way there.
Anyway, you have to ensure that only 1 async_read is in progress. In
my code I do it in the following way:
struct connection
{
// any attempt to async.read should be done through this function only
// you can call it anytime with no worries
activate_read()
{
if (!active_read_)
{
active_read_ = true;
async_read_some(...);
}
}

void read_handler(...)
{
active_read_ = false;
// do things...
if (some_condition())
activate_read();
}

void other_func_requiring_to_read()
{
//...
activate_read();
}
};

In other words, I make a trivial state-machine, which enforces the above rule.
Post by Art Tevs
Since otherwise I would not be able pursue any protocol where one have
to read a response from the socket back as soon as you wrote something to it.
In such scenario cancel() of the parallel read/write operation might
be required, which is in other case not well portable (some troubles
with it in my other thread on the list).
Or there must be some kind of state-based implementation, to make sure
that you never have to cancel any operation (except when timeout, but
here a close() might be also ok).
Actually, in the numerous clients and servers that I wrote with asio I
reallly never had to cancel i/o operations (maybe only during
connection closing process).


HTH,

Igor'.

Loading...