diff --git a/src/services/autoproxy/p3i2psam3.cpp b/src/services/autoproxy/p3i2psam3.cpp index ca7a44575..889a5779d 100644 --- a/src/services/autoproxy/p3i2psam3.cpp +++ b/src/services/autoproxy/p3i2psam3.cpp @@ -29,6 +29,14 @@ static const std::string kConfigKeyBOBAddr = "BOB_ADDR"; static constexpr bool kDefaultSAM3Enable = false; +/* At least i2pd doesn't like to instantaniously stop and (re)start a session, so + * a new session is never created less than this long after the previous one was + * closed. The wait happens in startSession(), where it is actually needed, and + * not in stopSession(): a stop is not always followed by a start (RS shutdown, + * "stop" button in the settings) and waiting there held back the whole + * application exit by 10 seconds. */ +static constexpr auto kSessionRestartDelay = std::chrono::seconds(10); + RS_SET_CONTEXT_DEBUG_LEVEL(2) static void inline doSleep(std::chrono::duration> timeToSleepMS) { @@ -36,7 +44,9 @@ static void inline doSleep(std::chrono::duration> timeT } p3I2pSam3::p3I2pSam3(p3PeerMgr *peerMgr) : - mConfigLoaded(false), mPeerMgr(peerMgr), mPending(), mLock("p3i2p-sam3") + mConfigLoaded(false), mPeerMgr(peerMgr), mPending(), + mLastSessionClosed(std::chrono::steady_clock::time_point::min()), + mLock("p3i2p-sam3") #ifdef RS_USE_I2P_SAM3_LIBSAM3 , mLockSam3Access("p3i2p-sam3-access") #endif @@ -498,6 +508,19 @@ bool p3I2pSam3::startSession() stopSession(); } + // Give i2pd time to let go of the previous session. No lock may be held here. + std::chrono::steady_clock::time_point sessionReady; + { + RS_STACK_MUTEX(mLock); + sessionReady = mLastSessionClosed + kSessionRestartDelay; + } + const auto now = std::chrono::steady_clock::now(); + if (now < sessionReady) { + const auto wait = std::chrono::duration_cast(sessionReady - now); + RS_DBG("waiting ", wait.count(), " ms before creating a new SAM session"); + doSleep(wait); + } + auto session = (Sam3Session*)rs_malloc(sizeof (Sam3Session)); // add nick @@ -592,12 +615,10 @@ void p3I2pSam3::stopSession() mSetting.session = nullptr; mState = samStatus::samState::offline; - } - // At least i2pd doesn't like to instantaniously stop and (re)start a session, wait here just a little bit. - // Not ideal but does the trick. - // (This happens when using the "restart" button in the settings.) - doSleep(std::chrono::seconds(10)); + // startSession() waits out kSessionRestartDelay counted from here + mLastSessionClosed = std::chrono::steady_clock::now(); + } } void p3I2pSam3::stopForwarding() diff --git a/src/services/autoproxy/p3i2psam3.h b/src/services/autoproxy/p3i2psam3.h index c6b57615e..1ddc8c3cf 100644 --- a/src/services/autoproxy/p3i2psam3.h +++ b/src/services/autoproxy/p3i2psam3.h @@ -1,6 +1,7 @@ #ifndef P3I2PSAM3_H #define P3I2PSAM3_H +#include #include #include @@ -102,6 +103,10 @@ private: // used to keep track of connections, libsam3 does it internally but it can be unreliable since pointers are shared std::list mValidConnections, mInvalidConnections; + // Point in time at which the last SAM session was closed. startSession() uses + // it to honour kSessionRestartDelay. Guarded by mLock. + std::chrono::steady_clock::time_point mLastSessionClosed; + // mutex RsMutex mLock; RsMutex mLockSam3Access; // libsam3 is not thread safe! (except for key lookup)