Matter SDK Coverage Report
Current view: top level - app/server-cluster - ServerClusterInterfaceRegistry.cpp (source / functions) Coverage Total Hit
Test: SHA:2a48c1efeab1c0f76f3adb3a0940b0f7de706453 Lines: 100.0 % 72 72
Test Date: 2026-01-31 08:14:20 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          241 : ServerClusterInterfaceRegistry::~ServerClusterInterfaceRegistry()
      31              : {
      32          459 :     while (mRegistrations != nullptr)
      33              :     {
      34          218 :         ServerClusterRegistration * next = mRegistrations->next;
      35          218 :         if (mContext.has_value())
      36              :         {
      37            5 :             mRegistrations->serverClusterInterface->Shutdown(ClusterShutdownType::kClusterShutdown);
      38              :         }
      39          218 :         mRegistrations->next = nullptr;
      40          218 :         mRegistrations       = next;
      41              :     }
      42          241 : }
      43              : 
      44         1072 : 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         1072 :     VerifyOrReturnError(entry.next == nullptr, CHIP_ERROR_INVALID_ARGUMENT);
      49         1071 :     VerifyOrReturnError(entry.serverClusterInterface != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
      50              : 
      51         1071 :     Span<const ConcreteClusterPath> paths = entry.serverClusterInterface->GetPaths();
      52         1071 :     VerifyOrReturnError(!paths.empty(), CHIP_ERROR_INVALID_ARGUMENT);
      53              : 
      54         2145 :     for (const ConcreteClusterPath & path : paths)
      55              :     {
      56         1081 :         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         1078 :         VerifyOrReturnError(Get(path) == nullptr, CHIP_ERROR_DUPLICATE_KEY_ID);
      61              :     }
      62              : 
      63         1064 :     if (mContext.has_value())
      64              :     {
      65              :         // To preserve similarity with SetContext, do not fail the register even if Startup fails.
      66              :         // This will cause Shutdown to be called for both successful and failed startups.
      67           24 :         LogErrorOnFailure(entry.serverClusterInterface->Startup(*mContext));
      68              :     }
      69              : 
      70         1064 :     entry.next     = mRegistrations;
      71         1064 :     mRegistrations = &entry;
      72              : 
      73         1064 :     return CHIP_NO_ERROR;
      74              : }
      75              : 
      76          843 : CHIP_ERROR ServerClusterInterfaceRegistry::Unregister(ServerClusterInterface * what, ClusterShutdownType clusterShutdownType)
      77              : {
      78          843 :     ServerClusterRegistration * prev    = nullptr;
      79          843 :     ServerClusterRegistration * current = mRegistrations;
      80              : 
      81        60106 :     while (current != nullptr)
      82              :     {
      83        59703 :         if (current->serverClusterInterface == what)
      84              :         {
      85              :             // take the item out of the current list and return it.
      86          440 :             ServerClusterRegistration * next = current->next;
      87              : 
      88          440 :             if (prev == nullptr)
      89              :             {
      90           44 :                 mRegistrations = next;
      91              :             }
      92              :             else
      93              :             {
      94          396 :                 prev->next = next;
      95              :             }
      96              : 
      97          440 :             if (mCachedInterface == current->serverClusterInterface)
      98              :             {
      99          417 :                 mCachedInterface = nullptr;
     100              :             }
     101              : 
     102          440 :             current->next = nullptr; // Make sure current does not look like part of a list.
     103          440 :             if (mContext.has_value())
     104              :             {
     105           10 :                 current->serverClusterInterface->Shutdown(clusterShutdownType);
     106              :             }
     107              : 
     108          440 :             return CHIP_NO_ERROR;
     109              :         }
     110              : 
     111        59263 :         prev    = current;
     112        59263 :         current = current->next;
     113              :     }
     114              : 
     115          403 :     return CHIP_ERROR_NOT_FOUND;
     116              : }
     117              : 
     118        38438 : ServerClusterInterface * ServerClusterInterfaceRegistry::Get(const ConcreteClusterPath & clusterPath)
     119              : {
     120              :     // Check the cache to speed things up
     121        38438 :     if ((mCachedInterface != nullptr) && mCachedInterface->PathsContains(clusterPath))
     122              :     {
     123           21 :         return mCachedInterface;
     124              :     }
     125              : 
     126              :     // The cluster searched for is not cached, do a linear search for it
     127        38417 :     ServerClusterRegistration * current = mRegistrations;
     128              : 
     129      1736744 :     while (current != nullptr)
     130              :     {
     131      1699757 :         if (current->serverClusterInterface->PathsContains(clusterPath))
     132              :         {
     133         1430 :             mCachedInterface = current->serverClusterInterface;
     134         1430 :             return mCachedInterface;
     135              :         }
     136              : 
     137      1698327 :         current = current->next;
     138              :     }
     139              : 
     140              :     // not found
     141        36987 :     return nullptr;
     142              : }
     143              : 
     144          167 : CHIP_ERROR ServerClusterInterfaceRegistry::SetContext(ServerClusterContext && context)
     145              : {
     146          167 :     if (mContext.has_value())
     147              :     {
     148              :         // if there is no difference, do not re-initialize.
     149            4 :         VerifyOrReturnError(*mContext != context, CHIP_NO_ERROR);
     150            2 :         ClearContext();
     151              :     }
     152              : 
     153          165 :     mContext.emplace(std::move(context));
     154          165 :     bool had_failure = false;
     155              : 
     156          179 :     for (ServerClusterRegistration * registration = mRegistrations; registration != nullptr; registration = registration->next)
     157              :     {
     158           14 :         CHIP_ERROR err = registration->serverClusterInterface->Startup(*mContext);
     159           28 :         if (err != CHIP_NO_ERROR)
     160              :         {
     161              : #if CHIP_ERROR_LOGGING
     162              :             // Paths MUST contain at least one element. Log the first one for identification (even though there may be more)
     163            3 :             const ConcreteClusterPath path = registration->serverClusterInterface->GetPaths().front();
     164            3 :             ChipLogError(DataManagement, "Cluster %u/" ChipLogFormatMEI " startup failed: %" CHIP_ERROR_FORMAT, path.mEndpointId,
     165              :                          ChipLogValueMEI(path.mClusterId), err.Format());
     166              : #endif
     167            3 :             had_failure = true;
     168              :             // NOTE: this makes the object be in an awkward state:
     169              :             //       - cluster is not initialized
     170              :             //       - mContext is valid
     171              :             //       As a result, ::Shutdown on this cluster WILL be called even if startup failed.
     172              :         }
     173              :     }
     174              : 
     175          165 :     if (had_failure)
     176              :     {
     177            2 :         return CHIP_ERROR_HAD_FAILURES;
     178              :     }
     179              : 
     180          163 :     return CHIP_NO_ERROR;
     181              : }
     182              : 
     183          185 : void ServerClusterInterfaceRegistry::ClearContext()
     184              : {
     185          185 :     if (!mContext.has_value())
     186              :     {
     187           29 :         return;
     188              :     }
     189          166 :     for (ServerClusterRegistration * registration = mRegistrations; registration != nullptr; registration = registration->next)
     190              :     {
     191           10 :         registration->serverClusterInterface->Shutdown(ClusterShutdownType::kClusterShutdown);
     192              :     }
     193              : 
     194          156 :     mContext.reset();
     195              : }
     196              : 
     197          300 : ServerClusterInterfaceRegistry::ServerClusterInstances ServerClusterInterfaceRegistry::AllServerClusterInstances()
     198              : {
     199          300 :     return { mRegistrations };
     200              : }
     201              : 
     202              : } // namespace app
     203              : } // namespace chip
        

Generated by: LCOV version 2.0-1