LCOV - code coverage report
Current view: top level - lib/support - PersistedCounter.h (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 27 37 73.0 %
Date: 2024-02-15 08:20:41 Functions: 6 8 75.0 %

          Line data    Source code
       1             : /*
       2             :  *
       3             :  *    Copyright (c) 2020 Project CHIP Authors
       4             :  *    Copyright (c) 2016-2017 Nest Labs, Inc.
       5             :  *    All rights reserved.
       6             :  *
       7             :  *    Licensed under the Apache License, Version 2.0 (the "License");
       8             :  *    you may not use this file except in compliance with the License.
       9             :  *    You may obtain a copy of the License at
      10             :  *
      11             :  *        http://www.apache.org/licenses/LICENSE-2.0
      12             :  *
      13             :  *    Unless required by applicable law or agreed to in writing, software
      14             :  *    distributed under the License is distributed on an "AS IS" BASIS,
      15             :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      16             :  *    See the License for the specific language governing permissions and
      17             :  *    limitations under the License.
      18             :  */
      19             : 
      20             : /**
      21             :  * @file
      22             :  *
      23             :  * @brief
      24             :  *   Class declarations for a monotonically-increasing counter that is periodically
      25             :  *   saved to the provided storage.
      26             :  */
      27             : 
      28             : #pragma once
      29             : 
      30             : #include <lib/core/CHIPEncoding.h>
      31             : #include <lib/core/CHIPPersistentStorageDelegate.h>
      32             : #include <lib/support/CHIPCounter.h>
      33             : #include <lib/support/CodeUtils.h>
      34             : #include <lib/support/DefaultStorageKeyAllocator.h>
      35             : 
      36             : namespace chip {
      37             : 
      38             : /**
      39             :  * @class PersistedCounter
      40             :  *
      41             :  * @brief
      42             :  *   A class for managing a counter as an integer value intended to persist
      43             :  *   across reboots.
      44             :  *
      45             :  * Counter values are always set to start at a multiple of a bootup value
      46             :  * "epoch".
      47             :  *
      48             :  * Example:
      49             :  *
      50             :  * - Assuming epoch is 100 via PersistedCounter::Init(_, 100) and GetValue +
      51             :  *   AdvanceValue is called, we get the following outputs:
      52             :  *
      53             :  *   - Output: 0, 1, 2, 3, 4  <reboot/reinit>
      54             :  *   - Output: 100, 101, 102, 103, 104, 105 <reboot/reinit>
      55             :  *   - Output: 200, 201, 202, ...., 299, 300, 301, 302 <reboot/reinit>
      56             :  *   - Output: 400, 401 ...
      57             :  *
      58             :  */
      59             : template <typename T>
      60             : class PersistedCounter : public MonotonicallyIncreasingCounter<T>
      61             : {
      62             : public:
      63           1 :     PersistedCounter() : mKey(StorageKeyName::Uninitialized()) {}
      64           1 :     ~PersistedCounter() override {}
      65             : 
      66             :     /**
      67             :      *  @brief
      68             :      *    Initialize a PersistedCounter object.
      69             :      *
      70             :      *  @param[in] aStorage the storage to use for the counter values.
      71             :      *  @param[in] aKey the key to use for storing the counter values.
      72             :      *  @param[in] aEpoch  On bootup, values we vend will start at a
      73             :      *                     multiple of this parameter.
      74             :      *
      75             :      *  @return CHIP_ERROR_INVALID_ARGUMENT if aStorageDelegate or aKey is NULL
      76             :      *          CHIP_ERROR_INVALID_INTEGER_VALUE if aEpoch is 0.
      77             :      *          CHIP_NO_ERROR otherwise
      78             :      */
      79           1 :     CHIP_ERROR Init(PersistentStorageDelegate * aStorage, StorageKeyName aKey, T aEpoch)
      80             :     {
      81           1 :         VerifyOrReturnError(aStorage != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
      82           1 :         VerifyOrReturnError(aKey.IsInitialized(), CHIP_ERROR_INVALID_ARGUMENT);
      83           1 :         VerifyOrReturnError(aEpoch > 0, CHIP_ERROR_INVALID_INTEGER_VALUE);
      84             : 
      85           1 :         mStorage = aStorage;
      86           1 :         mKey     = aKey;
      87           1 :         mEpoch   = aEpoch;
      88             : 
      89             :         T startValue;
      90             : 
      91             :         // Read our previously-stored starting value.
      92           1 :         ReturnErrorOnFailure(ReadStartValue(startValue));
      93             : 
      94             : #if CHIP_CONFIG_PERSISTED_COUNTER_DEBUG_LOGGING
      95             :         if constexpr (std::is_same_v<decltype(startValue), uint64_t>)
      96             :         {
      97             :             ChipLogDetail(EventLogging, "PersistedCounter::Init() aEpoch 0x" ChipLogFormatX64 " startValue 0x" ChipLogFormatX64,
      98             :                           ChipLogValueX64(aEpoch), ChipLogValueX64(startValue));
      99             :         }
     100             :         else if (std::is_same_v<decltype(startValue), uint32_t>)
     101             :         {
     102             :             ChipLogDetail(EventLogging, "PersistedCounter::Init() aEpoch 0x%" PRIx32 " startValue 0x%" PRIx32,
     103             :                           static_cast<uint32_t>(aEpoch), static_cast<uint32_t>(startValue));
     104             :         }
     105             : #endif
     106             : 
     107           1 :         ReturnErrorOnFailure(PersistNextEpochStart(startValue + aEpoch));
     108             : 
     109             :         // This will set the starting value, after which we're ready.
     110           1 :         return MonotonicallyIncreasingCounter<T>::Init(startValue);
     111             :     }
     112             : 
     113             :     /**
     114             :      *  @brief
     115             :      *  Increment the counter and write to persisted storage if we've completed
     116             :      *  the current epoch.
     117             :      *
     118             :      *  @return Any error returned by a write to persisted storage.
     119             :      */
     120           0 :     CHIP_ERROR Advance() override
     121             :     {
     122           0 :         VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
     123           0 :         VerifyOrReturnError(mKey.IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
     124             : 
     125           0 :         ReturnErrorOnFailure(MonotonicallyIncreasingCounter<T>::Advance());
     126             : 
     127           0 :         if (MonotonicallyIncreasingCounter<T>::GetValue() >= mNextEpoch)
     128             :         {
     129             :             // Value advanced past the previously persisted "start point".
     130             :             // Ensure that a new starting point is persisted.
     131           0 :             ReturnErrorOnFailure(PersistNextEpochStart(mNextEpoch + mEpoch));
     132             : 
     133             :             // Advancing the epoch should have ensured that the current value
     134             :             // is valid
     135           0 :             VerifyOrReturnError(MonotonicallyIncreasingCounter<T>::GetValue() < mNextEpoch, CHIP_ERROR_INTERNAL);
     136             :         }
     137           0 :         return CHIP_NO_ERROR;
     138             :     }
     139             : 
     140             : private:
     141             :     /**
     142             :      *  @brief
     143             :      *    Write out the counter value to persistent storage.
     144             :      *
     145             :      *  @param[in] aStartValue  The counter value to write out.
     146             :      *
     147             :      *  @return Any error returned by a write to persistent storage.
     148             :      */
     149           1 :     CHIP_ERROR PersistNextEpochStart(T aStartValue)
     150             :     {
     151           1 :         mNextEpoch = aStartValue;
     152             : #if CHIP_CONFIG_PERSISTED_COUNTER_DEBUG_LOGGING
     153             :         if constexpr (std::is_same_v<decltype(aStartValue), uint64_t>)
     154             :         {
     155             :             ChipLogDetail(EventLogging, "PersistedCounter::WriteStartValue() aStartValue 0x" ChipLogFormatX64,
     156             :                           ChipLogValueX64(aStartValue));
     157             :         }
     158             :         else
     159             :         {
     160             :             ChipLogDetail(EventLogging, "PersistedCounter::WriteStartValue() aStartValue 0x%" PRIx32,
     161             :                           static_cast<uint32_t>(aStartValue));
     162             :         }
     163             : #endif
     164             : 
     165           1 :         T valueLE = Encoding::LittleEndian::HostSwap<T>(aStartValue);
     166           1 :         return mStorage->SyncSetKeyValue(mKey.KeyName(), &valueLE, sizeof(valueLE));
     167             :     }
     168             : 
     169             :     /**
     170             :      *  @brief
     171             :      *    Read our starting counter value (if we have one) in from persistent storage.
     172             :      *
     173             :      *  @param[in,out] aStartValue  The value read out.
     174             :      *
     175             :      *  @return Any error returned by a read from persistent storage.
     176             :      */
     177           1 :     CHIP_ERROR ReadStartValue(T & aStartValue)
     178             :     {
     179           1 :         T valueLE     = GetInitialCounterValue();
     180           1 :         uint16_t size = sizeof(valueLE);
     181             : 
     182           1 :         VerifyOrReturnError(mKey.IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
     183             : 
     184           1 :         CHIP_ERROR err = mStorage->SyncGetKeyValue(mKey.KeyName(), &valueLE, size);
     185           1 :         if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)
     186             :         {
     187             :             // No previously-stored value, no worries, the counter is initialized to zero.
     188             :             // Suppress the error.
     189           1 :             err = CHIP_NO_ERROR;
     190             :         }
     191             :         else
     192             :         {
     193             :             // TODO: Figure out how to avoid a bootloop here.  Maybe we should just
     194             :             // init to 0?  Or a random value?
     195           0 :             ReturnErrorOnFailure(err);
     196             :         }
     197             : 
     198           1 :         if (size != sizeof(valueLE))
     199             :         {
     200             :             // TODO: Again, figure out whether this could lead to bootloops.
     201           0 :             return CHIP_ERROR_INCORRECT_STATE;
     202             :         }
     203             : 
     204           1 :         aStartValue = Encoding::LittleEndian::HostSwap<T>(valueLE);
     205             : 
     206             : #if CHIP_CONFIG_PERSISTED_COUNTER_DEBUG_LOGGING
     207             :         if constexpr (std::is_same_v<decltype(aStartValue), uint64_t>)
     208             :         {
     209             :             ChipLogDetail(EventLogging, "PersistedCounter::ReadStartValue() aStartValue 0x" ChipLogFormatX64,
     210             :                           ChipLogValueX64(aStartValue));
     211             :         }
     212             :         else
     213             :         {
     214             :             ChipLogDetail(EventLogging, "PersistedCounter::ReadStartValue() aStartValue 0x%" PRIx32,
     215             :                           static_cast<uint32_t>(aStartValue));
     216             :         }
     217             : #endif
     218             : 
     219           1 :         return CHIP_NO_ERROR;
     220             :     }
     221             : 
     222             :     /**
     223             :      * @brief Get the Initial Counter Value
     224             :      *
     225             :      * By default, persisted counters start off at 0.
     226             :      */
     227           1 :     virtual inline T GetInitialCounterValue() { return 0; }
     228             : 
     229             :     PersistentStorageDelegate * mStorage = nullptr; // start value is stored here
     230             :     StorageKeyName mKey;
     231             :     T mEpoch     = 0; // epoch modulus value
     232             :     T mNextEpoch = 0; // next epoch start
     233             : };
     234             : 
     235             : } // namespace chip

Generated by: LCOV version 1.14