LCOV - code coverage report
Current view: top level - lib/dnssd - ActiveResolveAttempts.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 80 115 69.6 %
Date: 2024-02-15 08:20:41 Functions: 10 15 66.7 %

          Line data    Source code
       1             : /*
       2             :  *
       3             :  *    Copyright (c) 2021 Project CHIP Authors
       4             :  *
       5             :  *    Licensed under the Apache License, Version 2.0 (the "License");
       6             :  *    you may not use this file except in compliance with the License.
       7             :  *    You may obtain a copy of the License at
       8             :  *
       9             :  *        http://www.apache.org/licenses/LICENSE-2.0
      10             :  *
      11             :  *    Unless required by applicable law or agreed to in writing, software
      12             :  *    distributed under the License is distributed on an "AS IS" BASIS,
      13             :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      14             :  *    See the License for the specific language governing permissions and
      15             :  *    limitations under the License.
      16             :  */
      17             : 
      18             : #include "ActiveResolveAttempts.h"
      19             : 
      20             : #include <lib/support/logging/CHIPLogging.h>
      21             : 
      22             : #include <algorithm>
      23             : 
      24             : using namespace chip;
      25             : 
      26             : namespace mdns {
      27             : namespace Minimal {
      28             : 
      29             : constexpr chip::System::Clock::Timeout ActiveResolveAttempts::kMaxRetryDelay;
      30             : 
      31          36 : void ActiveResolveAttempts::Reset()
      32             : 
      33             : {
      34         180 :     for (auto & item : mRetryQueue)
      35             :     {
      36         144 :         item.attempt.Clear();
      37             :     }
      38          36 : }
      39             : 
      40           4 : void ActiveResolveAttempts::Complete(const PeerId & peerId)
      41             : {
      42           6 :     for (auto & item : mRetryQueue)
      43             :     {
      44           6 :         if (item.attempt.Matches(peerId))
      45             :         {
      46           4 :             item.attempt.Clear();
      47           4 :             return;
      48             :         }
      49             :     }
      50             : 
      51             : #if CHIP_MINMDNS_HIGH_VERBOSITY
      52             :     // This may happen during boot time adverisements: nodes come online
      53             :     // and advertise their IP without any explicit queries for them
      54             :     ChipLogProgress(Discovery, "Discovered node without a pending query");
      55             : #endif
      56             : }
      57             : 
      58           0 : void ActiveResolveAttempts::CompleteCommissioner(const chip::Dnssd::DiscoveredNodeData & data)
      59             : {
      60           0 :     for (auto & item : mRetryQueue)
      61             :     {
      62           0 :         if (item.attempt.Matches(data, chip::Dnssd::DiscoveryType::kCommissionerNode))
      63             :         {
      64           0 :             item.attempt.Clear();
      65           0 :             return;
      66             :         }
      67             :     }
      68             : }
      69             : 
      70          34 : void ActiveResolveAttempts::CompleteCommissionable(const chip::Dnssd::DiscoveredNodeData & data)
      71             : {
      72         163 :     for (auto & item : mRetryQueue)
      73             :     {
      74         131 :         if (item.attempt.Matches(data, chip::Dnssd::DiscoveryType::kCommissionableNode))
      75             :         {
      76           2 :             item.attempt.Clear();
      77           2 :             return;
      78             :         }
      79             :     }
      80             : }
      81             : 
      82          32 : bool ActiveResolveAttempts::HasBrowseFor(chip::Dnssd::DiscoveryType type) const
      83             : {
      84         160 :     for (auto & item : mRetryQueue)
      85             :     {
      86         128 :         if (!item.attempt.IsBrowse())
      87             :         {
      88         128 :             continue;
      89             :         }
      90             : 
      91           0 :         if (item.attempt.BrowseData().type == type)
      92             :         {
      93           0 :             return true;
      94             :         }
      95             :     }
      96             : 
      97          32 :     return false;
      98             : }
      99             : 
     100          32 : void ActiveResolveAttempts::CompleteIpResolution(SerializedQNameIterator targetHostName)
     101             : {
     102         160 :     for (auto & item : mRetryQueue)
     103             :     {
     104         128 :         if (item.attempt.MatchesIpResolve(targetHostName))
     105             :         {
     106           0 :             item.attempt.Clear();
     107           0 :             return;
     108             :         }
     109             :     }
     110             : }
     111             : 
     112           0 : CHIP_ERROR ActiveResolveAttempts::CompleteAllBrowses()
     113             : {
     114           0 :     for (auto & item : mRetryQueue)
     115             :     {
     116           0 :         if (item.attempt.IsBrowse())
     117             :         {
     118           0 :             item.attempt.Clear();
     119             :         }
     120             :     }
     121             : 
     122           0 :     return CHIP_NO_ERROR;
     123             : }
     124             : 
     125           0 : void ActiveResolveAttempts::NodeIdResolutionNoLongerNeeded(const PeerId & peerId)
     126             : {
     127           0 :     for (auto & item : mRetryQueue)
     128             :     {
     129           0 :         if (item.attempt.Matches(peerId))
     130             :         {
     131           0 :             item.attempt.ConsumerRemoved();
     132           0 :             return;
     133             :         }
     134             :     }
     135             : }
     136             : 
     137          13 : void ActiveResolveAttempts::MarkPending(const chip::PeerId & peerId)
     138             : {
     139          13 :     MarkPending(ScheduledAttempt(peerId, /* firstSend */ true));
     140          13 : }
     141             : 
     142           4 : void ActiveResolveAttempts::MarkPending(const chip::Dnssd::DiscoveryFilter & filter, const chip::Dnssd::DiscoveryType type)
     143             : {
     144           4 :     MarkPending(ScheduledAttempt(filter, type, /* firstSend */ true));
     145           4 : }
     146             : 
     147           0 : void ActiveResolveAttempts::MarkPending(ScheduledAttempt::IpResolve && resolve)
     148             : {
     149           0 :     MarkPending(ScheduledAttempt(std::move(resolve), /* firstSend */ true));
     150           0 : }
     151             : 
     152          17 : void ActiveResolveAttempts::MarkPending(ScheduledAttempt && attempt)
     153             : {
     154             :     // Strategy when picking the peer id to use:
     155             :     //   1 if a matching peer id is already found, use that one
     156             :     //   2 if an 'unused' entry is found, use that
     157             :     //   3 otherwise expire the one with the largest nextRetryDelay
     158             :     //     or if equal nextRetryDelay, pick the one with the oldest
     159             :     //     queryDueTime
     160             : 
     161          17 :     RetryEntry * entryToUse = &mRetryQueue[0];
     162             : 
     163          62 :     for (size_t i = 1; i < kRetryQueueSize; i++)
     164             :     {
     165          47 :         if (entryToUse->attempt.Matches(attempt))
     166             :         {
     167           2 :             break; // best match possible
     168             :         }
     169             : 
     170          45 :         RetryEntry * entry = mRetryQueue + i;
     171             : 
     172             :         // Rule 1: attempt match always matches
     173          45 :         if (entry->attempt.Matches(attempt))
     174             :         {
     175           0 :             entryToUse = entry;
     176           0 :             continue;
     177             :         }
     178             : 
     179             :         // Rule 2: select unused entries
     180          45 :         if (!entryToUse->attempt.IsEmpty() && entry->attempt.IsEmpty())
     181             :         {
     182           6 :             entryToUse = entry;
     183           6 :             continue;
     184             :         }
     185          39 :         if (entryToUse->attempt.IsEmpty())
     186             :         {
     187          32 :             continue;
     188             :         }
     189             : 
     190             :         // Rule 3: both choices are used (have a defined node id):
     191             :         //    - try to find the one with the largest next delay (oldest request)
     192             :         //    - on same delay, use queryDueTime to determine the oldest request
     193             :         //      (the one with the smallest  due time was issued the longest time
     194             :         //       ago)
     195           7 :         if (entry->nextRetryDelay > entryToUse->nextRetryDelay)
     196             :         {
     197           0 :             entryToUse = entry;
     198             :         }
     199           7 :         else if ((entry->nextRetryDelay == entryToUse->nextRetryDelay) && (entry->queryDueTime < entryToUse->queryDueTime))
     200             :         {
     201           0 :             entryToUse = entry;
     202             :         }
     203             :     }
     204             : 
     205          17 :     if ((!entryToUse->attempt.IsEmpty()) && (!entryToUse->attempt.Matches(attempt)))
     206             :     {
     207             :         // TODO: node was evicted here, if/when resolution failures are
     208             :         // supported this could be a place for error callbacks
     209             :         //
     210             :         // Note however that this is NOT an actual 'timeout' it is showing
     211             :         // a burst of lookups for which we cannot maintain state. A reply may
     212             :         // still be received for this peer id (query was already sent on the
     213             :         // network)
     214           1 :         ChipLogError(Discovery, "Re-using pending resolve entry before reply was received.");
     215             :     }
     216             : 
     217          17 :     attempt.WillCoalesceWith(entryToUse->attempt);
     218          17 :     entryToUse->attempt        = attempt;
     219          17 :     entryToUse->queryDueTime   = mClock->GetMonotonicTimestamp();
     220          17 :     entryToUse->nextRetryDelay = System::Clock::Seconds16(1);
     221          17 : }
     222             : 
     223          84 : Optional<System::Clock::Timeout> ActiveResolveAttempts::GetTimeUntilNextExpectedResponse() const
     224             : {
     225          84 :     Optional<System::Clock::Timeout> minDelay = Optional<System::Clock::Timeout>::Missing();
     226             : 
     227          84 :     chip::System::Clock::Timestamp now = mClock->GetMonotonicTimestamp();
     228             : 
     229         374 :     for (auto & entry : mRetryQueue)
     230             :     {
     231         302 :         if (entry.attempt.IsEmpty())
     232             :         {
     233         219 :             continue;
     234             :         }
     235             : 
     236          83 :         if (now >= entry.queryDueTime)
     237             :         {
     238             :             // found an entry that needs processing right now
     239          12 :             return Optional<System::Clock::Timeout>::Value(0);
     240             :         }
     241             : 
     242          71 :         System::Clock::Timeout entryDelay = entry.queryDueTime - now;
     243          71 :         if (!minDelay.HasValue() || (minDelay.Value() > entryDelay))
     244             :         {
     245          42 :             minDelay.SetValue(entryDelay);
     246             :         }
     247             :     }
     248             : 
     249          72 :     return minDelay;
     250          84 : }
     251             : 
     252          85 : Optional<ActiveResolveAttempts::ScheduledAttempt> ActiveResolveAttempts::NextScheduled()
     253             : {
     254          85 :     chip::System::Clock::Timestamp now = mClock->GetMonotonicTimestamp();
     255             : 
     256         289 :     for (auto & entry : mRetryQueue)
     257             :     {
     258         247 :         if (entry.attempt.IsEmpty())
     259             :         {
     260         204 :             continue; // not a pending item
     261             :         }
     262             : 
     263         159 :         if (entry.queryDueTime > now)
     264             :         {
     265         112 :             continue; // not yet due
     266             :         }
     267             : 
     268          47 :         if (entry.nextRetryDelay > kMaxRetryDelay)
     269             :         {
     270           4 :             ChipLogError(Discovery, "Timeout waiting for mDNS resolution.");
     271           4 :             entry.attempt.Clear();
     272           4 :             continue;
     273             :         }
     274             : 
     275          43 :         entry.queryDueTime = now + entry.nextRetryDelay;
     276          43 :         entry.nextRetryDelay *= 2;
     277             : 
     278          43 :         Optional<ScheduledAttempt> attempt = MakeOptional(entry.attempt);
     279          43 :         entry.attempt.firstSend            = false;
     280             : 
     281          43 :         return attempt;
     282          43 :     }
     283             : 
     284          42 :     return Optional<ScheduledAttempt>::Missing();
     285             : }
     286             : 
     287           0 : bool ActiveResolveAttempts::IsWaitingForIpResolutionFor(SerializedQNameIterator hostName) const
     288             : {
     289           0 :     for (auto & entry : mRetryQueue)
     290             :     {
     291           0 :         if (entry.attempt.IsEmpty())
     292             :         {
     293           0 :             continue; // not a pending item
     294             :         }
     295             : 
     296           0 :         if (!entry.attempt.IsIpResolve())
     297             :         {
     298           0 :             continue;
     299             :         }
     300             : 
     301           0 :         if (hostName == entry.attempt.IpResolveData().hostName.Content())
     302             :         {
     303           0 :             return true;
     304             :         }
     305             :     }
     306             : 
     307           0 :     return false;
     308             : }
     309             : 
     310             : } // namespace Minimal
     311             : } // namespace mdns

Generated by: LCOV version 1.14