简体   繁体   中英

Boost Asio, async UDP client - crash on shutdown

I use UDP client-server for IPC in my application

It works fine, but when trying to shutdown the client side some race condition happen, which causes application crash or deadlock.

UDP client:

// IOServiceBase class contain boost::asio::io_service instance
// it is accessible by service() protected method
class AsyncUDPClient : public IOServiceBase
{
public:

    /// @brief Create a network client
    AsyncUDPClient(const std::string& host, const std::string port)
        : _host(host)
        , _port(port)
        , _reply()
        , _work(service())
        , _sock(service(), ba_ip::udp::endpoint(ba_ip::udp::v4(), 0)) {

        run();
    }

    /// @brief Start async packets processing
    void run(){
        std::thread t([&]{ service().run(); });
        t.detach();
    }

    /// @brief Async request to server
    void send(uint8_t* message, size_t length) {
        std::vector<uint8_t> packet(message, message + length);
        service().post(boost::bind(&AsyncUDPClient::do_send, this, packet));
    }

    /// @brief Cleanup io_service to dismiss already putted tasks
    ~AsyncUDPClient() {
        close();

        // trying to wait until service is stopped, but it does not help
        while (!service().stopped()){
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }
    }

    /// @brief Cleanup io_service to dismiss already putted tasks
    void close(){
        std::thread t([&]{ service().stop(); });
        t.join();
    }
protected:

// send-response methods are pretty standard

private:

    std::string _host;
    std::string _port;
    std::array<uint8_t, max_length> _reply;
    ba::io_service::work _work;
    ba_ip::udp::socket _sock;
};

Usage example:

{
    AsyncUDPClient ipc(addr, port);
    ipc.send(&archive_data[0], archive_data.size());
    // it seems client is destroyed before some internal processing is finished?
}

Behaviour is not deterministic, sometimes works fine, sometimes crashes, sometimes freeze. Stacktrace shows the crash point somewhere in boost.asio internals

The destruction of AsyncUDPClient does not properly synchronize with the thread(s) running the io_service . This can result in undefined behavior being invoked when a thread processing the io_service attempts to interact with the AsyncUDPClient and its io_service after their lifetime has ended.

To resolve this, do not detach from the threads processing the io_service , and explicitly join the thread(s) once the io_service has been stopped.

class AsyncUDPClient
 : public IOServiceBase
{
public:

  // ...

  void run()
  {
    _threads.emplace_back([&]{ service().run(); })
  }

  // ...

  ~AsyncUDPClient()
  {
    close();
  }

  void close()
  {
    // Stop the io_service.  This changes its state and return immediately.
    service().stop();

    // Explicitly synchronize with threads running the io_service.
    for (auto& thread: _threads)
    {
      thread.join();
    }
  }

private:

  // ...
  std::vector<std::thread> _threads;
};

As hinted in the comments above, io_service::stop() does not block. It changes the state of io_service to stopped, returns immediately, and causes all invocations of run() and run_one() to return as soon as possible. Calls to io_service::stopped() immediately return the state of the io_service . Neither of these calls indicate if there are threads curretly within the invocation of run() or run_one() .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM