diff --git a/map/traffic_manager.hpp b/map/traffic_manager.hpp index e8bcb9956..2f7690dde 100644 --- a/map/traffic_manager.hpp +++ b/map/traffic_manager.hpp @@ -35,15 +35,26 @@ class TrafficManager final public: using CountryParentNameGetterFn = std::function; + /** + * @brief Global state of traffic information. + */ enum class TrafficState { + /** Traffic is disabled, no traffic data will be retrieved or considered for routing. */ Disabled, + /** Traffic is enabled and working normally (the first request may not have been scheduled yet). */ Enabled, + /** At least one request is currently pending. */ WaitingData, + /** At least one MWM has stale traffic data. */ Outdated, + /** Traffic data for at least one MWM was invalid or not found on the server. */ NoData, + /** At least one request failed or timed out. */ NetworkError, + /** Traffic data could not be retrieved because the map data is outdated. */ ExpiredData, + /** Traffic data could not be retrieved because the app version is outdated. */ ExpiredApp }; @@ -112,46 +123,172 @@ public: bool HasSimplifiedColorScheme() const { return m_hasSimplifiedColorScheme; } private: + /** + * @brief Holds information about pending or previous traffic requests pertaining to an MWM. + */ struct CacheEntry { CacheEntry(); explicit CacheEntry(std::chrono::time_point const & requestTime); + /** + * @brief Whether we have traffic data for this MWM. + */ bool m_isLoaded; + + /** + * @brief The amount of memory occupied by the coloring for this MWM. + */ size_t m_dataSize; + /** + * @brief When the last update request occurred, not including forced updates. + * + * This timestamp is the basis for eliminating the oldest entries from the cache. + */ std::chrono::time_point m_lastActiveTime; + + /** + * @brief When the last update request occurred, including forced updates. + * + * This timestamp is the basis for determining whether an update is needed. + */ std::chrono::time_point m_lastRequestTime; + + /** + * @brief When the last response was received. + * + * This timestamp is the basis for determining whether a network request timed out, or if data is outdated. + */ std::chrono::time_point m_lastResponseTime; + /** + * @brief The number of failed traffic requests for this MWM. + * + * Reset when the MWM becomes inactive. + */ int m_retriesCount; + + /** + * @brief Whether a request is currently pending for this MWM. + * + * Set to `true` when a request is scheduled, reverted to `false` when a response is received or the request fails. + */ bool m_isWaitingForResponse; traffic::TrafficInfo::Availability m_lastAvailability; }; + /** + * @brief Event loop for the traffic worker thread. + * + * This method runs an event loop, which blocks until woken up or a timeout equivalent to the + * update interval elapses. It cycles through the list of MWMs for which updates have been + * scheduled, triggering a network request for each and processing the result. + */ void ThreadRoutine(); + + /** + * @brief Blocks until a request for traffic data is received or a timeout expires. + * + * This method acts as the loop condition for `ThreadRoutine()`. It blocks until woken up or the + * update interval expires. In the latter case, it calls `RequestTrafficData()` to insert all + * currently active MWMs into the list of MWMs to update; otherwise, it leaves the list as it is. + * In either case, it populates `mwms` with the list and returns. + * + * @param mwms Receives a list of MWMs for which to update traffic data. + * @return `true` during normal operation, `false` during teardown (signaling the event loop to exit). + */ bool WaitForRequest(std::vector & mwms); void OnTrafficDataResponse(traffic::TrafficInfo && info); + /** + * @brief Processes a failed traffic request. + * + * This method gets called when a traffic request has failed. + * + * It updates the `m_isWaitingForResponse` and `m_lastAvailability` of `info. + * + * If the MWM is no longer active, this method returns immediately after that. + * + * If the retry limit has not been reached, the MWM is re-inserted into the list by calling + * `RequestTrafficData(MwmSet::MwmId, bool)` with `force` set to true. Otherwise, the retry count + * is reset and the state updated accordingly. + * + * @param info + */ void OnTrafficRequestFailed(traffic::TrafficInfo && info); - /// \brief Updates |activeMwms| and request traffic data. - /// \param rect is a rectangle covering a new active mwm set. - /// \note |lastMwmsByRect|/|activeMwms| may be either |m_lastDrapeMwmsByRect/|m_activeDrapeMwms| - /// or |m_lastRoutingMwmsByRect|/|m_activeRoutingMwms|. - /// \note |m_mutex| is locked inside the method. So the method should be called without |m_mutex|. + /** + * @brief Updates `activeMwms` and requests traffic data. + * + * The old and new list of active MWMs may refer either to those used by the rendering engine + * (`m_lastDrapeMwmsByRect`/`m_activeDrapeMwms`) or to those used by the routing engine + * (`m_lastRoutingMwmsByRect`/`m_activeRoutingMwms`). + * + * The method first determines the list of MWMs overlapping with `rect`. If it is identical to + * `lastMwmsByRect`, the method returns immediately. Otherwise, it stores the new set in + * `lastMwmsByRect` and populates `activeMwms` with the elements. + * + * This method locks `m_mutex` while populating `activeMwms`. There is no need for the caller to + * do that. + * + * @param rect Rectangle covering the new active MWM set. + * @param lastMwmsByRect Set of active MWMs, see description. + * @param activeMwms Vector of active MWMs, see description. + */ void UpdateActiveMwms(m2::RectD const & rect, std::vector & lastMwmsByRect, std::set & activeMwms); // This is a group of methods that haven't their own synchronization inside. + + /** + * @brief Requests a refresh of traffic data for all currently active MWMs. + * + * This method is the entry point for periodic traffic data refresh operations. It cycles through + * all active MWMs and calls `RequestTrafficData(MwmSet::MwmId, bool)` on each `MwmId`, + * scheduling a refresh if needed. The actual network operation is performed asynchronously on a + * separate thread. + * + * The method does nothing if the `TrafficManager` instance is disabled, paused, in an invalid + * state (`NetworkError`) or if neither the rendering engine nor the routing engine have any + * active MWMs. + * + * This method is unsynchronized; the caller must lock `m_mutex` prior to calling it. + */ void RequestTrafficData(); + + /** + * @brief Requests a refresh of traffic data for a single MWM. + * + * This method first checks if traffic data for the given MWM needs to be refreshed, which is the + * case if no traffic data has ever been fetched for the given MWM, the update interval has + * expired or `force` is true. In that case, the method inserts the `mwmId` into the list of MWMs + * for which to update traffic and wakes up the worker thread. + * + * This method is unsynchronized; the caller must lock `m_mutex` prior to calling it. + * + * @param mwmId Describes the MWM for which traffic data is to be refreshed. + * @param force If true, a refresh is requested even if the update interval has not expired. + */ void RequestTrafficData(MwmSet::MwmId const & mwmId, bool force); void Clear(); void ClearCache(MwmSet::MwmId const & mwmId); void ShrinkCacheToAllowableSize(); + /** + * @brief Updates the state of the traffic manager based on the state of all MWMs used by the renderer. + * + * This method cycles through the state of all MWMs used by the renderer (MWMs used by the + * routing engine but not by the rendering engine are not considered), examines their traffic + * state and sets the global state accordingly. + * + * For a description of states, see `TrafficState`. The order of states is as follows, the first + * state whose conditions are fulfilled becomes the new state: `TrafficState::NetworkError`, + * `TrafficState::WaitingData`, `TrafficState::ExpiredApp`, `TrafficState::ExpiredData`, + * `TrafficState::NoData`, `TrafficState::Outdated`, `TrafficState::Enabled`. + */ void UpdateState(); void ChangeState(TrafficState newState); @@ -194,6 +331,31 @@ private: bool m_isRunning; std::condition_variable m_condition; + /* + * To determine for which MWMs we need traffic data, we need to keep track of two groups of MWMs: + * those used by the renderer (i.e. in or just around the viewport) and those used by the routing + * engine (i.e. those within a certain area around the route endpoints). + * + * Each group is stored twice: as a set and as a vector. The set always holds the MWMs which were + * last seen in use. Both get updated together when active MWMs are added or removed. However, + * the vector is used as a reference to detect changes. It may get cleared when the set is not, + * which is used to invalidate the set without destroying its contents. + * + * Methods which use only the set: + * + * * RequestTrafficData(), exits if empty, otherwise cycles through the set. + * * OnTrafficRequestFailed(), determines if an MWM is still active and the request should be retried. + * * UniteActiveMwms(), build the list of active MWMs (used by RequestTrafficData() or to shrink the cache). + * * UpdateState(), cycles through the set to determine the state of traffic requests (renderer only). + * + * Methods which use both, but in a different way: + * + * * ClearCache(), removes the requested MWM from the set but clears the vector completely. + * * Invalidate(), clears the vector but not the set. + * * UpdateActiveMwms(), uses the vector to detect changes. If so, it updates both vector and set. + * + * Clear() clears both the set and the vector. + */ std::vector m_lastDrapeMwmsByRect; std::set m_activeDrapeMwms; std::vector m_lastRoutingMwmsByRect; @@ -206,8 +368,15 @@ private: std::atomic m_isPaused; + /** + * @brief MWMs for which to retrieve traffic data. + */ std::vector m_requestedMwms; std::mutex m_mutex; + + /** + * @brief Worker thread which fetches traffic updates. + */ threads::SimpleThread m_thread; }; diff --git a/traffic/traffic_info.hpp b/traffic/traffic_info.hpp index 60e789986..a0a6ed147 100644 --- a/traffic/traffic_info.hpp +++ b/traffic/traffic_info.hpp @@ -25,12 +25,26 @@ public: static uint8_t const kLatestKeysVersion; static uint8_t const kLatestValuesVersion; + /** + * @brief Whether traffic data is available in this `TrafficInfo` instance. + */ + /* + * TODO A global traffic update would require some 2–3 states: + * * IsAvailable + * * Data available but not yet decoded + * * (possibly) No traffic reports for this MWM + */ enum class Availability { + /** This `TrafficInfo` instance has data available. */ IsAvailable, + /** No traffic data is available (file not found on the server, or server returned invalid data). */ NoData, + /** Traffic data could not be retrieved because the map data is outdated. */ ExpiredData, + /** Traffic data could not be retrieved because the app version is outdated. */ ExpiredApp, + /** No traffic data is available because the server responded with an error (other than “not found”), or no request was made yet. */ Unknown }; @@ -166,11 +180,18 @@ public: static void DeserializeTrafficValues(std::vector const & data, std::vector & result); private: + /** + * @brief Result of the last request to the server. + */ enum class ServerDataStatus { + /** New data was returned. */ New, + /** Data has not changed since the last request. */ NotChanged, + /** The URL was not found on the server. */ NotFound, + /** An error prevented data from being requested, or the server responded with an error. */ Error, };