Matter SDK Coverage Report
Current view: top level - app/server-cluster - ServerClusterInterfaceRegistry.cpp (source / functions) Coverage Total Hit
Test: SHA:97920baf58e6bf4a18be41e121e08f13676f36b0 Lines: 100.0 % 72 72
Test Date: 2025-08-17 07:11:11 Functions: 100.0 % 7 7

            Line data    Source code
       1              : /*
       2              :  *    Copyright (c) 2025 Project CHIP Authors
       3              :  *    All rights reserved.
       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              : #include <app/server-cluster/ServerClusterInterfaceRegistry.h>
      18              : 
      19              : #include <app/ConcreteClusterPath.h>
      20              : #include <app/server-cluster/ServerClusterInterface.h>
      21              : #include <lib/core/CHIPError.h>
      22              : #include <lib/core/DataModelTypes.h>
      23              : #include <lib/support/CHIPMem.h>
      24              : #include <lib/support/CodeUtils.h>
      25              : #include <optional>
      26              : 
      27              : namespace chip {
      28              : namespace app {
      29              : 
      30          187 : ServerClusterInterfaceRegistry::~ServerClusterInterfaceRegistry()
      31              : {
      32          403 :     while (mRegistrations != nullptr)
      33              :     {
      34          216 :         ServerClusterRegistration * next = mRegistrations->next;
      35          216 :         if (mContext.has_value())
      36              :         {
      37            3 :             mRegistrations->serverClusterInterface->Shutdown();
      38              :         }
      39          216 :         mRegistrations->next = nullptr;
      40          216 :         mRegistrations       = next;
      41              :     }
      42          187 : }
      43              : 
      44         1041 : CHIP_ERROR ServerClusterInterfaceRegistry::Register(ServerClusterRegistration & entry)
      45              : {
      46              :     // we have no strong way to check if entry is already registered somewhere else, so we use "next" as some
      47              :     // form of double-check
      48         1041 :     VerifyOrReturnError(entry.next == nullptr, CHIP_ERROR_INVALID_ARGUMENT);
      49         1040 :     VerifyOrReturnError(entry.serverClusterInterface != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
      50              : 
      51         1040 :     Span<const ConcreteClusterPath> paths = entry.serverClusterInterface->GetPaths();
      52         1040 :     VerifyOrReturnError(!paths.empty(), CHIP_ERROR_INVALID_ARGUMENT);
      53              : 
      54         2081 :     for (const ConcreteClusterPath & path : paths)
      55              :     {
      56         1047 :         VerifyOrReturnError(path.HasValidIds(), CHIP_ERROR_INVALID_ARGUMENT);
      57              : 
      58              :         // Double-checking for duplicates makes the checks O(n^2) on the total number of registered
      59              :         // items. We preserve this however we may want to make this optional at some point in time.
      60         1044 :         VerifyOrReturnError(Get(path) == nullptr, CHIP_ERROR_DUPLICATE_KEY_ID);
      61              :     }
      62              : 
      63         1034 :     if (mContext.has_value())
      64              :     {
      65            9 :         ReturnErrorOnFailure(entry.serverClusterInterface->Startup(*mContext));
      66              :     }
      67              : 
      68         1034 :     entry.next     = mRegistrations;
      69         1034 :     mRegistrations = &entry;
      70              : 
      71         1034 :     return CHIP_NO_ERROR;
      72              : }
      73              : 
      74          815 : CHIP_ERROR ServerClusterInterfaceRegistry::Unregister(ServerClusterInterface * what)
      75              : {
      76          815 :     ServerClusterRegistration * prev    = nullptr;
      77          815 :     ServerClusterRegistration * current = mRegistrations;
      78              : 
      79        60077 :     while (current != nullptr)
      80              :     {
      81        59674 :         if (current->serverClusterInterface == what)
      82              :         {
      83              :             // take the item out of the current list and return it.
      84          412 :             ServerClusterRegistration * next = current->next;
      85              : 
      86          412 :             if (prev == nullptr)
      87              :             {
      88           17 :                 mRegistrations = next;
      89              :             }
      90              :             else
      91              :             {
      92          395 :                 prev->next = next;
      93              :             }
      94              : 
      95          412 :             if (mCachedInterface == current->serverClusterInterface)
      96              :             {
      97          407 :                 mCachedInterface = nullptr;
      98              :             }
      99              : 
     100          412 :             current->next = nullptr; // Make sure current does not look like part of a list.
     101          412 :             if (mContext.has_value())
     102              :             {
     103            7 :                 current->serverClusterInterface->Shutdown();
     104              :             }
     105              : 
     106          412 :             return CHIP_NO_ERROR;
     107              :         }
     108              : 
     109        59262 :         prev    = current;
     110        59262 :         current = current->next;
     111              :     }
     112              : 
     113          403 :     return CHIP_ERROR_NOT_FOUND;
     114              : }
     115              : 
     116        45312 : ServerClusterInterface * ServerClusterInterfaceRegistry::Get(const ConcreteClusterPath & clusterPath)
     117              : {
     118              :     // Check the cache to speed things up
     119        45312 :     if ((mCachedInterface != nullptr) && mCachedInterface->PathsContains(clusterPath))
     120              :     {
     121           18 :         return mCachedInterface;
     122              :     }
     123              : 
     124              :     // The cluster searched for is not cached, do a linear search for it
     125        45294 :     ServerClusterRegistration * current = mRegistrations;
     126              : 
     127      1743614 :     while (current != nullptr)
     128              :     {
     129      1699740 :         if (current->serverClusterInterface->PathsContains(clusterPath))
     130              :         {
     131         1420 :             mCachedInterface = current->serverClusterInterface;
     132         1420 :             return mCachedInterface;
     133              :         }
     134              : 
     135      1698320 :         current = current->next;
     136              :     }
     137              : 
     138              :     // not found
     139        43874 :     return nullptr;
     140              : }
     141              : 
     142          161 : CHIP_ERROR ServerClusterInterfaceRegistry::SetContext(ServerClusterContext && context)
     143              : {
     144          161 :     if (mContext.has_value())
     145              :     {
     146              :         // if there is no difference, do not re-initialize.
     147            8 :         VerifyOrReturnError(*mContext != context, CHIP_NO_ERROR);
     148            6 :         ClearContext();
     149              :     }
     150              : 
     151          159 :     mContext.emplace(std::move(context));
     152          159 :     bool had_failure = false;
     153              : 
     154          171 :     for (ServerClusterRegistration * registration = mRegistrations; registration != nullptr; registration = registration->next)
     155              :     {
     156           12 :         CHIP_ERROR err = registration->serverClusterInterface->Startup(*mContext);
     157           12 :         if (err != CHIP_NO_ERROR)
     158              :         {
     159              : #if CHIP_ERROR_LOGGING
     160              :             // Paths MUST contain at least one element. Log the first one for identification (even though there may be more)
     161            1 :             const ConcreteClusterPath path = registration->serverClusterInterface->GetPaths().front();
     162            1 :             ChipLogError(DataManagement, "Cluster %u/" ChipLogFormatMEI " startup failed: %" CHIP_ERROR_FORMAT, path.mEndpointId,
     163              :                          ChipLogValueMEI(path.mClusterId), err.Format());
     164              : #endif
     165            1 :             had_failure = true;
     166              :             // NOTE: this makes the object be in an awkward state:
     167              :             //       - cluster is not initialized
     168              :             //       - mContext is valid
     169              :             //       As a result, ::Shutdown on this cluster WILL be called even if startup failed.
     170              :         }
     171              :     }
     172              : 
     173          159 :     if (had_failure)
     174              :     {
     175            1 :         return CHIP_ERROR_HAD_FAILURES;
     176              :     }
     177              : 
     178          158 :     return CHIP_NO_ERROR;
     179              : }
     180              : 
     181          173 : void ServerClusterInterfaceRegistry::ClearContext()
     182              : {
     183          173 :     if (!mContext.has_value())
     184              :     {
     185           18 :         return;
     186              :     }
     187          165 :     for (ServerClusterRegistration * registration = mRegistrations; registration != nullptr; registration = registration->next)
     188              :     {
     189           10 :         registration->serverClusterInterface->Shutdown();
     190              :     }
     191              : 
     192          155 :     mContext.reset();
     193              : }
     194              : 
     195            5 : ServerClusterInterfaceRegistry::ServerClusterInstances ServerClusterInterfaceRegistry::AllServerClusterInstances()
     196              : {
     197            5 :     return { mRegistrations };
     198              : }
     199              : 
     200              : } // namespace app
     201              : } // namespace chip
        

Generated by: LCOV version 2.0-1