@@ -41,7 +41,8 @@ static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed"
4141#if defined(USE_NATPMP) || defined(USE_UPNP)
4242static CThreadInterrupt g_mapport_interrupt;
4343static std::thread g_mapport_thread;
44- static std::atomic_uint g_mapport_target_proto{MapPortProtoFlag::NONE};
44+ static std::atomic_uint g_mapport_enabled_protos{MapPortProtoFlag::NONE};
45+ static std::atomic<MapPortProtoFlag> g_mapport_current_proto{MapPortProtoFlag::NONE};
4546
4647static constexpr auto PORT_MAPPING_REANNOUNCE_PERIOD = std::chrono::minutes(20 );
4748static constexpr auto PORT_MAPPING_RETRY_PERIOD = std::chrono::minutes(5 );
@@ -220,9 +221,34 @@ static bool ProcessUpnp()
220221
221222static void ThreadMapPort ()
222223{
224+ bool ok;
223225 do {
224- if (ProcessUpnp ()) return ;
225- } while (g_mapport_interrupt.sleep_for (PORT_MAPPING_RETRY_PERIOD));
226+ ok = false ;
227+
228+ #ifdef USE_UPNP
229+ // High priority protocol.
230+ if (g_mapport_enabled_protos & MapPortProtoFlag::UPNP) {
231+ g_mapport_current_proto = MapPortProtoFlag::UPNP;
232+ ok = ProcessUpnp ();
233+ if (ok) continue ;
234+ }
235+ #endif // USE_UPNP
236+
237+ #ifdef USE_NATPMP
238+ // Low priority protocol.
239+ if (g_mapport_enabled_protos & MapPortProtoFlag::NAT_PMP) {
240+ g_mapport_current_proto = MapPortProtoFlag::NAT_PMP;
241+ ok = ProcessNatpmp ();
242+ if (ok) continue ;
243+ }
244+ #endif // USE_NATPMP
245+
246+ g_mapport_current_proto = MapPortProtoFlag::NONE;
247+ if (g_mapport_enabled_protos == MapPortProtoFlag::NONE) {
248+ return ;
249+ }
250+
251+ } while (ok || g_mapport_interrupt.sleep_for (PORT_MAPPING_RETRY_PERIOD));
226252}
227253
228254void StartThreadMapPort ()
@@ -235,20 +261,39 @@ void StartThreadMapPort()
235261
236262static void DispatchMapPort ()
237263{
238- if (g_mapport_target_proto == MapPortProtoFlag::UPNP) {
264+ if (g_mapport_current_proto == MapPortProtoFlag::NONE && g_mapport_enabled_protos == MapPortProtoFlag::NONE) {
265+ return ;
266+ }
267+
268+ if (g_mapport_current_proto == MapPortProtoFlag::NONE && g_mapport_enabled_protos != MapPortProtoFlag::NONE) {
239269 StartThreadMapPort ();
240- } else {
270+ return ;
271+ }
272+
273+ if (g_mapport_current_proto != MapPortProtoFlag::NONE && g_mapport_enabled_protos == MapPortProtoFlag::NONE) {
241274 InterruptMapPort ();
242275 StopMapPort ();
276+ return ;
277+ }
278+
279+ if (g_mapport_enabled_protos & g_mapport_current_proto) {
280+ // Enabling another protocol does not cause switching from the currently used one.
281+ return ;
243282 }
283+
284+ assert (g_mapport_thread.joinable ());
285+ assert (!g_mapport_interrupt);
286+ // Interrupt a protocol-specific loop in the ThreadUpnp() or in the ThreadNatpmp()
287+ // to force trying the next protocol in the ThreadMapPort() loop.
288+ g_mapport_interrupt ();
244289}
245290
246291static void MapPortProtoSetEnabled (MapPortProtoFlag proto, bool enabled)
247292{
248293 if (enabled) {
249- g_mapport_target_proto |= proto;
294+ g_mapport_enabled_protos |= proto;
250295 } else {
251- g_mapport_target_proto &= ~proto;
296+ g_mapport_enabled_protos &= ~proto;
252297 }
253298}
254299
@@ -260,6 +305,7 @@ void StartMapPort(bool use_upnp)
260305
261306void InterruptMapPort ()
262307{
308+ g_mapport_enabled_protos = MapPortProtoFlag::NONE;
263309 if (g_mapport_thread.joinable ()) {
264310 g_mapport_interrupt ();
265311 }
0 commit comments