Using boost::asio::async_write to stream out data
My app writes out a large amount of data to a socket, in a loop, in pieces, say 8kb at a time. I'd like to switch from using Socket::send to use asio::async_write. At a high level, what do I need to do? From my reading, I think I need to: 1. Call async_write instead of send 2. Ensure that my buffers have the correct lifetime - today since my writes are synchronous I re-use the same buffer over and over again - so instead, should I make a new buffer for each write? Whats a good way to ensure the buffer has the correct lifetime, e.g. how do I delete it once its been written out? 3. I understand I can't call async_write again before the first one has been completed (since I don't want my 8kb chunks interleaved), so should I maintain a queue of buffers waiting to go out? Then in my handle_write method should I check the queue, if at least one buffer is in the queue then I should start a new async_write? Thanks! - Alex
Hello,
On Fri, Jul 17, 2009 at 2:13 PM, Alex Black
My app writes out a large amount of data to a socket, in a loop, in pieces, say 8kb at a time.
I'd like to switch from using Socket::send to use asio::async_write. At a high level, what do I need to do? From my reading, I think I need to:
1. Call async_write instead of send
2. Ensure that my buffers have the correct lifetime - today since my writes are synchronous I re-use the same buffer over and over again - so instead, should I make a new buffer for each write? Whats a good way to ensure the buffer has the correct lifetime, e.g. how do I delete it once its been written out?
Yes, you should have a new buffer (at least a buffer that's not being used and will not be used untill the completion of the operation). You delete the buffer (or set it for reuse, if it is the case), at the write callback which is invoked when the async_write completes.
3. I understand I can't call async_write again before the first one has been completed (since I don't want my 8kb chunks interleaved), so should I maintain a queue of buffers waiting to go out?
Then in my handle_write method should I check the queue, if at least one buffer is in the queue then I should start a new async_write?
Well, i don't know how to answer this one, and maybe that's the problem i'm having right now with my tcp async write code, then i'm so confused as you.
Thanks!
- Alex
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
-- Matheus Araújo Aguiar Computer Scientist matheus.pit@gmail.com
3. I understand I can't call async_write again before the first one has been completed (since I don't want my 8kb chunks interleaved), so should I maintain a queue of buffers waiting to go out?
Then in my handle_write method should I check the queue, if at least one buffer is in the queue then I should start a new async_write?
Well, i don't know how to answer this one, and maybe that's the problem i'm having right now with my tcp async write code, then i'm so confused as you.
Actually, after thinking a little i guess it is this: If you call async_write from the same thread, then you should not worry about your chunks getting interleaved. But that's not the case if you call async_write from more that one thread and to prevent that you could use a mutex. Hope it helps, Kindly, -- Matheus Araújo Aguiar Computer Scientist matheus.pit@gmail.com
So, If I'm calling aysnc_write from the same thread each time, and I'm not waiting for previous writes to complete before starting a new write, how do I know when to free my buffer? The problem seems to be that the handle_write method will be called to let me know when the write is complete, but I won't have any information in handle_write to know *which* buffer to free... any suggestions? thx - Alex ________________________________ From: boost-users-bounces@lists.boost.org [mailto:boost-users-bounces@lists.boost.org] On Behalf Of Matheus Araújo Aguiar Sent: Friday, July 17, 2009 2:11 PM To: boost-users@lists.boost.org Subject: Re: [Boost-users] Using boost::asio::async_write to stream out data 3. I understand I can't call async_write again before the first one has been completed (since I don't want my 8kb chunks interleaved), so should I maintain a queue of buffers waiting to go out? Then in my handle_write method should I check the queue, if at least one buffer is in the queue then I should start a new async_write? Well, i don't know how to answer this one, and maybe that's the problem i'm having right now with my tcp async write code, then i'm so confused as you. Actually, after thinking a little i guess it is this: If you call async_write from the same thread, then you should not worry about your chunks getting interleaved. But that's not the case if you call async_write from more that one thread and to prevent that you could use a mutex. Hope it helps, Kindly, -- Matheus Araújo Aguiar Computer Scientist matheus.pit@gmail.com
On Fri, Jul 17, 2009 at 8:32 PM, Alex Black
So, If I'm calling aysnc_write from the same thread each time, and I'm not waiting for previous writes to complete before starting a new write, how do I know when to free my buffer?
The problem seems to be that the handle_write method will be called to let me know when the write is complete, but I won't have any information in handle_write to know *which* buffer to free... any suggestions?
thx
- Alex
you could pass the pointer to the buffer as an argument of the "handle_write method". Best Regards, -- Matheus Araújo Aguiar Computer Scientist matheus.pit@gmail.com
Thanks Matheus, thats very slick, works well.
________________________________
From: boost-users-bounces@lists.boost.org [mailto:boost-users-bounces@lists.boost.org] On Behalf Of Matheus Araújo Aguiar
Sent: Friday, July 17, 2009 8:16 PM
To: boost-users@lists.boost.org
Subject: Re: [Boost-users] Using boost::asio::async_write to stream out data
On Fri, Jul 17, 2009 at 8:32 PM, Alex Black
Alex Black wrote:
So, If I'm calling aysnc_write from the same thread each time, and I'm not waiting for previous writes to complete before starting a new write, how do I know when to free my buffer?
The problem seems to be that the handle_write method will be called to let me know when the write is complete, but I won't have any information in handle_write to know *which* buffer to free... any suggestions?
thx
- Alex
Alex - I recommend that you take a look at the asio chat example: http://www.boost.org/doc/libs/1_39_0/doc/html/boost_asio/examples.html#boost... It has all kinds of little techniques to learn from. Just to be clear on the "threads" reading/writing... I don't think you have "write thread" and a "read thread". Typically you have a thread (or thread pool) that is working on events. That thread (thread pool) calls io_service::run. There is some async read/write that is initiated often from a different thread, but once stuff starts going your completion routines (called from the io_service::run thread or pool) keeps things going. Typically you have some queue (like a std::deque) of input/output buffers to be processed. You will see a messages to send deque in the chat example. Calling async_write multiple times for a single socket without controlling completion would be considered "bad practice". // bad practice. wont allow your server to scale async_write( socket_, some_buffer1, some_completion_routine ); async_write( socket_, some_buffer2, some_completion_routine ); Instead, you should queue items to be written and then utilize the completion routine to start another async_write if there is something in the queue. Just like in the examples. Just to be clear, a socket has a message queue. The queue would be pop'd in the completion routine which would allow you to handle which buffer to free. Ideally you just use RAII and let the problem be solved for you automagically. If you still want to go the route of willy-nilly calling async_write, you can also get your buffer at the completion routine by changing the completion routine's signature and using boost::bind. Something like this: async_write( socket_, asio_buffer, bind( &write_done, shared_from_this(), raw_buffer, placeholders::error ) ); void write_done( char* buffer, const error_code* error ) { .... delete[] buffer; // or whatever it is } I hope this helps some. michael -- ---------------------------------- Michael Caisse Object Modeling Designs www.objectmodelingdesigns.com
Thanks Michael, I'll look at that example and consider your suggestions. What is RAII?
-----Original Message----- From: boost-users-bounces@lists.boost.org [mailto:boost-users-bounces@lists.boost.org] On Behalf Of Michael Caisse Sent: Friday, July 17, 2009 8:19 PM To: boost-users@lists.boost.org Subject: Re: [Boost-users] Using boost::asio::async_write to stream out data
Alex Black wrote:
So, If I'm calling aysnc_write from the same thread each time, and I'm not waiting for previous writes to complete before starting a new write, how do I know when to free my buffer?
The problem seems to be that the handle_write method will be called to let me know when the write is complete, but I won't have any information in handle_write to know *which* buffer to free... any suggestions?
thx
- Alex
Alex -
I recommend that you take a look at the asio chat example:
http://www.boost.org/doc/libs/1_39_0/doc/html/boost_asio/examp les.html#boost_asio.examples.chat
It has all kinds of little techniques to learn from.
Just to be clear on the "threads" reading/writing... I don't think you have "write thread" and a "read thread". Typically you have a thread (or thread pool) that is working on events. That thread (thread pool) calls io_service::run. There is some async read/write that is initiated often from a different thread, but once stuff starts going your completion routines (called from the io_service::run thread or pool) keeps things going.
Typically you have some queue (like a std::deque) of input/output buffers to be processed. You will see a messages to send deque in the chat example.
Calling async_write multiple times for a single socket without controlling completion would be considered "bad practice".
// bad practice. wont allow your server to scale async_write( socket_, some_buffer1, some_completion_routine ); async_write( socket_, some_buffer2, some_completion_routine );
Instead, you should queue items to be written and then utilize the completion routine to start another async_write if there is something in the queue. Just like in the examples. Just to be clear, a socket has a message queue.
The queue would be pop'd in the completion routine which would allow you to handle which buffer to free. Ideally you just use RAII and let the problem be solved for you automagically.
If you still want to go the route of willy-nilly calling async_write, you can also get your buffer at the completion routine by changing the completion routine's signature and using boost::bind. Something like this:
async_write( socket_, asio_buffer, bind( &write_done, shared_from_this(), raw_buffer, placeholders::error ) );
void write_done( char* buffer, const error_code* error ) { .... delete[] buffer; // or whatever it is }
I hope this helps some.
michael
--
---------------------------------- Michael Caisse Object Modeling Designs www.objectmodelingdesigns.com
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
Alex Black wrote:
Thanks Michael, I'll look at that example and consider your suggestions.
What is RAII?
Resource Acquisition Is Initialization http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization Good luck with your project. -- ---------------------------------- Michael Caisse Object Modeling Designs www.objectmodelingdesigns.com
Hello Alex - Alex Black wrote:
My app writes out a large amount of data to a socket, in a loop, in pieces, say 8kb at a time.
I'd like to switch from using Socket::send to use asio::async_write. At a high level, what do I need to do? From my reading, I think I need to:
1. Call async_write instead of send
2. Ensure that my buffers have the correct lifetime - today since my writes are synchronous I re-use the same buffer over and over again - so instead, should I make a new buffer for each write? Whats a good way to ensure the buffer has the correct lifetime, e.g. how do I delete it once its been written out?
If you are newing off some space in the heap you could just use a queue of shared_ptr to your data and let the reference counting take care of it for you. In your completion routine for the write simply pop off the entry for the buffer just written and then the buffer would be deleted. You may find that using a pool technique will be much better depending on your application. The same concept would hold, the memory would be returned to the pool at the completion routine for the write.
3. I understand I can't call async_write again before the first one has been completed (since I don't want my 8kb chunks interleaved), so should I maintain a queue of buffers waiting to go out?
Correct. You need to check your queue in the completion routine and kick off another async_write if required. I typically use a std::deque to handle this for me. Sometimes just a std::deque< std::string > depending on the application.
Then in my handle_write method should I check the queue, if at least one buffer is in the queue then I should start a new async_write?
Exactly. michael -- ---------------------------------- Michael Caisse Object Modeling Designs www.objectmodelingdesigns.com
3. I understand I can't call async_write again before the first one has
been completed (since I don't want my 8kb chunks interleaved), so should I maintain a queue of buffers waiting to go out?
Correct. You need to check your queue in the completion routine and kick off another async_write if required. I typically use a std::deque to handle this for me. Sometimes just a std::deque< std::string > depending on the application.
Then in my handle_write method should I check the queue, if at least one
buffer is in the queue then I should start a new async_write?
Exactly.
I disagree. I don't have much technical knowledge about how it is done on the O.S layer, but for what i've experienced, you can call several async_write, one after another, without having to wait for the completion of any of the operations. If you call async_write from diferent threads, then data could be interleaved. And with TCP you may get data concatenated, but it has to do with the protocol. Maybe i'm wrong, but for now that's what i think. A third (and fourth, fifth and so on) opinion would be nice. -- Matheus Araújo Aguiar Computer Scientist matheus.pit@gmail.com
Matheus Araújo Aguiar wrote:
Then in my handle_write method should I check the queue, if at least one buffer is in the queue then I should start a new async_write?
Exactly.
I disagree. I don't have much technical knowledge about how it is done on the O.S layer, but for what i've experienced, you can call several async_write, one after another, without having to wait for the completion of any of the operations. If you call async_write from diferent threads, then data could be interleaved. And with TCP you may get data concatenated, but it has to do with the protocol.
Maybe i'm wrong, but for now that's what i think. A third (and fourth, fifth and so on) opinion would be nice.
Matheus - This is true if you have only called io_sevice::run from one thread. If you have multiple threads processing events you will have issues because each thread may be writing to the socket at the same time. This would not be a TCP layer issue... it would be an application problem. Best Regards - -- ---------------------------------- Michael Caisse Object Modeling Designs www.objectmodelingdesigns.com
Thanks for the feedback on this Matheus and Michael. I think I have a good idea now of what I need to do. The point about whether or not you can call async_write before the first one has completed seems important. Right now, I've only got one thread calling io_service.run(), and only 1 thread calling async_write, so it sounds like I don't have to wait, nor do I need to queue, but I do need to manage the buffers' lifetime properly. - Alex
-----Original Message----- From: boost-users-bounces@lists.boost.org [mailto:boost-users-bounces@lists.boost.org] On Behalf Of Michael Caisse Sent: Friday, July 17, 2009 3:14 PM To: boost-users@lists.boost.org Subject: Re: [Boost-users] Using boost::asio::async_write to stream out data
Matheus Araújo Aguiar wrote:
Then in my handle_write method should I check the
queue, if at
least one buffer is in the queue then I should start a new
async_write?
Exactly.
I disagree. I don't have much technical knowledge about how
it is done
on the O.S layer, but for what i've experienced, you can call several async_write, one after another, without having to wait for the completion of any of the operations. If you call async_write from diferent threads, then data could be interleaved. And with TCP you may get data concatenated, but it has to do with the protocol.
Maybe i'm wrong, but for now that's what i think. A third (and fourth, fifth and so on) opinion would be nice.
Matheus -
This is true if you have only called io_sevice::run from one thread. If you have multiple threads processing events you will have issues because each thread may be writing to the socket at the same time. This would not be a TCP layer issue... it would be an application problem.
Best Regards -
--
---------------------------------- Michael Caisse Object Modeling Designs www.objectmodelingdesigns.com
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
Matheus -
This is true if you have only called io_sevice::run from one thread. If you have multiple threads processing events you will have issues because each thread may be writing to the socket at the same time. This would not be a TCP layer issue... it would be an application problem.
Best Regards -
I see your point Michael, but in that case wouldn't the io_service::run threads be just picking results from the O.S and (maybe) requesting operations ? I think the threads wouldn't write to the socket itself, the O.S would do that. Regards, -- Matheus Araújo Aguiar Computer Scientist matheus.pit@gmail.com
The following thread can be helpful: http://www.nabble.com/-asio--async-write-handler-is-not-called-until-the-soc...
On Sat, Jul 18, 2009 at 2:59 PM, Igor R
The following thread can be helpful:
http://www.nabble.com/-asio--async-write-handler-is-not-called-until-the-soc... _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
Many thanx, Igor, this thread is really helpful. Finally i understand it and see i was wrong in admitting that many async operations could be requested without waiting for completion. best regards, -- Matheus Araújo Aguiar Computer Scientist matheus.pit@gmail.com
I'm not 100% clear on this yet, is it safe for my thread to call async_write over and over again without waiting for previous writes to complete?
Is it safe to do this on two different threads writing each out on their own socket?
thx
- Alex
________________________________
From: boost-users-bounces@lists.boost.org [mailto:boost-users-bounces@lists.boost.org] On Behalf Of Matheus Araújo Aguiar
Sent: Monday, July 20, 2009 10:39 AM
To: boost-users@lists.boost.org
Subject: Re: [Boost-users] Using boost::asio::async_write to stream out data
On Sat, Jul 18, 2009 at 2:59 PM, Igor R
I'm not 100% clear on this yet, is it safe for my thread to call async_write over and over again without waiting for previous writes to complete?
Is it safe to do this on two different threads writing each out on their own socket?
It's safe to issue simultanious async_write's for different sockets and buffers. It's not safe to do this for the same socket, because async_write() does not guarantee that all the data is sent at once.
So, doing this is not allowed: async_write( socket, ... ); async_write( socket, ... ); Instead, I have to queue the 2nd buffer until the first write is done?
-----Original Message----- From: boost-users-bounces@lists.boost.org [mailto:boost-users-bounces@lists.boost.org] On Behalf Of Igor R Sent: Thursday, July 23, 2009 9:41 AM To: boost-users@lists.boost.org Subject: Re: [Boost-users] Using boost::asio::async_write to stream out data
I'm not 100% clear on this yet, is it safe for my thread to call async_write over and over again without waiting for previous writes to complete?
Is it safe to do this on two different threads writing each out on their own socket?
It's safe to issue simultanious async_write's for different sockets and buffers. It's not safe to do this for the same socket, because async_write() does not guarantee that all the data is sent at once. _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
participants (4)
-
Alex Black
-
Igor R
-
Matheus Araújo Aguiar
-
Michael Caisse