1 // Copyright 2021 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 #pragma once 15 16 #include <cstdint> 17 #include <cstring> 18 #include <type_traits> 19 #include <utility> 20 21 #include "pw_assert/assert.h" 22 #include "pw_checksum/crc16_ccitt.h" 23 #include "pw_preprocessor/compiler.h" 24 #include "pw_span/span.h" 25 26 namespace pw::persistent_ram { 27 28 // Behavior to use when attempting to get a handle to the underlying data stored 29 // in persistent memory. 30 enum class GetterAction { 31 // Default-construct the object before returning a handle. 32 kReset, 33 // Assert that the object is valid before returning a handle. 34 kAssertValid, 35 }; 36 37 // The Persistent class intentionally uses uninitialized memory, which triggers 38 // compiler warnings. Disable those warnings for this file. 39 PW_MODIFY_DIAGNOSTICS_PUSH(); 40 PW_MODIFY_DIAGNOSTIC(ignored, "-Wuninitialized"); 41 PW_MODIFY_DIAGNOSTIC_GCC(ignored, "-Wmaybe-uninitialized"); 42 43 // A simple container for holding a value T with CRC16 integrity checking. 44 // 45 // A Persistent is simply a value T plus integrity checking for use in a 46 // persistent RAM section which is not initialized on boot. 47 // 48 // WARNING: Unlike a DoubleBufferedPersistent, a Persistent will be lost if a 49 // write/set operation is interrupted or otherwise not completed. 50 // 51 // TODO: b/235277454 - Consider a different integrity check implementation which 52 // does not use a 512B lookup table. 53 template <typename T> 54 class Persistent { 55 public: 56 // This object provides mutable access to the underlying object of a 57 // Persistent<T>. 58 // 59 // WARNING: This object must remain in scope for any modifications of the 60 // Underlying object. If the object is modified after the Mutator goes out 61 // of scope, the CRC will not be updated to reflect changes, invalidating the 62 // contents of the Persistent<T>! 63 // 64 // WARNING: Persistent<T>::has_value() will return false if there are 65 // in-flight modifications by a Mutator that have not yet been flushed. 66 class Mutator { 67 public: Mutator(Persistent<T> & persistent)68 explicit constexpr Mutator(Persistent<T>& persistent) 69 : persistent_(persistent) {} ~Mutator()70 ~Mutator() { persistent_.crc_ = persistent_.CalculateCrc(); } 71 72 Mutator(const Mutator&) = delete; // Copy constructor is disabled. 73 74 T* operator->() { return const_cast<T*>(&persistent_.contents_); } 75 76 // Be careful when sharing a reference or pointer to the underlying object. 77 // Once the Mutator goes out of scope, any changes to the object will 78 // invalidate the checksum. Avoid directly using the underlying object 79 // unless you need to pass it to a function. value()80 T& value() { return persistent_.contents_; } 81 T& operator*() { return *const_cast<T*>(&persistent_.contents_); } 82 83 private: 84 Persistent<T>& persistent_; 85 }; 86 87 // Constructor which does nothing, meaning it never sets the value. Persistent()88 constexpr Persistent() {} 89 90 Persistent(const Persistent&) = delete; // Copy constructor is disabled. 91 Persistent(Persistent&&) = delete; // Move constructor is disabled. ~Persistent()92 ~Persistent() {} // The destructor does nothing. 93 94 // Construct the value in-place. 95 template <class... Args> emplace(Args &&...args)96 const T& emplace(Args&&... args) { 97 new (const_cast<T*>(&contents_)) T(std::forward<Args>(args)...); 98 crc_ = CalculateCrc(); 99 return const_cast<T&>(contents_); 100 } 101 102 // Assignment operator. 103 template <typename U = T> 104 Persistent& operator=(U&& value) { 105 contents_ = std::forward<U>(value); 106 crc_ = CalculateCrc(); 107 return *this; 108 } 109 110 // Destroys any contained value. Invalidate()111 void Invalidate() { 112 // The trivial destructor is skipped as it's trivial. 113 std::memset(const_cast<T*>(&contents_), 0, sizeof(contents_)); 114 crc_ = 0; 115 } 116 117 // This is deprecated, use Invalidate() instead. reset()118 [[deprecated]] void reset() { Invalidate(); } 119 120 // Returns true if a value is held by the Persistent. has_value()121 bool has_value() const { 122 return crc_ == CalculateCrc(); // There's a value if its CRC matches. 123 } 124 125 // Access the value. 126 // 127 // Precondition: has_value() must be true. value()128 const T& value() const { 129 PW_ASSERT(has_value()); 130 return const_cast<T&>(contents_); 131 } 132 133 // Get a mutable handle to the underlying data. 134 // 135 // Args: 136 // action: Whether to default-construct the underlying value before 137 // providing a mutator, or to assert that the object is valid 138 // without modifying the underlying data. 139 // Precondition: has_value() must be true. 140 Mutator mutator(GetterAction action = GetterAction::kAssertValid) { 141 if (action == GetterAction::kReset) { 142 emplace(); 143 } else { 144 PW_ASSERT(has_value()); 145 } 146 return Mutator(*this); 147 } 148 149 private: 150 friend class Mutator; 151 152 static_assert(std::is_trivially_copy_constructible<T>::value, 153 "If a Persistent persists across reboots, it is effectively " 154 "loaded through a trivial copy constructor."); 155 156 static_assert(std::is_trivially_destructible<T>::value, 157 "A Persistent's destructor does not invoke the value's " 158 "destructor, ergo only trivially destructible types are " 159 "supported."); 160 CalculateCrc()161 uint16_t CalculateCrc() const { 162 return checksum::Crc16Ccitt::Calculate( 163 as_bytes(span(const_cast<const T*>(&contents_), 1))); 164 } 165 166 // Use unions to denote that these members are never initialized by design and 167 // on purpose. Volatile is used to ensure that the compiler cannot optimize 168 // out operations where it seems like there is no further usage of a 169 // Persistent as this may be on the next boot. 170 union { 171 volatile T contents_; 172 }; 173 union { 174 volatile uint16_t crc_; 175 }; 176 }; 177 178 PW_MODIFY_DIAGNOSTICS_POP(); 179 180 } // namespace pw::persistent_ram 181