Is ip :: tcp :: socket.close () safe?

If there is async_read in the ongoing socket, there must be an internal io_service thread that checks the status of the socket. Is it safe to call socket.close() from another thread (possibly when it starts a separate io_service handler)?

I mean, even I can guarantee that my handlers will not use asio socket at the same time, is this enough (taking into account the internal io_service threads)?

Update: I am using async_read on the coroutine stack. This is very similar to the example below this document , except that I have an extra level of calling the function with yield_context . If I send the socket.close() operation to my_strand in this example, are all threads safe? In other words, all related operations ( async_read , intermediate async_read_some , implicit socket.close() coroutine handlers, socket.close() ) are executed through one my_strand chain?

+4
source share
1 answer

In general, it is unsafe to make simultaneous calls to the same socket object 1 . The async_read() operation consists of zero or more intermediate async_read_some() . These intermediate operations are triggered only in threads that currently call io_service::run() . Internal threads are pretty transparent, and not one of the threads listed in the Platform Implementation Notes presents a problem.

Thus:

  • If one thread calls io_service::run() and socket.close() is called from the handler, then it is safe, since there is no possibility of simultaneous execution. The documentation refers to it as an implicit chain.
  • If one thread calls io_service::run() and socket.close() is called from outside the handler, then it is unsafe since socket can have two simultaneous calls: close() from outside io_service and async_read_some() from the thread that is currently calling io_service::run() . To make it thread safe, send a handler to io_service that calls socket.close() .
  • If multiple threads call io_service::run() , explicit strand is required to ensure thread safety. async_read() must be initiated from strand , and its completion handler must also be wrapped in the same chain. In addition, socket.close() must be sent through the chain.

For multi-level coroutines, using spawn() overload that accepts strand will execute the provided function in the context of strand . In addition, when a yield_context is passed as an asynchronous operation handler, handlers, including intermediate handlers from grouped operations, are called in the strand context. Therefore, to ensure thread safety, socket.close() should be:

  • called inside a coroutine:

     // The lambda will execute within the context of my_strand. boost::asio::spawn(my_strand, [socket&](boost::asio::yield_context yield) { // In my_strand. // ... // The socket.async_read_some() operations that composed async_read() // will run within the context of my_strand. async_read(socket, ..., yield); // Still within my_strand. socket.close(); }); 
  • explicitly sent to my_strand :

     // The lambda will execute within the context of my_strand. boost::asio::spawn(my_strand, [socket&](boost::asio::yield_context yield) { // In my_strand. // ... // The socket_.async_read_some() operations that composed async_read() // will run within the context of my_strand. async_read(socket, ..., yield); }); my_strand.dispatch([socket&](){ socket.close(); }); 

For more information on thread safety, compound operations, and chains, see the answer.


<sub> 1. The change history documents the anomaly of this rule. If supported by the OS, synchronous read, write, accept, and connect operations are thread safe. I include it here for completeness, but suggest using it with caution.

+14
source

Source: https://habr.com/ru/post/1200507/


All Articles