summaryrefslogtreecommitdiffstats
path: root/mozglue/baseprofiler/public/FailureLatch.h
blob: 10205e009bd1274dac818d772e467aa53e212c6d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// This header contains an interface `FailureLatch`, and some implementation
// helpers that may be used across a range of classes and functions to handle
// failures at any point during a process, and share that failure state so that
// the process may gracefully stop quickly and report the first error.
//
// It could be thought as a replacement for C++ exceptions, but it's less strong
// (cancellations may be delayed).
// Now, if possible, mozilla::Result may be a better option as C++ exceptions
// replacement, as it is more visible in all affected functions.
// Consider FailureLatch if failures may happen in different places, but where
// `return`ing this potential failure from all functions would be too arduous.

#ifndef mozilla_FailureLatch_h
#define mozilla_FailureLatch_h

#include <mozilla/Assertions.h>

#include <string>

namespace mozilla {

// ----------------------------------------------------------------------------
// Main interface
// ----------------------------------------------------------------------------

// Interface handling a failure latch (starting in a successful state, the first
// failure gets recorded, subsequent failures are ignored.)
class FailureLatch {
 public:
  virtual ~FailureLatch() = default;

  // Can this ever fail? (This may influence how some code deals with
  // failures, e.g., if infallible, OOMs should assert&crash.)
  [[nodiscard]] virtual bool Fallible() const = 0;

  // Set latch in its failed state because of an external cause.
  // The first call sets the reason, subsequent calls are ignored.
  virtual void SetFailure(std::string aReason) = 0;

  // Has there been any failure so far?
  [[nodiscard]] virtual bool Failed() const = 0;

  // Return first failure string, may be null if not failed yet.
  [[nodiscard]] virtual const char* GetFailure() const = 0;

  // Retrieve the one source FailureLatch. It could reference `*this`!
  // This may be used by dependent proxy FailureLatch'es to find where to
  // redirect calls.
  [[nodiscard]] virtual const FailureLatch& SourceFailureLatch() const = 0;
  [[nodiscard]] virtual FailureLatch& SourceFailureLatch() = 0;

  // Non-virtual helpers.

  // Transfer any failure from another FailureLatch.
  void SetFailureFrom(const FailureLatch& aOther) {
    if (Failed()) {
      return;
    }
    if (const char* otherFailure = aOther.GetFailure(); otherFailure) {
      SetFailure(otherFailure);
    }
  }
};

// ----------------------------------------------------------------------------
// Concrete implementations
// ----------------------------------------------------------------------------

// Concrete infallible FailureLatch class.
// Any `SetFailure` leads to an assert-crash, so the final runtime result can
// always be assumed to be succesful.
class FailureLatchInfallibleSource final : public FailureLatch {
 public:
  [[nodiscard]] bool Fallible() const final { return false; }

  void SetFailure(std::string aReason) final {
    MOZ_RELEASE_ASSERT(false,
                       "SetFailure in infallible FailureLatchInfallibleSource");
  }

  [[nodiscard]] bool Failed() const final { return false; }

  [[nodiscard]] const char* GetFailure() const final { return nullptr; }

  [[nodiscard]] const ::mozilla::FailureLatch& SourceFailureLatch()
      const final {
    return *this;
  }

  [[nodiscard]] ::mozilla::FailureLatch& SourceFailureLatch() final {
    return *this;
  }

  // Singleton FailureLatchInfallibleSource that may be used as default
  // FailureLatch proxy.
  static FailureLatchInfallibleSource& Singleton() {
    static FailureLatchInfallibleSource singleton;
    return singleton;
  }
};

// Concrete FailureLatch class, intended to be intantiated as an object shared
// between classes and functions that are part of a long operation, so that
// failures can happen anywhere and be visible everywhere.
// Not thread-safe.
class FailureLatchSource final : public FailureLatch {
 public:
  [[nodiscard]] bool Fallible() const final { return true; }

  void SetFailure(std::string aReason) final {
    if (!mFailed) {
      mFailed = true;
      mReason = std::move(aReason);
    }
  }

  [[nodiscard]] bool Failed() const final { return mFailed; }

  [[nodiscard]] const char* GetFailure() const final {
    return mFailed ? mReason.c_str() : nullptr;
  }

  [[nodiscard]] const FailureLatch& SourceFailureLatch() const final {
    return *this;
  }

  [[nodiscard]] FailureLatch& SourceFailureLatch() final { return *this; }

 private:
  bool mFailed = false;
  std::string mReason;
};

// ----------------------------------------------------------------------------
// Helper macros, to be used in FailureLatch-derived classes
// ----------------------------------------------------------------------------

// Classes deriving from FailureLatch can use this to forward virtual calls to
// another FailureLatch.
#define FAILURELATCH_IMPL_PROXY(FAILURELATCH_REF)                        \
  [[nodiscard]] bool Fallible() const final {                            \
    return static_cast<const ::mozilla::FailureLatch&>(FAILURELATCH_REF) \
        .Fallible();                                                     \
  }                                                                      \
  void SetFailure(std::string aReason) final {                           \
    static_cast<::mozilla::FailureLatch&>(FAILURELATCH_REF)              \
        .SetFailure(std::move(aReason));                                 \
  }                                                                      \
  [[nodiscard]] bool Failed() const final {                              \
    return static_cast<const ::mozilla::FailureLatch&>(FAILURELATCH_REF) \
        .Failed();                                                       \
  }                                                                      \
  [[nodiscard]] const char* GetFailure() const final {                   \
    return static_cast<const ::mozilla::FailureLatch&>(FAILURELATCH_REF) \
        .GetFailure();                                                   \
  }                                                                      \
  [[nodiscard]] const FailureLatch& SourceFailureLatch() const final {   \
    return static_cast<const ::mozilla::FailureLatch&>(FAILURELATCH_REF) \
        .SourceFailureLatch();                                           \
  }                                                                      \
  [[nodiscard]] FailureLatch& SourceFailureLatch() final {               \
    return static_cast<::mozilla::FailureLatch&>(FAILURELATCH_REF)       \
        .SourceFailureLatch();                                           \
  }

// Classes deriving from FailureLatch can use this to forward virtual calls to
// another FailureLatch through a pointer, unless it's null in which case act
// like an infallible FailureLatch.
#define FAILURELATCH_IMPL_PROXY_OR_INFALLIBLE(FAILURELATCH_PTR, CLASS_NAME)    \
  [[nodiscard]] bool Fallible() const final {                                  \
    return FAILURELATCH_PTR                                                    \
               ? static_cast<const ::mozilla::FailureLatch*>(FAILURELATCH_PTR) \
                     ->Fallible()                                              \
               : false;                                                        \
  }                                                                            \
  void SetFailure(std::string aReason) final {                                 \
    if (FAILURELATCH_PTR) {                                                    \
      static_cast<::mozilla::FailureLatch*>(FAILURELATCH_PTR)                  \
          ->SetFailure(std::move(aReason));                                    \
    } else {                                                                   \
      MOZ_RELEASE_ASSERT(false, "SetFailure in infallible " #CLASS_NAME);      \
    }                                                                          \
  }                                                                            \
  [[nodiscard]] bool Failed() const final {                                    \
    return FAILURELATCH_PTR                                                    \
               ? static_cast<const ::mozilla::FailureLatch*>(FAILURELATCH_PTR) \
                     ->Failed()                                                \
               : false;                                                        \
  }                                                                            \
  [[nodiscard]] const char* GetFailure() const final {                         \
    return FAILURELATCH_PTR                                                    \
               ? static_cast<const ::mozilla::FailureLatch*>(FAILURELATCH_PTR) \
                     ->GetFailure()                                            \
               : nullptr;                                                      \
  }                                                                            \
  [[nodiscard]] const FailureLatch& SourceFailureLatch() const final {         \
    return FAILURELATCH_PTR                                                    \
               ? static_cast<const ::mozilla::FailureLatch*>(FAILURELATCH_PTR) \
                     ->SourceFailureLatch()                                    \
               : ::mozilla::FailureLatchInfallibleSource::Singleton();         \
  }                                                                            \
  [[nodiscard]] FailureLatch& SourceFailureLatch() final {                     \
    return FAILURELATCH_PTR                                                    \
               ? static_cast<::mozilla::FailureLatch*>(FAILURELATCH_PTR)       \
                     ->SourceFailureLatch()                                    \
               : ::mozilla::FailureLatchInfallibleSource::Singleton();         \
  }

}  // namespace mozilla

#endif /* mozilla_FailureLatch_h */