diff -U2 -r /var/lib/copr-rpmbuild/results/i2pd-git/upstream-unpacked/Source0/i2pd-openssl/libi2pd/Destination.cpp /var/lib/copr-rpmbuild/results/i2pd-git/srpm-unpacked/i2pd-openssl.tar.gz-extract/i2pd-openssl/libi2pd/Destination.cpp --- /var/lib/copr-rpmbuild/results/i2pd-git/upstream-unpacked/Source0/i2pd-openssl/libi2pd/Destination.cpp 2026-08-24 20:55:17.000000000 +0000 +++ /var/lib/copr-rpmbuild/results/i2pd-git/srpm-unpacked/i2pd-openssl.tar.gz-extract/i2pd-openssl/libi2pd/Destination.cpp 2026-08-24 20:31:21.000000000 +0000 @@ -1157,32 +1157,4 @@ 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(); @@ -1721,5 +1693,18 @@ if (IsRunning ()) { - ClientDestination::Stop (); // takes care of the thread it runs on + // 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 (); + } StopIOService (); } diff -U2 -r /var/lib/copr-rpmbuild/results/i2pd-git/upstream-unpacked/Source0/i2pd-openssl/libi2pd/Destination.h /var/lib/copr-rpmbuild/results/i2pd-git/srpm-unpacked/i2pd-openssl.tar.gz-extract/i2pd-openssl/libi2pd/Destination.h --- /var/lib/copr-rpmbuild/results/i2pd-git/upstream-unpacked/Source0/i2pd-openssl/libi2pd/Destination.h 2026-08-24 20:55:17.000000000 +0000 +++ /var/lib/copr-rpmbuild/results/i2pd-git/srpm-unpacked/i2pd-openssl.tar.gz-extract/i2pd-openssl/libi2pd/Destination.h 2026-08-24 20:31:21.000000000 +0000 @@ -47,5 +47,4 @@ 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 @@ -267,5 +266,4 @@ 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; };