• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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