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_writeon the same socket without waiting completion of the first one? Will the handlers (and theasync_write) overlap orasioprovides serialization and synchronization? -
Same question of above with
async_connectandasync_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…).
I assume from your question that you have a single instance of
io_serviceand you want to callasync_write()on it from multiple threads.async_write()ultimately calls thepost()method ofio_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 callasync_write()and have the other sleep for an hour and then callasync_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 oneasync_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.