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/. */
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.
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.
19 #ifndef mozilla_FailureLatch_h
20 #define mozilla_FailureLatch_h
22 #include <mozilla/Assertions.h>
28 // ----------------------------------------------------------------------------
30 // ----------------------------------------------------------------------------
32 // Interface handling a failure latch (starting in a successful state, the first
33 // failure gets recorded, subsequent failures are ignored.)
36 virtual ~FailureLatch() = default;
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;
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;
46 // Has there been any failure so far?
47 [[nodiscard
]] virtual bool Failed() const = 0;
49 // Return first failure string, may be null if not failed yet.
50 [[nodiscard
]] virtual const char* GetFailure() const = 0;
52 // Retrieve the one source FailureLatch. It could reference `*this`!
53 // This may be used by dependent proxy FailureLatch'es to find where to
55 [[nodiscard
]] virtual const FailureLatch
& SourceFailureLatch() const = 0;
56 [[nodiscard
]] virtual FailureLatch
& SourceFailureLatch() = 0;
58 // Non-virtual helpers.
60 // Transfer any failure from another FailureLatch.
61 void SetFailureFrom(const FailureLatch
& aOther
) {
65 if (const char* otherFailure
= aOther
.GetFailure(); otherFailure
) {
66 SetFailure(otherFailure
);
71 // ----------------------------------------------------------------------------
72 // Concrete implementations
73 // ----------------------------------------------------------------------------
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
{
80 [[nodiscard
]] bool Fallible() const final
{ return false; }
82 void SetFailure(std::string aReason
) final
{
83 MOZ_RELEASE_ASSERT(false,
84 "SetFailure in infallible FailureLatchInfallibleSource");
87 [[nodiscard
]] bool Failed() const final
{ return false; }
89 [[nodiscard
]] const char* GetFailure() const final
{ return nullptr; }
91 [[nodiscard
]] const ::mozilla::FailureLatch
& SourceFailureLatch()
96 [[nodiscard
]] ::mozilla::FailureLatch
& SourceFailureLatch() final
{
100 // Singleton FailureLatchInfallibleSource that may be used as default
101 // FailureLatch proxy.
102 static FailureLatchInfallibleSource
& Singleton() {
103 static FailureLatchInfallibleSource singleton
;
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.
112 class FailureLatchSource final
: public FailureLatch
{
114 [[nodiscard
]] bool Fallible() const final
{ return true; }
116 void SetFailure(std::string aReason
) final
{
119 mReason
= std::move(aReason
);
123 [[nodiscard
]] bool Failed() const final
{ return mFailed
; }
125 [[nodiscard
]] const char* GetFailure() const final
{
126 return mFailed
? mReason
.c_str() : nullptr;
129 [[nodiscard
]] const FailureLatch
& SourceFailureLatch() const final
{
133 [[nodiscard
]] FailureLatch
& SourceFailureLatch() final
{ return *this; }
136 bool mFailed
= false;
140 // ----------------------------------------------------------------------------
141 // Helper macros, to be used in FailureLatch-derived classes
142 // ----------------------------------------------------------------------------
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) \
151 void SetFailure(std::string aReason) final { \
152 static_cast<::mozilla::FailureLatch&>(FAILURELATCH_REF) \
153 .SetFailure(std::move(aReason)); \
155 [[nodiscard]] bool Failed() const final { \
156 return static_cast<const ::mozilla::FailureLatch&>(FAILURELATCH_REF) \
159 [[nodiscard]] const char* GetFailure() const final { \
160 return static_cast<const ::mozilla::FailureLatch&>(FAILURELATCH_REF) \
163 [[nodiscard]] const FailureLatch& SourceFailureLatch() const final { \
164 return static_cast<const ::mozilla::FailureLatch&>(FAILURELATCH_REF) \
165 .SourceFailureLatch(); \
167 [[nodiscard]] FailureLatch& SourceFailureLatch() final { \
168 return static_cast<::mozilla::FailureLatch&>(FAILURELATCH_REF) \
169 .SourceFailureLatch(); \
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) \
182 void SetFailure(std::string aReason) final { \
183 if (FAILURELATCH_PTR) { \
184 static_cast<::mozilla::FailureLatch*>(FAILURELATCH_PTR) \
185 ->SetFailure(std::move(aReason)); \
187 MOZ_RELEASE_ASSERT(false, "SetFailure in infallible " #CLASS_NAME); \
190 [[nodiscard]] bool Failed() const final { \
191 return FAILURELATCH_PTR \
192 ? static_cast<const ::mozilla::FailureLatch*>(FAILURELATCH_PTR) \
196 [[nodiscard]] const char* GetFailure() const final { \
197 return FAILURELATCH_PTR \
198 ? static_cast<const ::mozilla::FailureLatch*>(FAILURELATCH_PTR) \
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(); \
208 [[nodiscard]] FailureLatch& SourceFailureLatch() final { \
209 return FAILURELATCH_PTR \
210 ? static_cast<::mozilla::FailureLatch*>(FAILURELATCH_PTR) \
211 ->SourceFailureLatch() \
212 : ::mozilla::FailureLatchInfallibleSource::Singleton(); \
215 } // namespace mozilla
217 #endif /* mozilla_FailureLatch_h */