The following abstraction model is used:

//SHP_*** обозначены shared_ptr<***> class A { std::vector<SHP_B> vecB; boost::asio::io_service io; } class B { B(...){socket = std::make_shared<Socket>(...);} ~B(){socket->s.close();} private: SHP_Socket socket; } class Socket { Socket(...){s.open(...); s.set_option(...); s.bind(...);} boost::asio::ip::udp::socket s; } 

All these classes and their implementation are in different files. Everything is going perfectly, it works, it is initialized, the sockets are sending data. vecB are added and removed in vecB ... Until I call the class А destructor. In this case, the vecB must contain (and contains, checked) 2 elements SHP_B .

Then a memory access error occurs: Unhandled exception at 0x77BE57DA (ntdll.dll) in Conference.exe: 0xC0000005: Access violation reading location 0xFEEEFEF6. and debager throws to the file: ...\boost\asio\detail\impl\win_iocp_socket_service_base.ipp

 void win_iocp_socket_service_base::destroy( win_iocp_socket_service_base::base_implementation_type& impl) { close_for_destruction(impl); // Remove implementation from linked list of all implementations. boost::asio::detail::mutex::scoped_lock lock(mutex_); if (impl_list_ == &impl) // <- дебагер указывает на эту строчку impl_list_ = impl.next_; if (impl.prev_) impl.prev_->next_ = impl.next_; if (impl.next_) impl.next_->prev_= impl.prev_; impl.next_ = 0; impl.prev_ = 0; } 

I understand that the error is caused by an attempt to delete an already deleted socket or an attempt to delete an open socket (which is unlikely), but why? I call closing the socket only in ~B() , which, after execution, should also call the Socket class destructor, in which the destructor is called for boost::asio::ip::udp::socket s ., Tried to close it manually and remove the elements from vecB one by one before calling the default destructor for class А (after all, before I need to remove an object from A worked fine ...). I tried to transfer the closure of the socket to ~Socket() - it did not help.

What caused this problem and how to solve it?

  • one
    Try to boost::asio::io_service io out of class. Those. so that he lived after the destruction of A - αλεχολυτ
  • @alexolut helped. Thank! But, logically, you need to have an instance of io_service for each object from А and it is not convenient to store them separately. Therefore, thanks to your tip, I added a ~A() cleaning vecB before the automatic destructor A ~A() . - Dmitry

2 answers 2

As it turned out in the comments, the problem is that an object of type boost::asio::io_service destroyed earlier than the last socket created on it.

To avoid this situation, you can create a custom destructor with the correct cleanup order, which you did. And you can simply change the order of members:

 class A { boost::asio::io_service io; std::vector<SHP_B> vecB; }; 

In this case, the automatically created destructor will first delete vecB , and then io . The truth in this case must be borne in mind that used by shared_ptr socket may formally exist somewhere else. If its existence in other places is not supposed - it makes sense to use another type of smart pointer, for example, unique_ptr .

  • Automatic destructor comes from the end of the class property list? If so, then interesting and useful knowledge) - Dmitry
  • one
    @Dmitry data members are created in the order they are declared in the class, and are destroyed in the reverse order. So it can be said that they are deleted from the end, yes. - αλεχολυτ
  • Yes, I agree, it will work this way too. - Pavel Mayorov

The problem is that your io_service destroyed before the socket is destroyed. Extend the life time in two ways:

  1. io_service out. A good place to create an io_service is a local variable in a main function or some similar one. Or even global / static.

  2. Create io_service via shared_ptr , keeping a copy in each class B.

  • In the socket constructor, I will have to pass shp_io_service->get() , unless in this case the counter shared_ptr increases and will it monitor this instance? - Dmitry
  • @Dmitry it will increase if you store shared_ptr in class B. As far as I understand, the main thing is for io_service live at the moment when the socket is called close . - Pavel Mayorov
  • Not sure if you can open the socket again after close , but if you can, io_service still has to live. Those. it is better to make sure that the socket is destroyed, and not just closed. - αλεχολυτ