1 // Copyright 2019 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of 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,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 #ifndef ABSL_STATUS_STATUS_H_
15 #define ABSL_STATUS_STATUS_H_
16
17 #include <iostream>
18 #include <string>
19
20 #include "absl/container/inlined_vector.h"
21 #include "absl/strings/cord.h"
22 #include "absl/types/optional.h"
23
24 namespace absl {
25 ABSL_NAMESPACE_BEGIN
26
27 enum class StatusCode : int {
28 kOk = 0,
29 kCancelled = 1,
30 kUnknown = 2,
31 kInvalidArgument = 3,
32 kDeadlineExceeded = 4,
33 kNotFound = 5,
34 kAlreadyExists = 6,
35 kPermissionDenied = 7,
36 kResourceExhausted = 8,
37 kFailedPrecondition = 9,
38 kAborted = 10,
39 kOutOfRange = 11,
40 kUnimplemented = 12,
41 kInternal = 13,
42 kUnavailable = 14,
43 kDataLoss = 15,
44 kUnauthenticated = 16,
45 kDoNotUseReservedForFutureExpansionUseDefaultInSwitchInstead_ = 20
46 };
47
48 // Returns the name for the status code, or "" if it is an unknown value.
49 std::string StatusCodeToString(StatusCode code);
50
51 // Streams StatusCodeToString(code) to `os`.
52 std::ostream& operator<<(std::ostream& os, StatusCode code);
53
54 namespace status_internal {
55
56 // Container for status payloads.
57 struct Payload {
58 std::string type_url;
59 absl::Cord payload;
60 };
61
62 using Payloads = absl::InlinedVector<Payload, 1>;
63
64 // Reference-counted representation of Status data.
65 struct StatusRep {
66 std::atomic<int32_t> ref;
67 absl::StatusCode code;
68 std::string message;
69 std::unique_ptr<status_internal::Payloads> payloads;
70 };
71
72 absl::StatusCode MapToLocalCode(int value);
73 } // namespace status_internal
74
75 class ABSL_MUST_USE_RESULT Status final {
76 public:
77 // Creates an OK status with no message or payload.
78 Status();
79
80 // Create a status in the canonical error space with the specified code and
81 // error message. If `code == util::error::OK`, `msg` is ignored and an
82 // object identical to an OK status is constructed.
83 //
84 // `msg` must be in UTF-8. The implementation may complain (e.g.,
85 // by printing a warning) if it is not.
86 Status(absl::StatusCode code, absl::string_view msg);
87
88 Status(const Status&);
89 Status& operator=(const Status& x);
90
91 // Move operations.
92 // The moved-from state is valid but unspecified.
93 Status(Status&&) noexcept;
94 Status& operator=(Status&&);
95
96 ~Status();
97
98 // If `this->ok()`, stores `new_status` into *this. If `!this->ok()`,
99 // preserves the current data. May, in the future, augment the current status
100 // with additional information about `new_status`.
101 //
102 // Convenient way of keeping track of the first error encountered.
103 // Instead of:
104 // if (overall_status.ok()) overall_status = new_status
105 // Use:
106 // overall_status.Update(new_status);
107 //
108 // Style guide exception for rvalue reference granted in CL 153567220.
109 void Update(const Status& new_status);
110 void Update(Status&& new_status);
111
112 // Returns true if the Status is OK.
113 ABSL_MUST_USE_RESULT bool ok() const;
114
115 // Returns the (canonical) error code.
116 absl::StatusCode code() const;
117
118 // Returns the raw (canonical) error code which could be out of the range of
119 // the local `absl::StatusCode` enum. NOTE: This should only be called when
120 // converting to wire format. Use `code` for error handling.
121 int raw_code() const;
122
123 // Returns the error message. Note: prefer ToString() for debug logging.
124 // This message rarely describes the error code. It is not unusual for the
125 // error message to be the empty std::string.
126 absl::string_view message() const;
127
128 friend bool operator==(const Status&, const Status&);
129 friend bool operator!=(const Status&, const Status&);
130
131 // Returns a combination of the error code name, the message and the payloads.
132 // You can expect the code name and the message to be substrings of the
133 // result, and the payloads to be printed by the registered printer extensions
134 // if they are recognized.
135 // WARNING: Do not depend on the exact format of the result of `ToString()`
136 // which is subject to change.
137 std::string ToString() const;
138
139 // Ignores any errors. This method does nothing except potentially suppress
140 // complaints from any tools that are checking that errors are not dropped on
141 // the floor.
142 void IgnoreError() const;
143
144 // Swap the contents of `a` with `b`
145 friend void swap(Status& a, Status& b);
146
147 // Payload management APIs
148
149 // Type URL should be unique and follow the naming convention below:
150 // The idea of type URL comes from `google.protobuf.Any`
151 // (https://developers.google.com/protocol-buffers/docs/proto3#any). The
152 // type URL should be globally unique and follow the format of URL
153 // (https://en.wikipedia.org/wiki/URL). The default type URL for a given
154 // protobuf message type is "type.googleapis.com/packagename.messagename". For
155 // other custom wire formats, users should define the format of type URL in a
156 // similar practice so as to minimize the chance of conflict between type
157 // URLs. Users should make sure that the type URL can be mapped to a concrete
158 // C++ type if they want to deserialize the payload and read it effectively.
159
160 // Gets the payload based for `type_url` key, if it is present.
161 absl::optional<absl::Cord> GetPayload(absl::string_view type_url) const;
162
163 // Sets the payload for `type_url` key for a non-ok status, overwriting any
164 // existing payload for `type_url`.
165 //
166 // NOTE: Does nothing if the Status is ok.
167 void SetPayload(absl::string_view type_url, absl::Cord payload);
168
169 // Erases the payload corresponding to the `type_url` key. Returns true if
170 // the payload was present.
171 bool ErasePayload(absl::string_view type_url);
172
173 // Iterates over the stored payloads and calls `visitor(type_key, payload)`
174 // for each one.
175 //
176 // NOTE: The order of calls to `visitor` is not specified and may change at
177 // any time.
178 //
179 // NOTE: Any mutation on the same 'Status' object during visitation is
180 // forbidden and could result in undefined behavior.
181 void ForEachPayload(
182 const std::function<void(absl::string_view, const absl::Cord&)>& visitor)
183 const;
184
185 private:
186 friend Status CancelledError();
187
188 // Creates a status in the canonical error space with the specified
189 // code, and an empty error message.
190 explicit Status(absl::StatusCode code);
191
192 static void UnrefNonInlined(uintptr_t rep);
193 static void Ref(uintptr_t rep);
194 static void Unref(uintptr_t rep);
195
196 // REQUIRES: !ok()
197 // Ensures rep_ is not shared with any other Status.
198 void PrepareToModify();
199
200 const status_internal::Payloads* GetPayloads() const;
201 status_internal::Payloads* GetPayloads();
202
203 // Takes ownership of payload.
204 static uintptr_t NewRep(absl::StatusCode code, absl::string_view msg,
205 std::unique_ptr<status_internal::Payloads> payload);
206 static bool EqualsSlow(const absl::Status& a, const absl::Status& b);
207
208 // MSVC 14.0 limitation requires the const.
209 static constexpr const char kMovedFromString[] =
210 "Status accessed after move.";
211
212 static const std::string* EmptyString();
213 static const std::string* MovedFromString();
214
215 // Returns whether rep contains an inlined representation.
216 // See rep_ for details.
217 static bool IsInlined(uintptr_t rep);
218
219 // Indicates whether this Status was the rhs of a move operation. See rep_
220 // for details.
221 static bool IsMovedFrom(uintptr_t rep);
222 static uintptr_t MovedFromRep();
223
224 // Convert between error::Code and the inlined uintptr_t representation used
225 // by rep_. See rep_ for details.
226 static uintptr_t CodeToInlinedRep(absl::StatusCode code);
227 static absl::StatusCode InlinedRepToCode(uintptr_t rep);
228
229 // Converts between StatusRep* and the external uintptr_t representation used
230 // by rep_. See rep_ for details.
231 static uintptr_t PointerToRep(status_internal::StatusRep* r);
232 static status_internal::StatusRep* RepToPointer(uintptr_t r);
233
234 // Returns std::string for non-ok Status.
235 std::string ToStringSlow() const;
236
237 // Status supports two different representations.
238 // - When the low bit is off it is an inlined representation.
239 // It uses the canonical error space, no message or payload.
240 // The error code is (rep_ >> 2).
241 // The (rep_ & 2) bit is the "moved from" indicator, used in IsMovedFrom().
242 // - When the low bit is on it is an external representation.
243 // In this case all the data comes from a heap allocated Rep object.
244 // (rep_ - 1) is a status_internal::StatusRep* pointer to that structure.
245 uintptr_t rep_;
246 };
247
248 // Returns an OK status, equivalent to a default constructed instance.
249 Status OkStatus();
250
251 // Prints a human-readable representation of `x` to `os`.
252 std::ostream& operator<<(std::ostream& os, const Status& x);
253
254 // -----------------------------------------------------------------
255 // Implementation details follow
256
Status()257 inline Status::Status() : rep_(CodeToInlinedRep(absl::StatusCode::kOk)) {}
258
Status(absl::StatusCode code)259 inline Status::Status(absl::StatusCode code) : rep_(CodeToInlinedRep(code)) {}
260
Status(const Status & x)261 inline Status::Status(const Status& x) : rep_(x.rep_) { Ref(rep_); }
262
263 inline Status& Status::operator=(const Status& x) {
264 uintptr_t old_rep = rep_;
265 if (x.rep_ != old_rep) {
266 Ref(x.rep_);
267 rep_ = x.rep_;
268 Unref(old_rep);
269 }
270 return *this;
271 }
272
Status(Status && x)273 inline Status::Status(Status&& x) noexcept : rep_(x.rep_) {
274 x.rep_ = MovedFromRep();
275 }
276
277 inline Status& Status::operator=(Status&& x) {
278 uintptr_t old_rep = rep_;
279 rep_ = x.rep_;
280 x.rep_ = MovedFromRep();
281 Unref(old_rep);
282 return *this;
283 }
284
Update(const Status & new_status)285 inline void Status::Update(const Status& new_status) {
286 if (ok()) {
287 *this = new_status;
288 }
289 }
290
Update(Status && new_status)291 inline void Status::Update(Status&& new_status) {
292 if (ok()) {
293 *this = std::move(new_status);
294 }
295 }
296
~Status()297 inline Status::~Status() { Unref(rep_); }
298
ok()299 inline bool Status::ok() const {
300 return rep_ == CodeToInlinedRep(absl::StatusCode::kOk);
301 }
302
message()303 inline absl::string_view Status::message() const {
304 return !IsInlined(rep_)
305 ? RepToPointer(rep_)->message
306 : (IsMovedFrom(rep_) ? absl::string_view(kMovedFromString)
307 : absl::string_view());
308 }
309
310 inline bool operator==(const Status& lhs, const Status& rhs) {
311 return lhs.rep_ == rhs.rep_ || Status::EqualsSlow(lhs, rhs);
312 }
313
314 inline bool operator!=(const Status& lhs, const Status& rhs) {
315 return !(lhs == rhs);
316 }
317
ToString()318 inline std::string Status::ToString() const {
319 return ok() ? "OK" : ToStringSlow();
320 }
321
IgnoreError()322 inline void Status::IgnoreError() const {
323 // no-op
324 }
325
swap(absl::Status & a,absl::Status & b)326 inline void swap(absl::Status& a, absl::Status& b) {
327 using std::swap;
328 swap(a.rep_, b.rep_);
329 }
330
GetPayloads()331 inline const status_internal::Payloads* Status::GetPayloads() const {
332 return IsInlined(rep_) ? nullptr : RepToPointer(rep_)->payloads.get();
333 }
334
GetPayloads()335 inline status_internal::Payloads* Status::GetPayloads() {
336 return IsInlined(rep_) ? nullptr : RepToPointer(rep_)->payloads.get();
337 }
338
IsInlined(uintptr_t rep)339 inline bool Status::IsInlined(uintptr_t rep) { return (rep & 1) == 0; }
340
IsMovedFrom(uintptr_t rep)341 inline bool Status::IsMovedFrom(uintptr_t rep) {
342 return IsInlined(rep) && (rep & 2) != 0;
343 }
344
MovedFromRep()345 inline uintptr_t Status::MovedFromRep() {
346 return CodeToInlinedRep(absl::StatusCode::kInternal) | 2;
347 }
348
CodeToInlinedRep(absl::StatusCode code)349 inline uintptr_t Status::CodeToInlinedRep(absl::StatusCode code) {
350 return static_cast<uintptr_t>(code) << 2;
351 }
352
InlinedRepToCode(uintptr_t rep)353 inline absl::StatusCode Status::InlinedRepToCode(uintptr_t rep) {
354 assert(IsInlined(rep));
355 return static_cast<absl::StatusCode>(rep >> 2);
356 }
357
RepToPointer(uintptr_t rep)358 inline status_internal::StatusRep* Status::RepToPointer(uintptr_t rep) {
359 assert(!IsInlined(rep));
360 return reinterpret_cast<status_internal::StatusRep*>(rep - 1);
361 }
362
PointerToRep(status_internal::StatusRep * rep)363 inline uintptr_t Status::PointerToRep(status_internal::StatusRep* rep) {
364 return reinterpret_cast<uintptr_t>(rep) + 1;
365 }
366
Ref(uintptr_t rep)367 inline void Status::Ref(uintptr_t rep) {
368 if (!IsInlined(rep)) {
369 RepToPointer(rep)->ref.fetch_add(1, std::memory_order_relaxed);
370 }
371 }
372
Unref(uintptr_t rep)373 inline void Status::Unref(uintptr_t rep) {
374 if (!IsInlined(rep)) {
375 UnrefNonInlined(rep);
376 }
377 }
378
OkStatus()379 inline Status OkStatus() { return Status(); }
380
381 // Each of the functions below creates a Status object with a particular error
382 // code and the given message. The error code of the returned status object
383 // matches the name of the function.
384 Status AbortedError(absl::string_view message);
385 Status AlreadyExistsError(absl::string_view message);
386 Status CancelledError(absl::string_view message);
387 Status DataLossError(absl::string_view message);
388 Status DeadlineExceededError(absl::string_view message);
389 Status FailedPreconditionError(absl::string_view message);
390 Status InternalError(absl::string_view message);
391 Status InvalidArgumentError(absl::string_view message);
392 Status NotFoundError(absl::string_view message);
393 Status OutOfRangeError(absl::string_view message);
394 Status PermissionDeniedError(absl::string_view message);
395 Status ResourceExhaustedError(absl::string_view message);
396 Status UnauthenticatedError(absl::string_view message);
397 Status UnavailableError(absl::string_view message);
398 Status UnimplementedError(absl::string_view message);
399 Status UnknownError(absl::string_view message);
400
401 // Creates a `Status` object with the `absl::StatusCode::kCancelled` error code
402 // and an empty message. It is provided only for efficiency, given that
403 // message-less kCancelled errors are common in the infrastructure.
CancelledError()404 inline Status CancelledError() { return Status(absl::StatusCode::kCancelled); }
405
406 // Each of the functions below returns true if the given status matches the
407 // error code implied by the function's name.
408 ABSL_MUST_USE_RESULT bool IsAborted(const Status& status);
409 ABSL_MUST_USE_RESULT bool IsAlreadyExists(const Status& status);
410 ABSL_MUST_USE_RESULT bool IsCancelled(const Status& status);
411 ABSL_MUST_USE_RESULT bool IsDataLoss(const Status& status);
412 ABSL_MUST_USE_RESULT bool IsDeadlineExceeded(const Status& status);
413 ABSL_MUST_USE_RESULT bool IsFailedPrecondition(const Status& status);
414 ABSL_MUST_USE_RESULT bool IsInternal(const Status& status);
415 ABSL_MUST_USE_RESULT bool IsInvalidArgument(const Status& status);
416 ABSL_MUST_USE_RESULT bool IsNotFound(const Status& status);
417 ABSL_MUST_USE_RESULT bool IsOutOfRange(const Status& status);
418 ABSL_MUST_USE_RESULT bool IsPermissionDenied(const Status& status);
419 ABSL_MUST_USE_RESULT bool IsResourceExhausted(const Status& status);
420 ABSL_MUST_USE_RESULT bool IsUnauthenticated(const Status& status);
421 ABSL_MUST_USE_RESULT bool IsUnavailable(const Status& status);
422 ABSL_MUST_USE_RESULT bool IsUnimplemented(const Status& status);
423 ABSL_MUST_USE_RESULT bool IsUnknown(const Status& status);
424
425 ABSL_NAMESPACE_END
426 } // namespace absl
427
428 #endif // ABSL_STATUS_STATUS_H_
429