I have an io_context that is run by multiple threads. I'm using sockets and timers. I know that I need to wrap all async_writes by a strand to prevent concurrent writes to the socket.
But can two threads concurrently access the socket to issue an async_read and an async_write at the same time?
Or what about shutdown() or close() being called while another thread calls async_read? Or cancel() on a timer?
Do I need to protect the socket/timer with a mutex or strand in this cases?
This has oft been asked and answered:
Thread Safety
In general, it is safe to make concurrent use of distinct objects, but unsafe to make concurrent use of a single object. However, types such as io_context provide a stronger guarantee that it is safe to use a single object concurrently.
So yes, you need to protect active accesses to you timer/socket objects. However, having asynchronous operations in flight is not a concern: it's about your accesses that require your synchronization.
So,
asio::post(strand, [&sock] { sock.shutdown();});
is always safe, and when you have only 1 thread running the service, then
post(io_context_, [&sock] { sock.shutdown();});
is also fine. Both will have the effect of safely canceling any asynchronous operations still in flight on sock
.
See also the excellent: Why do I need strand per connection when using boost::asio?