-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[app] Implement deferred attribute persister (#23366)
* [app] Implement deferred attribute persister Fast-changing attributes with NVM storage, such as CurrentLevel of the LevelControl cluster, may result in rapid flash wearout using the default attribute persister which stores all values immediately. Implement a helper adapter class for the attribute persistence interface to defer writes of selected attributes. Use the new class in nRF Connect lighting-app for verification. * Code review * Add a comment
- Loading branch information
1 parent
2212809
commit 1300859
Showing
9 changed files
with
200 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
/* | ||
* Copyright (c) 2022 Project CHIP Authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
#include <app/DeferredAttributePersistenceProvider.h> | ||
|
||
#include <platform/CHIPDeviceLayer.h> | ||
|
||
namespace chip { | ||
namespace app { | ||
|
||
CHIP_ERROR DeferredAttribute::PrepareWrite(System::Clock::Timestamp flushTime, const ByteSpan & value) | ||
{ | ||
mFlushTime = flushTime; | ||
|
||
if (mValue.AllocatedSize() != value.size()) | ||
{ | ||
mValue.Alloc(value.size()); | ||
ReturnErrorCodeIf(!mValue, CHIP_ERROR_NO_MEMORY); | ||
} | ||
|
||
memcpy(mValue.Get(), value.data(), value.size()); | ||
return CHIP_NO_ERROR; | ||
} | ||
|
||
void DeferredAttribute::Flush(AttributePersistenceProvider & persister) | ||
{ | ||
VerifyOrReturn(IsArmed()); | ||
persister.WriteValue(mPath, ByteSpan(mValue.Get(), mValue.AllocatedSize())); | ||
mValue.Release(); | ||
} | ||
|
||
CHIP_ERROR DeferredAttributePersistenceProvider::WriteValue(const ConcreteAttributePath & path, const ByteSpan & value) | ||
{ | ||
for (DeferredAttribute & da : mDeferredAttributes) | ||
{ | ||
if (da.Matches(path)) | ||
{ | ||
ReturnErrorOnFailure(da.PrepareWrite(System::SystemClock().GetMonotonicTimestamp() + mWriteDelay, value)); | ||
FlushAndScheduleNext(); | ||
return CHIP_NO_ERROR; | ||
} | ||
} | ||
|
||
return mPersister.WriteValue(path, value); | ||
} | ||
|
||
CHIP_ERROR DeferredAttributePersistenceProvider::ReadValue(const ConcreteAttributePath & path, | ||
const EmberAfAttributeMetadata * metadata, MutableByteSpan & value) | ||
{ | ||
return mPersister.ReadValue(path, metadata, value); | ||
} | ||
|
||
void DeferredAttributePersistenceProvider::FlushAndScheduleNext() | ||
{ | ||
const System::Clock::Timestamp now = System::SystemClock().GetMonotonicTimestamp(); | ||
System::Clock::Timestamp nextFlushTime = System::Clock::Timestamp::max(); | ||
|
||
for (DeferredAttribute & da : mDeferredAttributes) | ||
{ | ||
if (!da.IsArmed()) | ||
{ | ||
continue; | ||
} | ||
|
||
if (da.GetFlushTime() <= now) | ||
{ | ||
da.Flush(mPersister); | ||
} | ||
else | ||
{ | ||
nextFlushTime = chip::min(nextFlushTime, da.GetFlushTime()); | ||
} | ||
} | ||
|
||
if (nextFlushTime != System::Clock::Timestamp::max()) | ||
{ | ||
DeviceLayer::SystemLayer().StartTimer( | ||
nextFlushTime - now, | ||
[](System::Layer *, void * me) { static_cast<DeferredAttributePersistenceProvider *>(me)->FlushAndScheduleNext(); }, | ||
this); | ||
} | ||
} | ||
|
||
} // namespace app | ||
} // namespace chip |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
/* | ||
* Copyright (c) 2022 Project CHIP Authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
#pragma once | ||
|
||
#include <app/AttributePersistenceProvider.h> | ||
#include <lib/support/ScopedBuffer.h> | ||
#include <lib/support/Span.h> | ||
|
||
namespace chip { | ||
namespace app { | ||
|
||
class DeferredAttribute | ||
{ | ||
public: | ||
explicit DeferredAttribute(const ConcreteAttributePath & path) : mPath(path) {} | ||
|
||
bool Matches(const ConcreteAttributePath & path) const { return mPath == path; } | ||
bool IsArmed() const { return static_cast<bool>(mValue); } | ||
System::Clock::Timestamp GetFlushTime() const { return mFlushTime; } | ||
|
||
CHIP_ERROR PrepareWrite(System::Clock::Timestamp flushTime, const ByteSpan & value); | ||
void Flush(AttributePersistenceProvider & persister); | ||
|
||
private: | ||
const ConcreteAttributePath mPath; | ||
System::Clock::Timestamp mFlushTime; | ||
Platform::ScopedMemoryBufferWithSize<uint8_t> mValue; | ||
}; | ||
|
||
/** | ||
* Decorator class for the AttributePersistenceProvider implementation that | ||
* defers writes of selected attributes. | ||
* | ||
* This class is useful to increase the flash lifetime by reducing the number | ||
* of writes of fast-changing attributes, such as CurrentLevel attribute of the | ||
* LevelControl cluster. | ||
*/ | ||
class DeferredAttributePersistenceProvider : public AttributePersistenceProvider | ||
{ | ||
public: | ||
DeferredAttributePersistenceProvider(AttributePersistenceProvider & persister, | ||
const Span<DeferredAttribute> & deferredAttributes, | ||
System::Clock::Milliseconds32 writeDelay) : | ||
mPersister(persister), | ||
mDeferredAttributes(deferredAttributes), mWriteDelay(writeDelay) | ||
{} | ||
|
||
/* | ||
* If the written attribute is one of the deferred attributes specified in the constructor, | ||
* postpone the write operation by the configured delay. If this attribute changes within the | ||
* delay period, further postpone the operation so that the actual write happens once the | ||
* attribute has remained constant for the write delay period. | ||
* | ||
* For other attributes, immediately pass the write operation to the decorated persister. | ||
*/ | ||
CHIP_ERROR WriteValue(const ConcreteAttributePath & path, const ByteSpan & value) override; | ||
CHIP_ERROR ReadValue(const ConcreteAttributePath & path, const EmberAfAttributeMetadata * metadata, | ||
MutableByteSpan & value) override; | ||
|
||
private: | ||
void FlushAndScheduleNext(); | ||
|
||
AttributePersistenceProvider & mPersister; | ||
const Span<DeferredAttribute> mDeferredAttributes; | ||
const System::Clock::Milliseconds32 mWriteDelay; | ||
}; | ||
|
||
} // namespace app | ||
} // namespace chip |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters