FailureLatch.h (10345B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 // This header contains an interface `FailureLatch`, and some implementation 8 // helpers that may be used across a range of classes and functions to handle 9 // failures at any point during a process, and share that failure state so that 10 // the process may gracefully stop quickly and report the first error. 11 // 12 // It could be thought as a replacement for C++ exceptions, but it's less strong 13 // (cancellations may be delayed). 14 // Now, if possible, mozilla::Result may be a better option as C++ exceptions 15 // replacement, as it is more visible in all affected functions. 16 // Consider FailureLatch if failures may happen in different places, but where 17 // `return`ing this potential failure from all functions would be too arduous. 18 19 #ifndef mozilla_FailureLatch_h 20 #define mozilla_FailureLatch_h 21 22 #include <mozilla/Assertions.h> 23 24 #include <string> 25 26 namespace mozilla { 27 28 // ---------------------------------------------------------------------------- 29 // Main interface 30 // ---------------------------------------------------------------------------- 31 32 // Interface handling a failure latch (starting in a successful state, the first 33 // failure gets recorded, subsequent failures are ignored.) 34 class FailureLatch { 35 public: 36 virtual ~FailureLatch() = default; 37 38 // Can this ever fail? (This may influence how some code deals with 39 // failures, e.g., if infallible, OOMs should assert&crash.) 40 [[nodiscard]] virtual bool Fallible() const = 0; 41 42 // Set latch in its failed state because of an external cause. 43 // The first call sets the reason, subsequent calls are ignored. 44 virtual void SetFailure(std::string aReason) = 0; 45 46 // Has there been any failure so far? 47 [[nodiscard]] virtual bool Failed() const = 0; 48 49 // Return first failure string, may be null if not failed yet. 50 [[nodiscard]] virtual const char* GetFailure() const = 0; 51 52 // Retrieve the one source FailureLatch. It could reference `*this`! 53 // This may be used by dependent proxy FailureLatch'es to find where to 54 // redirect calls. 55 [[nodiscard]] virtual const FailureLatch& SourceFailureLatch() const = 0; 56 [[nodiscard]] virtual FailureLatch& SourceFailureLatch() = 0; 57 58 // Non-virtual helpers. 59 60 // Transfer any failure from another FailureLatch. 61 void SetFailureFrom(const FailureLatch& aOther) { 62 if (Failed()) { 63 return; 64 } 65 if (const char* otherFailure = aOther.GetFailure(); otherFailure) { 66 SetFailure(otherFailure); 67 } 68 } 69 }; 70 71 // ---------------------------------------------------------------------------- 72 // Concrete implementations 73 // ---------------------------------------------------------------------------- 74 75 // Concrete infallible FailureLatch class. 76 // Any `SetFailure` leads to an assert-crash, so the final runtime result can 77 // always be assumed to be succesful. 78 class FailureLatchInfallibleSource final : public FailureLatch { 79 public: 80 [[nodiscard]] bool Fallible() const final { return false; } 81 82 void SetFailure(std::string aReason) final { 83 MOZ_RELEASE_ASSERT(false, 84 "SetFailure in infallible FailureLatchInfallibleSource"); 85 } 86 87 [[nodiscard]] bool Failed() const final { return false; } 88 89 [[nodiscard]] const char* GetFailure() const final { return nullptr; } 90 91 [[nodiscard]] const ::mozilla::FailureLatch& SourceFailureLatch() 92 const final { 93 return *this; 94 } 95 96 [[nodiscard]] ::mozilla::FailureLatch& SourceFailureLatch() final { 97 return *this; 98 } 99 100 // Singleton FailureLatchInfallibleSource that may be used as default 101 // FailureLatch proxy. 102 static FailureLatchInfallibleSource& Singleton() { 103 static FailureLatchInfallibleSource singleton; 104 return singleton; 105 } 106 }; 107 108 // Concrete FailureLatch class, intended to be intantiated as an object shared 109 // between classes and functions that are part of a long operation, so that 110 // failures can happen anywhere and be visible everywhere. 111 // Not thread-safe. 112 class FailureLatchSource final : public FailureLatch { 113 public: 114 [[nodiscard]] bool Fallible() const final { return true; } 115 116 void SetFailure(std::string aReason) final { 117 if (!mFailed) { 118 mFailed = true; 119 mReason = std::move(aReason); 120 } 121 } 122 123 [[nodiscard]] bool Failed() const final { return mFailed; } 124 125 [[nodiscard]] const char* GetFailure() const final { 126 return mFailed ? mReason.c_str() : nullptr; 127 } 128 129 [[nodiscard]] const FailureLatch& SourceFailureLatch() const final { 130 return *this; 131 } 132 133 [[nodiscard]] FailureLatch& SourceFailureLatch() final { return *this; } 134 135 private: 136 bool mFailed = false; 137 std::string mReason; 138 }; 139 140 // ---------------------------------------------------------------------------- 141 // Helper macros, to be used in FailureLatch-derived classes 142 // ---------------------------------------------------------------------------- 143 144 // Classes deriving from FailureLatch can use this to forward virtual calls to 145 // another FailureLatch. 146 #define FAILURELATCH_IMPL_PROXY(FAILURELATCH_REF) \ 147 [[nodiscard]] bool Fallible() const final { \ 148 return static_cast<const ::mozilla::FailureLatch&>(FAILURELATCH_REF) \ 149 .Fallible(); \ 150 } \ 151 void SetFailure(std::string aReason) final { \ 152 static_cast<::mozilla::FailureLatch&>(FAILURELATCH_REF) \ 153 .SetFailure(std::move(aReason)); \ 154 } \ 155 [[nodiscard]] bool Failed() const final { \ 156 return static_cast<const ::mozilla::FailureLatch&>(FAILURELATCH_REF) \ 157 .Failed(); \ 158 } \ 159 [[nodiscard]] const char* GetFailure() const final { \ 160 return static_cast<const ::mozilla::FailureLatch&>(FAILURELATCH_REF) \ 161 .GetFailure(); \ 162 } \ 163 [[nodiscard]] const FailureLatch& SourceFailureLatch() const final { \ 164 return static_cast<const ::mozilla::FailureLatch&>(FAILURELATCH_REF) \ 165 .SourceFailureLatch(); \ 166 } \ 167 [[nodiscard]] FailureLatch& SourceFailureLatch() final { \ 168 return static_cast<::mozilla::FailureLatch&>(FAILURELATCH_REF) \ 169 .SourceFailureLatch(); \ 170 } 171 172 // Classes deriving from FailureLatch can use this to forward virtual calls to 173 // another FailureLatch through a pointer, unless it's null in which case act 174 // like an infallible FailureLatch. 175 #define FAILURELATCH_IMPL_PROXY_OR_INFALLIBLE(FAILURELATCH_PTR, CLASS_NAME) \ 176 [[nodiscard]] bool Fallible() const final { \ 177 return FAILURELATCH_PTR \ 178 ? static_cast<const ::mozilla::FailureLatch*>(FAILURELATCH_PTR) \ 179 ->Fallible() \ 180 : false; \ 181 } \ 182 void SetFailure(std::string aReason) final { \ 183 if (FAILURELATCH_PTR) { \ 184 static_cast<::mozilla::FailureLatch*>(FAILURELATCH_PTR) \ 185 ->SetFailure(std::move(aReason)); \ 186 } else { \ 187 MOZ_RELEASE_ASSERT(false, "SetFailure in infallible " #CLASS_NAME); \ 188 } \ 189 } \ 190 [[nodiscard]] bool Failed() const final { \ 191 return FAILURELATCH_PTR \ 192 ? static_cast<const ::mozilla::FailureLatch*>(FAILURELATCH_PTR) \ 193 ->Failed() \ 194 : false; \ 195 } \ 196 [[nodiscard]] const char* GetFailure() const final { \ 197 return FAILURELATCH_PTR \ 198 ? static_cast<const ::mozilla::FailureLatch*>(FAILURELATCH_PTR) \ 199 ->GetFailure() \ 200 : nullptr; \ 201 } \ 202 [[nodiscard]] const FailureLatch& SourceFailureLatch() const final { \ 203 return FAILURELATCH_PTR \ 204 ? static_cast<const ::mozilla::FailureLatch*>(FAILURELATCH_PTR) \ 205 ->SourceFailureLatch() \ 206 : ::mozilla::FailureLatchInfallibleSource::Singleton(); \ 207 } \ 208 [[nodiscard]] FailureLatch& SourceFailureLatch() final { \ 209 return FAILURELATCH_PTR \ 210 ? static_cast<::mozilla::FailureLatch*>(FAILURELATCH_PTR) \ 211 ->SourceFailureLatch() \ 212 : ::mozilla::FailureLatchInfallibleSource::Singleton(); \ 213 } 214 215 } // namespace mozilla 216 217 #endif /* mozilla_FailureLatch_h */