Matter SDK Coverage Report
Current view: top level - data-model-providers/codegen - ServerClusterInterfaceRegistry.cpp (source / functions) Coverage Total Hit
Test: SHA:4cbce7f768f16e614f5a8ccb8cd93c92cbeae70d Lines: 100.0 % 92 92
Test Date: 2025-04-26 07:09:35 Functions: 100.0 % 8 8

            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 <data-model-providers/codegen/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          172 : ServerClusterInterfaceRegistry::~ServerClusterInterfaceRegistry()
      31              : {
      32          375 :     while (mRegistrations != nullptr)
      33              :     {
      34          203 :         ServerClusterRegistration * next = mRegistrations->next;
      35          203 :         if (mContext.has_value())
      36              :         {
      37            1 :             mRegistrations->serverClusterInterface->Shutdown();
      38              :         }
      39          203 :         mRegistrations->next = nullptr;
      40          203 :         mRegistrations       = next;
      41              :     }
      42          172 : }
      43              : 
      44         1018 : 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         1018 :     VerifyOrReturnError(entry.next == nullptr, CHIP_ERROR_INVALID_ARGUMENT);
      49         1017 :     VerifyOrReturnError(entry.serverClusterInterface != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
      50              : 
      51         1017 :     Span<const ConcreteClusterPath> paths = entry.serverClusterInterface->GetPaths();
      52         1017 :     VerifyOrReturnError(!paths.empty(), CHIP_ERROR_INVALID_ARGUMENT);
      53              : 
      54         2035 :     for (const ConcreteClusterPath & path : paths)
      55              :     {
      56         1023 :         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         1021 :         VerifyOrReturnError(Get(path) == nullptr, CHIP_ERROR_DUPLICATE_KEY_ID);
      61              : 
      62              :         // Codegen registry requirements (so that we can support endpoint unregistration): every
      63              :         // path must belong to the same endpoint id.
      64         1020 :         VerifyOrReturnError(path.mEndpointId == paths[0].mEndpointId, CHIP_ERROR_INVALID_ARGUMENT);
      65              :     }
      66              : 
      67         1012 :     if (mContext.has_value())
      68              :     {
      69            4 :         ReturnErrorOnFailure(entry.serverClusterInterface->Startup(*mContext));
      70              :     }
      71              : 
      72         1012 :     entry.next     = mRegistrations;
      73         1012 :     mRegistrations = &entry;
      74              : 
      75         1012 :     return CHIP_NO_ERROR;
      76              : }
      77              : 
      78          806 : CHIP_ERROR ServerClusterInterfaceRegistry::Unregister(ServerClusterInterface * what)
      79              : {
      80          806 :     ServerClusterRegistration * prev    = nullptr;
      81          806 :     ServerClusterRegistration * current = mRegistrations;
      82              : 
      83        60063 :     while (current != nullptr)
      84              :     {
      85        59662 :         if (current->serverClusterInterface == what)
      86              :         {
      87              :             // take the item out of the current list and return it.
      88          405 :             ServerClusterRegistration * next = current->next;
      89              : 
      90          405 :             if (prev == nullptr)
      91              :             {
      92           13 :                 mRegistrations = next;
      93              :             }
      94              :             else
      95              :             {
      96          392 :                 prev->next = next;
      97              :             }
      98              : 
      99          405 :             if (mCachedInterface == current->serverClusterInterface)
     100              :             {
     101          403 :                 mCachedInterface = nullptr;
     102              :             }
     103              : 
     104          405 :             current->next = nullptr; // Make sure current does not look like part of a list.
     105          405 :             if (mContext.has_value())
     106              :             {
     107            3 :                 current->serverClusterInterface->Shutdown();
     108              :             }
     109              : 
     110          405 :             return CHIP_NO_ERROR;
     111              :         }
     112              : 
     113        59257 :         prev    = current;
     114        59257 :         current = current->next;
     115              :     }
     116              : 
     117          401 :     return CHIP_ERROR_NOT_FOUND;
     118              : }
     119              : 
     120        16920 : ServerClusterInterfaceRegistry::ClustersList ServerClusterInterfaceRegistry::ClustersOnEndpoint(EndpointId endpointId)
     121              : {
     122        16920 :     return { mRegistrations, endpointId };
     123              : }
     124              : 
     125           23 : void ServerClusterInterfaceRegistry::UnregisterAllFromEndpoint(EndpointId endpointId)
     126              : {
     127           23 :     ServerClusterRegistration * prev    = nullptr;
     128           23 :     ServerClusterRegistration * current = mRegistrations;
     129         2175 :     while (current != nullptr)
     130              :     {
     131              :         // Requirements for Paths:
     132              :         //   - GetPaths() MUST be non-empty
     133              :         //   - GetPaths() MUST belong to the same endpoint
     134              :         // Loop below relies on that: if the endpoint matches, it can be removed
     135         2152 :         auto paths = current->serverClusterInterface->GetPaths();
     136         2152 :         if (paths.empty() || paths.front().mEndpointId == endpointId)
     137              :         {
     138          404 :             if (mCachedInterface == current->serverClusterInterface)
     139              :             {
     140            4 :                 mCachedInterface = nullptr;
     141              :             }
     142          404 :             if (prev == nullptr)
     143              :             {
     144           35 :                 mRegistrations = current->next;
     145              :             }
     146              :             else
     147              :             {
     148          369 :                 prev->next = current->next;
     149              :             }
     150          404 :             ServerClusterRegistration * actual_next = current->next;
     151              : 
     152          404 :             current->next = nullptr; // Make sure current does not look like part of a list.
     153          404 :             if (mContext.has_value())
     154              :             {
     155            1 :                 current->serverClusterInterface->Shutdown();
     156              :             }
     157              : 
     158          404 :             current = actual_next;
     159              :         }
     160              :         else
     161              :         {
     162         1748 :             prev    = current;
     163         1748 :             current = current->next;
     164              :         }
     165              :     }
     166           23 : }
     167              : 
     168        38144 : ServerClusterInterface * ServerClusterInterfaceRegistry::Get(const ConcreteClusterPath & clusterPath)
     169              : {
     170              :     // Check the cache to speed things up
     171        38144 :     if ((mCachedInterface != nullptr) && mCachedInterface->PathsContains(clusterPath))
     172              :     {
     173            9 :         return mCachedInterface;
     174              :     }
     175              : 
     176              :     // The cluster searched for is not cached, do a linear search for it
     177        38135 :     ServerClusterRegistration * current = mRegistrations;
     178              : 
     179      1736435 :     while (current != nullptr)
     180              :     {
     181      1699709 :         if (current->serverClusterInterface->PathsContains(clusterPath))
     182              :         {
     183         1409 :             mCachedInterface = current->serverClusterInterface;
     184         1409 :             return mCachedInterface;
     185              :         }
     186              : 
     187      1698300 :         current = current->next;
     188              :     }
     189              : 
     190              :     // not found
     191        36726 :     return nullptr;
     192              : }
     193              : 
     194          400 : CHIP_ERROR ServerClusterInterfaceRegistry::SetContext(ServerClusterContext && context)
     195              : {
     196          400 :     if (mContext.has_value())
     197              :     {
     198              :         // if there is no difference, do not re-initialize.
     199          229 :         VerifyOrReturnError(*mContext != context, CHIP_NO_ERROR);
     200            3 :         ClearContext();
     201              :     }
     202              : 
     203          174 :     mContext.emplace(std::move(context));
     204          174 :     bool had_failure = false;
     205              : 
     206          181 :     for (ServerClusterRegistration * registration = mRegistrations; registration != nullptr; registration = registration->next)
     207              :     {
     208            7 :         CHIP_ERROR err = registration->serverClusterInterface->Startup(*mContext);
     209            7 :         if (err != CHIP_NO_ERROR)
     210              :         {
     211              : #if CHIP_ERROR_LOGGING
     212              :             // Paths MUST contain at least one element. Log the first one for identification (even though there may be more)
     213            1 :             const ConcreteClusterPath path = registration->serverClusterInterface->GetPaths().front();
     214            1 :             ChipLogError(DataManagement, "Cluster %u/" ChipLogFormatMEI " startup failed: %" CHIP_ERROR_FORMAT, path.mEndpointId,
     215              :                          ChipLogValueMEI(path.mClusterId), err.Format());
     216              : #endif
     217            1 :             had_failure = true;
     218              :             // NOTE: this makes the object be in an awkward state:
     219              :             //       - cluster is not initialized
     220              :             //       - mContext is valid
     221              :             //       As a result, ::Shutdown on this cluster WILL be called even if startup failed.
     222              :         }
     223              :     }
     224              : 
     225          174 :     if (had_failure)
     226              :     {
     227            1 :         return CHIP_ERROR_HAD_FAILURES;
     228              :     }
     229              : 
     230          173 :     return CHIP_NO_ERROR;
     231              : }
     232              : 
     233          162 : void ServerClusterInterfaceRegistry::ClearContext()
     234              : {
     235          162 :     if (!mContext.has_value())
     236              :     {
     237            4 :         return;
     238              :     }
     239          164 :     for (ServerClusterRegistration * registration = mRegistrations; registration != nullptr; registration = registration->next)
     240              :     {
     241            6 :         registration->serverClusterInterface->Shutdown();
     242              :     }
     243              : 
     244          158 :     mContext.reset();
     245              : }
     246              : 
     247              : } // namespace app
     248              : } // namespace chip
        

Generated by: LCOV version 2.0-1