| /* |
| * |
| * Copyright 2016 gRPC authors. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| #ifndef GRPC_CORE_LIB_IOMGR_ERROR_H |
| #define GRPC_CORE_LIB_IOMGR_ERROR_H |
| |
| #include <grpc/support/port_platform.h> |
| |
| #include <inttypes.h> |
| #include <stdbool.h> |
| |
| #include "absl/status/status.h" |
| |
| #include <grpc/slice.h> |
| #include <grpc/status.h> |
| #include <grpc/support/log.h> |
| #include <grpc/support/time.h> |
| |
| #include "src/core/lib/debug/trace.h" |
| #include "src/core/lib/gpr/spinlock.h" |
| #include "src/core/lib/gprpp/status_helper.h" |
| #include "src/core/lib/slice/slice_internal.h" |
| |
| /// Opaque representation of an error. |
| /// See https://github.com/grpc/grpc/blob/master/doc/core/grpc-error.md for a |
| /// full write up of this object. |
| |
| #ifdef GRPC_ERROR_IS_ABSEIL_STATUS |
| |
| typedef absl::Status grpc_error_handle; |
| |
| #else // GRPC_ERROR_IS_ABSEIL_STATUS |
| |
| typedef struct grpc_error grpc_error; |
| typedef grpc_error* grpc_error_handle; |
| |
| #endif // GRPC_ERROR_IS_ABSEIL_STATUS |
| |
| typedef enum { |
| /// 'errno' from the operating system |
| GRPC_ERROR_INT_ERRNO = |
| static_cast<int>(grpc_core::StatusIntProperty::kErrorNo), |
| /// __LINE__ from the call site creating the error |
| GRPC_ERROR_INT_FILE_LINE = |
| static_cast<int>(grpc_core::StatusIntProperty::kFileLine), |
| /// stream identifier: for errors that are associated with an individual |
| /// wire stream |
| GRPC_ERROR_INT_STREAM_ID = |
| static_cast<int>(grpc_core::StatusIntProperty::kStreamId), |
| /// grpc status code representing this error |
| GRPC_ERROR_INT_GRPC_STATUS = |
| static_cast<int>(grpc_core::StatusIntProperty::kRpcStatus), |
| /// offset into some binary blob (usually represented by |
| /// GRPC_ERROR_STR_RAW_BYTES) where the error occurred |
| GRPC_ERROR_INT_OFFSET = |
| static_cast<int>(grpc_core::StatusIntProperty::kOffset), |
| /// context sensitive index associated with the error |
| GRPC_ERROR_INT_INDEX = static_cast<int>(grpc_core::StatusIntProperty::kIndex), |
| /// context sensitive size associated with the error |
| GRPC_ERROR_INT_SIZE = static_cast<int>(grpc_core::StatusIntProperty::kSize), |
| /// http2 error code associated with the error (see the HTTP2 RFC) |
| GRPC_ERROR_INT_HTTP2_ERROR = |
| static_cast<int>(grpc_core::StatusIntProperty::kHttp2Error), |
| /// TSI status code associated with the error |
| GRPC_ERROR_INT_TSI_CODE = |
| static_cast<int>(grpc_core::StatusIntProperty::kTsiCode), |
| /// WSAGetLastError() reported when this error occurred |
| GRPC_ERROR_INT_WSA_ERROR = |
| static_cast<int>(grpc_core::StatusIntProperty::kWsaError), |
| /// File descriptor associated with this error |
| GRPC_ERROR_INT_FD = static_cast<int>(grpc_core::StatusIntProperty::kFd), |
| /// HTTP status (i.e. 404) |
| GRPC_ERROR_INT_HTTP_STATUS = |
| static_cast<int>(grpc_core::StatusIntProperty::kHttpStatus), |
| /// chttp2: did the error occur while a write was in progress |
| GRPC_ERROR_INT_OCCURRED_DURING_WRITE = |
| static_cast<int>(grpc_core::StatusIntProperty::kOccurredDuringWrite), |
| /// channel connectivity state associated with the error |
| GRPC_ERROR_INT_CHANNEL_CONNECTIVITY_STATE = |
| static_cast<int>(grpc_core::StatusIntProperty::ChannelConnectivityState), |
| /// LB policy drop |
| GRPC_ERROR_INT_LB_POLICY_DROP = |
| static_cast<int>(grpc_core::StatusIntProperty::kLbPolicyDrop), |
| |
| /// Must always be last |
| GRPC_ERROR_INT_MAX, |
| } grpc_error_ints; |
| |
| typedef enum { |
| /// top-level textual description of this error |
| GRPC_ERROR_STR_DESCRIPTION = |
| static_cast<int>(grpc_core::StatusStrProperty::kDescription), |
| /// source file in which this error occurred |
| GRPC_ERROR_STR_FILE = static_cast<int>(grpc_core::StatusStrProperty::kFile), |
| /// operating system description of this error |
| GRPC_ERROR_STR_OS_ERROR = |
| static_cast<int>(grpc_core::StatusStrProperty::kOsError), |
| /// syscall that generated this error |
| GRPC_ERROR_STR_SYSCALL = |
| static_cast<int>(grpc_core::StatusStrProperty::kSyscall), |
| /// peer that we were trying to communicate when this error occurred |
| GRPC_ERROR_STR_TARGET_ADDRESS = |
| static_cast<int>(grpc_core::StatusStrProperty::kTargetAddress), |
| /// grpc status message associated with this error |
| GRPC_ERROR_STR_GRPC_MESSAGE = |
| static_cast<int>(grpc_core::StatusStrProperty::kGrpcMessage), |
| /// hex dump (or similar) with the data that generated this error |
| GRPC_ERROR_STR_RAW_BYTES = |
| static_cast<int>(grpc_core::StatusStrProperty::kRawBytes), |
| /// tsi error string associated with this error |
| GRPC_ERROR_STR_TSI_ERROR = |
| static_cast<int>(grpc_core::StatusStrProperty::kTsiError), |
| /// filename that we were trying to read/write when this error occurred |
| GRPC_ERROR_STR_FILENAME = |
| static_cast<int>(grpc_core::StatusStrProperty::kFilename), |
| /// key associated with the error |
| GRPC_ERROR_STR_KEY = static_cast<int>(grpc_core::StatusStrProperty::kKey), |
| /// value associated with the error |
| GRPC_ERROR_STR_VALUE = static_cast<int>(grpc_core::StatusStrProperty::kValue), |
| |
| /// Must always be last |
| GRPC_ERROR_STR_MAX, |
| } grpc_error_strs; |
| |
| typedef enum { |
| /// timestamp of error creation |
| GRPC_ERROR_TIME_CREATED, |
| |
| /// Must always be last |
| GRPC_ERROR_TIME_MAX, |
| } grpc_error_times; |
| |
| std::string grpc_error_std_string(grpc_error_handle error); |
| |
| // debug only toggles that allow for a sanity to check that ensures we will |
| // never create any errors in the per-RPC hotpath. |
| void grpc_disable_error_creation(); |
| void grpc_enable_error_creation(); |
| |
| #ifdef GRPC_ERROR_IS_ABSEIL_STATUS |
| |
| #define GRPC_ERROR_NONE absl::OkStatus() |
| #define GRPC_ERROR_OOM absl::Status(absl::ResourceExhaustedError("")) |
| #define GRPC_ERROR_CANCELLED absl::CancelledError() |
| |
| #define GRPC_ERROR_REF(err) (err) |
| #define GRPC_ERROR_UNREF(err) (void)(err) |
| |
| #define GRPC_ERROR_CREATE_FROM_STATIC_STRING(desc) \ |
| StatusCreate(absl::StatusCode::kUnknown, desc, DEBUG_LOCATION, {}) |
| #define GRPC_ERROR_CREATE_FROM_COPIED_STRING(desc) \ |
| StatusCreate(absl::StatusCode::kUnknown, desc, DEBUG_LOCATION, {}) |
| #define GRPC_ERROR_CREATE_FROM_CPP_STRING(desc) \ |
| StatusCreate(absl::StatusCode::kUnknown, desc, DEBUG_LOCATION, {}) |
| #define GRPC_ERROR_CREATE_FROM_STRING_VIEW(desc) \ |
| StatusCreate(absl::StatusCode::kUnknown, desc, DEBUG_LOCATION, {}) |
| |
| absl::Status grpc_status_create(absl::StatusCode code, absl::string_view msg, |
| const grpc_core::DebugLocation& location, |
| size_t children_count, |
| absl::Status* children) GRPC_MUST_USE_RESULT; |
| |
| // Create an error that references some other errors. This function adds a |
| // reference to each error in errs - it does not consume an existing reference |
| #define GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(desc, errs, count) \ |
| grpc_status_create(absl::StatusCode::kUnknown, desc, DEBUG_LOCATION, count, \ |
| errs) |
| #define GRPC_ERROR_CREATE_REFERENCING_FROM_COPIED_STRING(desc, errs, count) \ |
| grpc_status_create(absl::StatusCode::kUnknown, desc, DEBUG_LOCATION, count, \ |
| errs) |
| |
| // Consumes all the errors in the vector and forms a referencing error from |
| // them. If the vector is empty, return GRPC_ERROR_NONE. |
| template <typename VectorType> |
| static absl::Status grpc_status_create_from_vector( |
| const grpc_core::DebugLocation& location, absl::string_view desc, |
| VectorType* error_list) { |
| absl::Status error = GRPC_ERROR_NONE; |
| if (error_list->size() != 0) { |
| error = grpc_status_create(absl::StatusCode::kUnknown, desc, location, |
| error_list->size(), error_list->data()); |
| error_list->clear(); |
| } |
| return error; |
| } |
| |
| #define GRPC_ERROR_CREATE_FROM_VECTOR(desc, error_list) \ |
| grpc_status_create_from_vector(DEBUG_LOCATION, desc, error_list) |
| #define GRPC_ERROR_CREATE_FROM_VECTOR_AND_CPP_STRING(desc, error_list) \ |
| grpc_status_create_from_vector(DEBUG_LOCATION, desc, error_list) |
| |
| absl::Status grpc_os_error(const grpc_core::DebugLocation& location, int err, |
| const char* call_name) GRPC_MUST_USE_RESULT; |
| |
| inline absl::Status grpc_assert_never_ok(absl::Status error) { |
| GPR_ASSERT(error != GRPC_ERROR_NONE); |
| return error; |
| } |
| |
| /// create an error associated with errno!=0 (an 'operating system' error) |
| #define GRPC_OS_ERROR(err, call_name) \ |
| grpc_assert_never_ok(grpc_os_error(DEBUG_LOCATION, err, call_name)) |
| |
| absl::Status grpc_wsa_error(const grpc_core::DebugLocation& location, int err, |
| const char* call_name) GRPC_MUST_USE_RESULT; |
| |
| /// windows only: create an error associated with WSAGetLastError()!=0 |
| #define GRPC_WSA_ERROR(err, call_name) \ |
| grpc_wsa_error(DEBUG_LOCATION, err, call_name) |
| |
| #else // GRPC_ERROR_IS_ABSEIL_STATUS |
| |
| /// The following "special" errors can be propagated without allocating memory. |
| /// They are always even so that other code (particularly combiner locks, |
| /// polling engines) can safely use the lower bit for themselves. |
| |
| #define GRPC_ERROR_NONE ((grpc_error_handle)NULL) |
| #define GRPC_ERROR_RESERVED_1 ((grpc_error_handle)1) |
| #define GRPC_ERROR_OOM ((grpc_error_handle)2) |
| #define GRPC_ERROR_RESERVED_2 ((grpc_error_handle)3) |
| #define GRPC_ERROR_CANCELLED ((grpc_error_handle)4) |
| #define GRPC_ERROR_SPECIAL_MAX GRPC_ERROR_CANCELLED |
| |
| inline bool grpc_error_is_special(grpc_error_handle err) { |
| return err <= GRPC_ERROR_SPECIAL_MAX; |
| } |
| |
| #ifndef NDEBUG |
| grpc_error_handle grpc_error_do_ref(grpc_error_handle err, const char* file, |
| int line); |
| void grpc_error_do_unref(grpc_error_handle err, const char* file, int line); |
| inline grpc_error_handle grpc_error_ref(grpc_error_handle err, const char* file, |
| int line) { |
| if (grpc_error_is_special(err)) return err; |
| return grpc_error_do_ref(err, file, line); |
| } |
| inline void grpc_error_unref(grpc_error_handle err, const char* file, |
| int line) { |
| if (grpc_error_is_special(err)) return; |
| grpc_error_do_unref(err, file, line); |
| } |
| #define GRPC_ERROR_REF(err) grpc_error_ref(err, __FILE__, __LINE__) |
| #define GRPC_ERROR_UNREF(err) grpc_error_unref(err, __FILE__, __LINE__) |
| #else |
| grpc_error_handle grpc_error_do_ref(grpc_error_handle err); |
| void grpc_error_do_unref(grpc_error_handle err); |
| inline grpc_error_handle grpc_error_ref(grpc_error_handle err) { |
| if (grpc_error_is_special(err)) return err; |
| return grpc_error_do_ref(err); |
| } |
| inline void grpc_error_unref(grpc_error_handle err) { |
| if (grpc_error_is_special(err)) return; |
| grpc_error_do_unref(err); |
| } |
| #define GRPC_ERROR_REF(err) grpc_error_ref(err) |
| #define GRPC_ERROR_UNREF(err) grpc_error_unref(err) |
| #endif |
| |
| /// Create an error - but use GRPC_ERROR_CREATE instead |
| grpc_error_handle grpc_error_create(const char* file, int line, |
| const grpc_slice& desc, |
| grpc_error_handle* referencing, |
| size_t num_referencing); |
| /// Create an error (this is the preferred way of generating an error that is |
| /// not due to a system call - for system calls, use GRPC_OS_ERROR or |
| /// GRPC_WSA_ERROR as appropriate) |
| /// \a referencing is an array of num_referencing elements indicating one or |
| /// more errors that are believed to have contributed to this one |
| /// err = grpc_error_create(x, y, z, r, nr) is equivalent to: |
| /// err = grpc_error_create(x, y, z, NULL, 0); |
| /// for (i=0; i<nr; i++) err = grpc_error_add_child(err, r[i]); |
| #define GRPC_ERROR_CREATE_FROM_STATIC_STRING(desc) \ |
| grpc_error_create(__FILE__, __LINE__, grpc_slice_from_static_string(desc), \ |
| NULL, 0) |
| #define GRPC_ERROR_CREATE_FROM_COPIED_STRING(desc) \ |
| grpc_error_create(__FILE__, __LINE__, grpc_slice_from_copied_string(desc), \ |
| NULL, 0) |
| #define GRPC_ERROR_CREATE_FROM_CPP_STRING(desc) \ |
| grpc_error_create(__FILE__, __LINE__, grpc_slice_from_cpp_string(desc), \ |
| NULL, 0) |
| #define GRPC_ERROR_CREATE_FROM_STRING_VIEW(desc) \ |
| grpc_error_create( \ |
| __FILE__, __LINE__, \ |
| grpc_slice_from_copied_buffer((desc).data(), (desc).size()), NULL, 0) |
| |
| // Create an error that references some other errors. This function adds a |
| // reference to each error in errs - it does not consume an existing reference |
| #define GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(desc, errs, count) \ |
| grpc_error_create(__FILE__, __LINE__, grpc_slice_from_static_string(desc), \ |
| errs, count) |
| #define GRPC_ERROR_CREATE_REFERENCING_FROM_COPIED_STRING(desc, errs, count) \ |
| grpc_error_create(__FILE__, __LINE__, grpc_slice_from_copied_string(desc), \ |
| errs, count) |
| |
| #define GRPC_ERROR_CREATE_FROM_VECTOR(desc, error_list) \ |
| grpc_error_create_from_vector( \ |
| __FILE__, __LINE__, grpc_slice_from_static_string, desc, error_list) |
| #define GRPC_ERROR_CREATE_FROM_VECTOR_AND_CPP_STRING(desc, error_list) \ |
| grpc_error_create_from_vector(__FILE__, __LINE__, \ |
| grpc_slice_from_cpp_string, desc, error_list) |
| |
| // Consumes all the errors in the vector and forms a referencing error from |
| // them. If the vector is empty, return GRPC_ERROR_NONE. |
| template <typename VectorType, typename StringType, |
| typename SliceFromStringFunction> |
| static grpc_error_handle grpc_error_create_from_vector( |
| const char* file, int line, |
| SliceFromStringFunction slice_from_string_function, StringType desc, |
| VectorType* error_list) { |
| grpc_error_handle error = GRPC_ERROR_NONE; |
| if (error_list->size() != 0) { |
| error = grpc_error_create(file, line, |
| slice_from_string_function(std::move(desc)), |
| error_list->data(), error_list->size()); |
| // Remove refs to all errors in error_list. |
| for (size_t i = 0; i < error_list->size(); i++) { |
| GRPC_ERROR_UNREF((*error_list)[i]); |
| } |
| error_list->clear(); |
| } |
| return error; |
| } |
| |
| grpc_error_handle grpc_os_error(const char* file, int line, int err, |
| const char* call_name) GRPC_MUST_USE_RESULT; |
| |
| inline grpc_error_handle grpc_assert_never_ok(grpc_error_handle error) { |
| GPR_ASSERT(error != GRPC_ERROR_NONE); |
| return error; |
| } |
| |
| /// create an error associated with errno!=0 (an 'operating system' error) |
| #define GRPC_OS_ERROR(err, call_name) \ |
| grpc_assert_never_ok(grpc_os_error(__FILE__, __LINE__, err, call_name)) |
| grpc_error_handle grpc_wsa_error(const char* file, int line, int err, |
| const char* call_name) GRPC_MUST_USE_RESULT; |
| /// windows only: create an error associated with WSAGetLastError()!=0 |
| #define GRPC_WSA_ERROR(err, call_name) \ |
| grpc_wsa_error(__FILE__, __LINE__, err, call_name) |
| |
| #endif // GRPC_ERROR_IS_ABSEIL_STATUS |
| |
| grpc_error_handle grpc_error_set_int(grpc_error_handle src, |
| grpc_error_ints which, |
| intptr_t value) GRPC_MUST_USE_RESULT; |
| /// It is an error to pass nullptr as `p`. Caller should allocate a phony |
| /// intptr_t for `p`, even if the value of `p` is not used. |
| bool grpc_error_get_int(grpc_error_handle error, grpc_error_ints which, |
| intptr_t* p); |
| grpc_error_handle grpc_error_set_str( |
| grpc_error_handle src, grpc_error_strs which, |
| absl::string_view str) GRPC_MUST_USE_RESULT; |
| /// Returns false if the specified string is not set. |
| bool grpc_error_get_str(grpc_error_handle error, grpc_error_strs which, |
| std::string* str); |
| |
| /// Add a child error: an error that is believed to have contributed to this |
| /// error occurring. Allows root causing high level errors from lower level |
| /// errors that contributed to them. The src error takes ownership of the |
| /// child error. |
| /// |
| /// Edge Conditions - |
| /// 1) If either of \a src or \a child is GRPC_ERROR_NONE, returns a reference |
| /// to the other argument. 2) If both \a src and \a child are GRPC_ERROR_NONE, |
| /// returns GRPC_ERROR_NONE. 3) If \a src and \a child point to the same error, |
| /// returns a single reference. (Note that, 2 references should have been |
| /// received to the error in this case.) |
| grpc_error_handle grpc_error_add_child( |
| grpc_error_handle src, grpc_error_handle child) GRPC_MUST_USE_RESULT; |
| |
| bool grpc_log_error(const char* what, grpc_error_handle error, const char* file, |
| int line); |
| inline bool grpc_log_if_error(const char* what, grpc_error_handle error, |
| const char* file, int line) { |
| return error == GRPC_ERROR_NONE ? true |
| : grpc_log_error(what, error, file, line); |
| } |
| |
| #define GRPC_LOG_IF_ERROR(what, error) \ |
| (grpc_log_if_error((what), (error), __FILE__, __LINE__)) |
| |
| /// Helper class to get & set grpc_error_handle in a thread-safe fashion. |
| /// This could be considered as atomic<grpc_error_handle>. |
| class AtomicError { |
| public: |
| AtomicError() { |
| error_ = GRPC_ERROR_NONE; |
| lock_ = GPR_SPINLOCK_STATIC_INITIALIZER; |
| } |
| explicit AtomicError(grpc_error_handle error) { |
| error_ = GRPC_ERROR_REF(error); |
| } |
| ~AtomicError() { GRPC_ERROR_UNREF(error_); } |
| |
| AtomicError(const AtomicError&) = delete; |
| AtomicError& operator=(const AtomicError&) = delete; |
| |
| /// returns get() == GRPC_ERROR_NONE |
| bool ok() { |
| gpr_spinlock_lock(&lock_); |
| bool ret = error_ == GRPC_ERROR_NONE; |
| gpr_spinlock_unlock(&lock_); |
| return ret; |
| } |
| |
| grpc_error_handle get() { |
| gpr_spinlock_lock(&lock_); |
| grpc_error_handle ret = error_; |
| gpr_spinlock_unlock(&lock_); |
| return ret; |
| } |
| |
| void set(grpc_error_handle error) { |
| gpr_spinlock_lock(&lock_); |
| GRPC_ERROR_UNREF(error_); |
| error_ = GRPC_ERROR_REF(error); |
| gpr_spinlock_unlock(&lock_); |
| } |
| |
| private: |
| grpc_error_handle error_; |
| gpr_spinlock lock_; |
| }; |
| |
| #endif /* GRPC_CORE_LIB_IOMGR_ERROR_H */ |