How to remove a child from the parent slot? Amplification possible :: asio specific

I wrote a network server class that supports std :: set network clients. Network clients issue a signal to the network server when disconnected (via boost :: bind). When the network client disconnects, the client instance must be removed from the set and eventually deleted. I would think that this is a common template, but I am having problems that may or may not be specific to ASIO.

I tried to trim only the corresponding code:

/** NetworkServer.hpp **/ class NetworkServices : private boost::noncopyable { public: NetworkServices(void); ~NetworkServices(void); private: void run(); void onNetworkClientEvent(NetworkClientEvent&); private: std::set<boost::shared_ptr<const NetworkClient>> clients; }; /** NetworkClient.cpp **/ void NetworkServices::run() { running = true; boost::asio::io_service::work work(io_service); //keeps service running even if no operations // This creates just one thread for the boost::asio async network services boost::thread iot(boost::bind(&NetworkServices::run_io_service, this)); while (running) { boost::system::error_code err; try { tcp::socket* socket = new tcp::socket(io_service); acceptor->accept(*socket, err); if (!err) { NetworkClient* networkClient = new NetworkClient(io_service, boost::shared_ptr<tcp::socket>(socket)); networkClient->networkClientEventSignal.connect(boost::bind(&NetworkServices::onNetworkClientEvent, this, _1)); clients.insert(boost::shared_ptr<NetworkClient>(networkClient)); networkClient->init(); //kicks off 1st asynch_read call } } // etc... } } void NetworkServices::onNetworkClientEvent(NetworkClientEvent& evt) { switch(evt.getType()) { case NetworkClientEvent::CLIENT_ERROR : { boost::shared_ptr<const NetworkClient> clientPtr = evt.getClient().getSharedPtr(); // ------ THIS IS THE MAGIC LINE ----- // If I keep this, the io_service hangs. If I comment it out, // everything works fine (but I never delete the disconnected NetworkClient). // If actually deleted the client here I might expect problems because it is the caller // of this method via boost::signal and bind. However, The clientPtr is a shared ptr, and a // reference is being kept in the client itself while signaling, so // I would the object is not going to be deleted from the heap here. That seems to be the case. // Never-the-less, this line makes all the difference, most likely because it controls whether or not the NetworkClient ever gets deleted. clients.erase(clientPtr); //I should probably put this socket clean-up in NetworkClient destructor. Regardless by doing this, // I would expect the ASIO socket stuff to be adequately cleaned-up after this. tcp::socket& socket = clientPtr->getSocket(); try { socket.shutdown(boost::asio::socket_base::shutdown_both); socket.close(); } catch(...) { CommServerContext::error("Error while shutting down and closing socket."); } break; } default : { break; } } } /** NetworkClient.hpp **/ class NetworkClient : public boost::enable_shared_from_this<NetworkClient>, Client { NetworkClient(boost::asio::io_service& io_service, boost::shared_ptr<tcp::socket> socket); virtual ~NetworkClient(void); inline boost::shared_ptr<const NetworkClient> getSharedPtr() const { return shared_from_this(); }; boost::signal <void (NetworkClientEvent&)> networkClientEventSignal; void onAsyncReadHeader(const boost::system::error_code& error, size_t bytes_transferred); }; /** NetworkClient.cpp - onAsyncReadHeader method called from io_service.run() thread as result of an async_read operation. Error condition usually result of an unexpected client disconnect.**/ void NetworkClient::onAsyncReadHeader( const boost::system::error_code& error, size_t bytes_transferred) { if (error) { //Make sure this instance doesn't get deleted from parent/slot deferencing //Alternatively, somehow schedule for future delete? boost::shared_ptr<const NetworkClient> clientPtr = getSharedPtr(); //Signal to service that this client is disconnecting NetworkClientEvent evt(*this, NetworkClientEvent::CLIENT_ERROR); networkClientEventSignal(evt); networkClientEventSignal.disconnect_all_slots(); return; } 

I believe that it will not be possible to remove the client from the slot handler, because the function return will be ... undefined? (Interesting, but it doesn’t look like me.) So I used boost: shared_ptr along with shared_from_this to make sure that the client is not deleted until all the slots are signaled. It does not seem to matter much.

I believe this question is not specific to ASIO, but the problem manifests itself in a special way when using ASIO. I have one thread executing io_service.run (). All ASIO read / write operations are asynchronous. Everything works fine when several clients connect / disconnect IF I do not remove my client object from the set for the above code. If I delete my client object, io_service seems to block internally, and further asynchronous operations are not performed unless I start another thread. I am trying / catching a call to io_service.run () and could not detect any errors.

Questions:

  • Are there better methods for removing child objects, which are also signal emitters, from parent slots?

  • Any ideas as to why io_service hangs when I delete my network client object?

+4
source share
2 answers

I finally figured it out, and the short answer is that it was primarily a coding / streaming error on my part. I determined this by creating a simpler example of stand-alone code and found that it does not exhibit the same behavior. I had to do this in the first place, and I'm sorry that I took the time to spend the time. To answer my original questions in full, though:

1 - Are there better methods for removing child objects, which are also signal emitters, from parent slots?

No one answered. I think my suggestion and sample code above, where I use shared_from_this, works fine.

Also, as pointed out by Villintehaspam in the comments above, it would be better to use boost :: signal2, which seems to have better support for controlling the signal's lifetime.

2 - Any ideas as to why io_service hangs when I delete my network client object

My mistake was that the destructor in my NetworkClient ran an operation that called the current thread (and only the thread available to handle asych IO operations) to block indefinitely. I did not understand that this was happening. New clients could still connect due to the way I handle the acceptor in its own thread, regardless of the asynch io_service operations. Of course, although I planned asynchronous operations for a new client, they never started because the one thread that I made available to io_service was still blocked.

Thanks to everyone who took the time to look at this.

0
source

you can store weak_ptr in the set, so shared_ptr will only be stored in asio and will be automatically disabled. remove the corresponding weak_ptr from the set in ~ Client ()

+1
source

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


All Articles