diff --git a/src/i2p.cpp b/src/i2p.cpp index 0420bc92382..6dd723083ae 100644 --- a/src/i2p.cpp +++ b/src/i2p.cpp @@ -131,6 +131,13 @@ Session::Session(const Proxy& control_host, CThreadInterrupt* interrupt) m_interrupt{interrupt}, m_transient{true} { + try { + LOCK(m_mutex); + CreateIfNotCreatedAlready(); + } catch (const std::runtime_error&) { + // This was just an eager optimistic attempt to create the session. + // If it fails, then it will be reattempted again when `Connect()` is called. + } } Session::~Session() diff --git a/src/net.cpp b/src/net.cpp index 735985a8414..369d498410b 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -455,6 +455,8 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo connect_to.push_back(addrConnect); } + AddI2PSessionsIfNeeded(); + // Connect std::unique_ptr sock; Proxy proxy; @@ -474,16 +476,18 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo if (m_i2p_sam_session) { connected = m_i2p_sam_session->Connect(target_addr, conn, proxyConnectionFailed); } else { + // Use an existent transient session, if one was created earlier... { LOCK(m_unused_i2p_sessions_mutex); - if (m_unused_i2p_sessions.empty()) { - i2p_transient_session = - std::make_unique(proxy, &interruptNet); - } else { + if (!m_unused_i2p_sessions.empty()) { i2p_transient_session.swap(m_unused_i2p_sessions.front()); m_unused_i2p_sessions.pop(); } } + // ... or create a new one if m_unused_i2p_sessions was empty. + if (!i2p_transient_session) { + i2p_transient_session = std::make_unique(proxy, &interruptNet); + } connected = i2p_transient_session->Connect(target_addr, conn, proxyConnectionFailed); if (!connected) { LOCK(m_unused_i2p_sessions_mutex); @@ -3237,6 +3241,28 @@ uint16_t CConnman::GetDefaultPort(const std::string& addr) const return a.SetSpecial(addr) ? GetDefaultPort(a.GetNetwork()) : m_params.GetDefaultPort(); } +void CConnman::AddI2PSessionsIfNeeded() +{ + AssertLockNotHeld(m_unused_i2p_sessions_mutex); + + Proxy i2p_proxy; + + if (!g_reachable_nets.Contains(NET_I2P) || // Not using I2P at all. + m_i2p_sam_session || // Using a single persistent session, m_unused_i2p_sessions is not used. + WITH_LOCK(m_unused_i2p_sessions_mutex, return m_unused_i2p_sessions.size() >= MAX_UNUSED_I2P_SESSIONS_SIZE) || // Already have enough sessions. + !GetProxy(NET_I2P, i2p_proxy)) { + return; + } + + // It is preferable to slightly exceed MAX_UNUSED_I2P_SESSIONS_SIZE compared to holding + // m_unused_i2p_sessions_mutex while creating the session which could take a few seconds. + + auto new_session = std::make_unique(i2p_proxy, &interruptNet); + + LOCK(m_unused_i2p_sessions_mutex); + m_unused_i2p_sessions.emplace(std::move(new_session)); +} + bool CConnman::Bind(const CService& addr_, unsigned int flags, NetPermissionFlags permissions) { const CService addr{MaybeFlipIPv6toCJDNS(addr_)}; diff --git a/src/net.h b/src/net.h index e64d9a67f46..52e7ef57c49 100644 --- a/src/net.h +++ b/src/net.h @@ -1408,6 +1408,11 @@ private: uint16_t GetDefaultPort(Network net) const; uint16_t GetDefaultPort(const std::string& addr) const; + /** + * Add entries to `m_unused_i2p_sessions` if needed. + */ + void AddI2PSessionsIfNeeded() EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex); + // Network usage totals mutable Mutex m_total_bytes_sent_mutex; std::atomic nTotalBytesRecv{0};