i2p(sam3): move the session restart delay to startSession()

stopSession() ended with an unconditional 10 s sleep, meant to give i2pd
time to let go of a session before the next one is created ("restart"
button in the settings, which posts a stop ticket immediately followed by
a start ticket).

A stop is not always followed by a start. On application exit
p3face-config's shutdown sequence calls rsAutoProxyMonitor::stopAllRSShutdown(),
which posts that same stop ticket and then waits for the service to
acknowledge it; the sleep therefore delayed the whole shutdown by 10 s
for nothing. Measured on a node with SAM3 enabled: 10.0 s of a 21.8 s
shutdown were spent in this sleep, the monitor logging
"waiting for auto proxy service(s) to shut down 0..10/15 (remaining: 1)".
The "stop" button in the settings pays it too, equally uselessly.

Record when the session was closed instead, and wait out what is left of
the delay in startSession(), where it is actually needed. The guarantee
i2pd relies on is unchanged -- a session is still never created less than
10 s after the previous one was closed -- but a stop that is not followed
by a start now returns immediately.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
jolavillette 2026-08-22 14:18:26 +02:00
parent 339c765235
commit 285328d92f
2 changed files with 32 additions and 6 deletions

View File

@ -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<long, std::ratio<1,1000>> timeToSleepMS) {
@ -36,7 +44,9 @@ static void inline doSleep(std::chrono::duration<long, std::ratio<1,1000>> 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<std::chrono::milliseconds>(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()

View File

@ -1,6 +1,7 @@
#ifndef P3I2PSAM3_H
#define P3I2PSAM3_H
#include <chrono>
#include <queue>
#include <list>
@ -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<Sam3Connection *> 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)