diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -165,6 +165,7 @@ InterruptRPC(); InterruptREST(); InterruptTorControl(); + InterruptMapPort(); if (g_connman) { g_connman->Interrupt(); } @@ -195,7 +196,7 @@ pwallet->Flush(false); } #endif - MapPort(false); + StopMapPort(); // Because these depend on each-other, we make sure that neither can be // using the other before destroying them. @@ -2224,7 +2225,9 @@ Discover(); // Map ports with UPnP - MapPort(gArgs.GetBoolArg("-upnp", DEFAULT_UPNP)); + if (gArgs.GetBoolArg("-upnp", DEFAULT_UPNP)) { + StartMapPort(); + } CConnman::Options connOptions; connOptions.nLocalServices = nLocalServices; diff --git a/src/net.h b/src/net.h --- a/src/net.h +++ b/src/net.h @@ -419,7 +419,9 @@ }; extern std::unique_ptr g_connman; void Discover(); -void MapPort(bool fUseUPnP); +void StartMapPort(); +void InterruptMapPort(); +void StopMapPort(); unsigned short GetListenPort(); bool BindListenPort(const CService &bindAddr, std::string &strError, bool fWhitelisted = false); diff --git a/src/net.cpp b/src/net.cpp --- a/src/net.cpp +++ b/src/net.cpp @@ -1536,6 +1536,8 @@ } #ifdef USE_UPNP +static CThreadInterrupt g_upnp_interrupt; +static std::thread g_upnp_thread; void ThreadMapPort() { std::string port = strprintf("%u", GetListenPort()); const char *multicastif = 0; @@ -1584,40 +1586,36 @@ std::string strDesc = "Bitcoin " + FormatFullVersion(); - try { - while (true) { + do { #ifndef UPNPDISCOVER_SUCCESS - /* miniupnpc 1.5 */ - r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, - port.c_str(), port.c_str(), lanaddr, - strDesc.c_str(), "TCP", 0); + /* miniupnpc 1.5 */ + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, + port.c_str(), port.c_str(), lanaddr, + strDesc.c_str(), "TCP", 0); #else - /* miniupnpc 1.6 */ - r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, - port.c_str(), port.c_str(), lanaddr, - strDesc.c_str(), "TCP", 0, "0"); + /* miniupnpc 1.6 */ + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, + port.c_str(), port.c_str(), lanaddr, + strDesc.c_str(), "TCP", 0, "0"); #endif - if (r != UPNPCOMMAND_SUCCESS) { - LogPrintf( - "AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", - port, port, lanaddr, r, strupnperror(r)); - } else { - LogPrintf("UPnP Port Mapping successful.\n"); - } - - // Refresh every 20 minutes - MilliSleep(20 * 60 * 1000); + if (r != UPNPCOMMAND_SUCCESS) { + LogPrintf( + "AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", + port, port, lanaddr, r, strupnperror(r)); + } else { + LogPrintf("UPnP Port Mapping successful.\n"); } - } catch (const boost::thread_interrupted &) { - r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, - port.c_str(), "TCP", 0); - LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r); - freeUPNPDevlist(devlist); - devlist = 0; - FreeUPNPUrls(&urls); - throw; } + + while (g_upnp_interrupt.sleep_for(std::chrono::minutes(20))); + + r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, + port.c_str(), "TCP", 0); + LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r); + freeUPNPDevlist(devlist); + devlist = nullptr; + FreeUPNPUrls(&urls); } else { LogPrintf("No valid UPnP IGDs found\n"); freeUPNPDevlist(devlist); @@ -1628,27 +1626,35 @@ } } -void MapPort(bool fUseUPnP) { - static boost::thread *upnp_thread = nullptr; +void StartMapPort() { + if (!g_upnp_thread.joinable()) { + assert(!g_upnp_interrupt); + g_upnp_thread = std::thread( + (std::bind(&TraceThread, "upnp", &ThreadMapPort))); + } +} - if (fUseUPnP) { - if (upnp_thread) { - upnp_thread->interrupt(); - upnp_thread->join(); - delete upnp_thread; - } - upnp_thread = new boost::thread( - boost::bind(&TraceThread, "upnp", &ThreadMapPort)); - } else if (upnp_thread) { - upnp_thread->interrupt(); - upnp_thread->join(); - delete upnp_thread; - upnp_thread = nullptr; +void InterruptMapPort() { + if (g_upnp_thread.joinable()) { + g_upnp_interrupt(); + } +} + +void StopMapPort() { + if (g_upnp_thread.joinable()) { + g_upnp_thread.join(); + g_upnp_interrupt.reset(); } } #else -void MapPort(bool) { +void StartMapPort() { + // Intentionally left blank. +} +void InterruptMapPort() { + // Intentionally left blank. +} +void StopMapPort() { // Intentionally left blank. } #endif diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -325,7 +325,12 @@ break; case MapPortUPnP: // core option - can be changed on-the-fly settings.setValue("fUseUPnP", value.toBool()); - MapPort(value.toBool()); + if (value.toBool()) { + StartMapPort(); + } else { + InterruptMapPort(); + StopMapPort(); + } break; case MinimizeOnClose: fMinimizeOnClose = value.toBool();