3
votes

I'm writing some code with boost::asio, using asynchronous TCP connections. I've to admit that I have some doubts about it. All these regarding concurrency. Here are some:

  • What happens if I start two or more async_write on the same socket without waiting completion of the first one? Will the handlers (and the async_write) overlap or asio provides serialization and synchronization?

  • Same question of above with async_connect and async_read. In general is it safe to call these functions from different threads (I'm not talking about using different buffers, that's another problem...).

2

2 Answers

3
votes

I assume from your question that you have a single instance of io_service and you want to call async_write() on it from multiple threads.

async_write() ultimately calls the post() method of io_service, which in turn takes a lock and pushes the bits to be written into a work queue, ensuring that the bits won't be written interleaved. Those bits will eventually get written out and the underlying data structure that holds them (a char array or whatever) must remain valid until you get the callback signifying that the write has completed. If you are using the exact same callback function as your completion handler, you will have no way of knowing which of the two writes resulted in that function being called and if that function does anything not thread-safe, behavior may be undefined or incorrect. A popular way to handle this situation is to have a instance of a struct that is the completion handler (just overload the call () operator): you can set the properties of the struct to denote which write it corresponds to and then consult these values when the completion handler is called.

However, absent a shared lock, you have no way of controlling which of the threads actually executes its async_write() method. In fact, even if you start up two threads and have one thread immediately call async_write() and have the other sleep for an hour and then call async_write(), you are still not assured that the OS didn't schedule your threads stupidly and execute the second thread's call first. (The example is pathological but the point is universally valid.)

The same situation applies to async_read(). You certainly can interleave calls (ie do one async_read() and then another before the completion handler is called) but there is no guarantee that the will execute in the order you intend without some external means to ensure this.

0
votes

If you start two asynchronous operations on the same socket, they could occur in either order. As long as they are using different buffers it's "safe", in that it won't crash, but such behavior is almost never what you want.