upnp: add IGD discovery phase.

When the protocols start searching for IGD, the IGD discovery phase begins.
During this phase, all requested mappings will be marked as pending.
If an IGD is found, the pending mappings will be requested from the router.
If the phase ends due to a timeout, the pending mappings will be marked as failed

GitLab: #36
Change-Id: Icb19b05e40968d619cb032a949a8fe8acfef311b
diff --git a/include/upnp/upnp_context.h b/include/upnp/upnp_context.h
index fca38a5..d161053 100644
--- a/include/upnp/upnp_context.h
+++ b/include/upnp/upnp_context.h
@@ -75,6 +75,7 @@
     virtual void onMappingRequestFailed(const Mapping& map) = 0;
     virtual void onMappingRenewed(const std::shared_ptr<IGD>& igd, const Mapping& map) = 0;
     virtual void onMappingRemoved(const std::shared_ptr<IGD>& igd, const Mapping& map) = 0;
+    virtual void onIgdDiscoveryStarted() = 0;
 };
 
 class UPnPContext : public UpnpMappingObserver
@@ -133,6 +134,11 @@
         });
     }
 
+    // Set the timeout for the IGD discovery process.
+    // If the timeout expires and no valid IGD has been discovered,
+    // then the state of all pending mappings is set to FAILED.
+    void setIgdDiscoveryTimeout(std::chrono::milliseconds timeout);
+
 private:
     // Initialization
     void init();
@@ -242,6 +248,9 @@
     // Callback used to report remove request status.
     void onMappingRemoved(const std::shared_ptr<IGD>& igd, const Mapping& map) override;
 
+    // Callback used to report the start of the discovery process: search for IGDs.
+    void onIgdDiscoveryStarted() override;
+
 private:
     UPnPContext(const UPnPContext&) = delete;
     UPnPContext(UPnPContext&&) = delete;
@@ -304,6 +313,16 @@
     // Shutdown synchronization
     bool shutdownComplete_ {false};
     bool shutdownTimedOut_ {false};
+
+    // IGD Discovery synchronization. This boolean indicates if the IGD discovery is in progress.
+    bool igdDiscoveryInProgress_ {true};
+    std::mutex igdDiscoveryMutex_;
+    std::chrono::milliseconds igdDiscoveryTimeout_ {std::chrono::milliseconds(500)};
+
+    // End of the discovery process.
+    void _endIgdDiscovery();
+
+    asio::steady_timer igdDiscoveryTimer_;
 };
 
 } // namespace upnp