diff --git a/libi2pd/Destination.cpp b/libi2pd/Destination.cpp index 35f96df9..e25a84b6 100644 --- a/libi2pd/Destination.cpp +++ b/libi2pd/Destination.cpp @@ -1155,6 +1155,34 @@ namespace client } void ClientDestination::Stop () + { + // Stop may be called from any thread, while the service thread is still + // inside handlers of this destination, so the teardown goes there. A + // stopped service would never run it, in that case there is nobody left + // to race with and it is done right here + auto& service = GetService (); + if (!service.stopped () && !service.get_executor ().running_in_this_thread ()) + { + // the promise is shared because the wait below is bounded: a service + // stopped right after the check would never run the handler, and a + // stack promise would be gone by then + auto done = std::make_shared >(); + auto future = done->get_future (); + boost::asio::post (service, [this, done]() + { + StopInternal (); + done->set_value (); + }); + if (future.wait_for (std::chrono::seconds (STOP_ON_SERVICE_TIMEOUT)) == std::future_status::ready) + return; + LogPrint (eLogError, "Destination: Service didn't stop the destination in ", + STOP_ON_SERVICE_TIMEOUT, " seconds"); + return; + } + StopInternal (); + } + + void ClientDestination::StopInternal () { LogPrint(eLogDebug, "Destination: Stopping destination ", GetIdentHash().ToBase32(), ".b32.i2p"); m_ReadyChecker.cancel(); @@ -1692,20 +1720,7 @@ namespace client { if (IsRunning ()) { - // the destination's own thread may still be handling packets of this - // very destination, so tear it down there rather than under the caller - if (GetIOService ().get_executor ().running_in_this_thread ()) - ClientDestination::Stop (); - else - { - std::promise done; - boost::asio::post (GetIOService (), [this, &done]() - { - ClientDestination::Stop (); - done.set_value (); - }); - done.get_future ().wait (); - } + ClientDestination::Stop (); // takes care of the thread it runs on StopIOService (); } } diff --git a/libi2pd/Destination.h b/libi2pd/Destination.h index 986254b5..ed486001 100644 --- a/libi2pd/Destination.h +++ b/libi2pd/Destination.h @@ -45,6 +45,7 @@ namespace client const int PUBLISH_VERIFICATION_TIMEOUT_VARIANCE = 3; // in seconds const int PUBLISH_MIN_INTERVAL = 20; // in seconds const int PUBLISH_REGULAR_VERIFICATION_INTERNAL = 100; // in seconds periodically + const int STOP_ON_SERVICE_TIMEOUT = 10; // in seconds, how long Stop waits for the destination's thread const int LEASESET_REQUEST_TIMEOUT = 1200; // in milliseconds const int MAX_LEASESET_REQUEST_TIMEOUT = 17000; // in milliseconds const int DESTINATION_CLEANUP_TIMEOUT = 44; // in seconds @@ -260,6 +261,7 @@ namespace client void Start () override; void Stop () override; + void StopInternal (); // the teardown itself, always on the destination's thread const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; }; void SetPrivateKeys (const i2p::data::PrivateKeys& keys);