// Status supports two different representations. // - When the low bit is set it is an inlined representation. // It uses the canonical error space, no message or payload. // The error code is (rep_ >> 2). // The (rep_ & 2) bit is the "moved from" indicator, used in IsMovedFrom(). // - When the low bit is off it is an external representation. // In this case all the data comes from a heap allocated Rep object. // rep_ is a status_internal::StatusRep* pointer to that structure. uintptr_t rep_;
// As an internal implementation detail, we guarantee that if status.message() // is non-empty, then the resulting string_view is null terminated. // This is required to implement 'StatusMessageAsCStr(...)' std::string message_; std::unique_ptr<status_internal::Payloads> payloads_; };
当 Status 需要表示 non-trivial case 时就需要动态创建 StatusRep 对象,这个对象保存:
// Convert canonical code to a value known to this binary. absl::StatusCode MapToLocalCode(int value){ absl::StatusCode code = static_cast<absl::StatusCode>(value); switch (code) { case absl::StatusCode::kOk: case absl::StatusCode::kCancelled: case absl::StatusCode::kUnknown: case absl::StatusCode::kInvalidArgument: case absl::StatusCode::kDeadlineExceeded: case absl::StatusCode::kNotFound: case absl::StatusCode::kAlreadyExists: case absl::StatusCode::kPermissionDenied: case absl::StatusCode::kResourceExhausted: case absl::StatusCode::kFailedPrecondition: case absl::StatusCode::kAborted: case absl::StatusCode::kOutOfRange: case absl::StatusCode::kUnimplemented: case absl::StatusCode::kInternal: case absl::StatusCode::kUnavailable: case absl::StatusCode::kDataLoss: case absl::StatusCode::kUnauthenticated: return code; default: return absl::StatusCode::kUnknown; } }
获取 raw code 时需要考虑当前内部的 rep,分别获取
最后返回前还需要做一个是否当前实现可识别的 status code 检查;不认识的一律当作 unknown 返回
这样一套下来确实不太适合作为 ok() 这样的高频调用的实现
4. 获取 message
前面提到了 non-ok status 要存储 error message 的话就要额外在堆上创建一个 StatusRep 对象来存放 error message,所以这部分读取类似前面的 raw code,要从 StatusRep 上取
// Ref and unref are const to allow access through a const pointer, and are // used during copying operations. voidStatusRep::Ref()const{ ref_.fetch_add(1, std::memory_order_relaxed); }
voidStatusRep::Unref()const{ // Fast path: if ref==1, there is no need for a RefCountDec (since // this is the only reference and therefore no other thread is // allowed to be mucking with r). if (ref_.load(std::memory_order_acquire) == 1 || ref_.fetch_sub(1, std::memory_order_acq_rel) - 1 == 0) { deletethis; } }
Status 的 Ref()/Unref() 是 static 函数
StatusRep::Unref() 的 fast path 优化的原因是:
虽然引用计数是 thread-safe 的,但是 Status 对象本身不是 thread-safe
所以在遵循 thread-safe access 前提下,如果当前 Status 的 rep reference count 是1,则说明当前的 Status 是唯一一个 refer 这个 StatusRep 的对象,因为多个线程同时访问同一个 Status 本身就是 non-thread-safe 的
std::string StatusCodeToString(StatusCode code){ switch (code) { case StatusCode::kOk: return"OK"; case StatusCode::kCancelled: return"CANCELLED"; case StatusCode::kUnknown: return"UNKNOWN"; case StatusCode::kInvalidArgument: return"INVALID_ARGUMENT"; case StatusCode::kDeadlineExceeded: return"DEADLINE_EXCEEDED"; case StatusCode::kNotFound: return"NOT_FOUND"; case StatusCode::kAlreadyExists: return"ALREADY_EXISTS"; case StatusCode::kPermissionDenied: return"PERMISSION_DENIED"; case StatusCode::kUnauthenticated: return"UNAUTHENTICATED"; case StatusCode::kResourceExhausted: return"RESOURCE_EXHAUSTED"; case StatusCode::kFailedPrecondition: return"FAILED_PRECONDITION"; case StatusCode::kAborted: return"ABORTED"; case StatusCode::kOutOfRange: return"OUT_OF_RANGE"; case StatusCode::kUnimplemented: return"UNIMPLEMENTED"; case StatusCode::kInternal: return"INTERNAL"; case StatusCode::kUnavailable: return"UNAVAILABLE"; case StatusCode::kDataLoss: return"DATA_LOSS"; default: return""; } }
absl::Nonnull<StatusRep*> StatusRep::CloneAndUnref()const{ // Optimization: no need to create a clone if we already have a refcount of 1. if (ref_.load(std::memory_order_acquire) == 1) { // All StatusRep instances are heap allocated and mutable, therefore this // const_cast will never cast away const from a stack instance. // // CloneAndUnref is the only method that doesn't involve an external cast to // get a mutable StatusRep* from the uintptr_t rep stored in Status. returnconst_cast<StatusRep*>(this); } std::unique_ptr<status_internal::Payloads> payloads; if (payloads_) { payloads = absl::make_unique<status_internal::Payloads>(*payloads_); } auto* new_rep = newStatusRep(code_, message_, std::move(payloads)); Unref(); return new_rep; }
inlineboolStatus::ErasePayload(absl::string_view type_url){ if (IsInlined(rep_)) returnfalse; status_internal::StatusRep* rep = PrepareToModify(rep_); auto res = rep->ErasePayload(type_url); rep_ = res.new_rep; return res.erased; }
// =>
StatusRep::EraseResult StatusRep::ErasePayload(absl::string_view type_url){ absl::optional<size_t> index = status_internal::FindPayloadIndexByUrl(payloads_.get(), type_url); if (!index.has_value()) return {false, Status::PointerToRep(this)}; payloads_->erase(payloads_->begin() + index.value()); if (payloads_->empty() && message_.empty()) { // Special case: If this can be represented inlined, it MUST be inlined // (== depends on this behavior). EraseResult result = {true, Status::CodeToInlinedRep(code_)}; Unref(); return result; } return {true, Status::PointerToRep(this)}; }
// Status::ForEachPayload() // // Iterates over the stored payloads and calls the // `visitor(type_key, payload)` callable for each one. // // NOTE: The order of calls to `visitor()` is not specified and may change at // any time. // // NOTE: Any mutation on the same 'absl::Status' object during visitation is // forbidden and could result in undefined behavior. inlinevoidStatus::ForEachPayload( absl::FunctionRef<void(absl::string_view, const absl::Cord&)> visitor) const{ if (IsInlined(rep_)) return; RepToPointer(rep_)->ForEachPayload(visitor); }
for (size_t index = 0; index < payloads->size(); ++index) { constauto& elem = (*payloads)[in_reverse ? payloads->size() - 1 - index : index];
#ifdef NDEBUG visitor(elem.type_url, elem.payload); #else // In debug mode invalidate the type url to prevent users from relying on // this string lifetime.
// NOLINTNEXTLINE intentional extra conversion to force temporary. visitor(std::string(elem.type_url), elem.payload); #endif// NDEBUG } } }