mirror of https://github.com/grpc/grpc.git
Removed error.md (#30338)
parent
8f7927f7bc
commit
309d83832c
3 changed files with 0 additions and 162 deletions
@ -1,160 +0,0 @@ |
||||
# gRPC Error |
||||
|
||||
## Background |
||||
|
||||
`grpc_error` is the c-core's opaque representation of an error. It holds a |
||||
collection of integers, strings, timestamps, and child errors that related to |
||||
the final error. |
||||
|
||||
always present are: |
||||
|
||||
* GRPC_ERROR_STR_FILE and GRPC_ERROR_INT_FILE_LINE - the source location where |
||||
the error was generated |
||||
* GRPC_ERROR_STR_DESCRIPTION - a human readable description of the error |
||||
* GRPC_ERROR_TIME_CREATED - a timestamp indicating when the error happened |
||||
|
||||
An error can also have children; these are other errors that are believed to |
||||
have contributed to this one. By accumulating children, we can begin to root |
||||
cause high level failures from low level failures, without having to derive |
||||
execution paths from log lines. |
||||
|
||||
grpc_errors are refcounted objects, which means they need strict ownership |
||||
semantics. An extra ref on an error can cause a memory leak, and a missing ref |
||||
can cause a crash. |
||||
|
||||
This document serves as a detailed overview of grpc_error's ownership rules. It |
||||
should help people use the errors, as well as help people debug refcount related |
||||
errors. |
||||
|
||||
## Clarification of Ownership |
||||
|
||||
If a particular function is said to "own" an error, that means it has the |
||||
responsibility of calling unref on the error. A function may have access to an |
||||
error without ownership of it. |
||||
|
||||
This means the function may use the error, but must not call unref on it, since |
||||
that will be done elsewhere in the code. A function that does not own an error |
||||
may explicitly take ownership of it by manually calling GRPC_ERROR_REF. |
||||
|
||||
## Ownership Rules |
||||
|
||||
There are three rules of error ownership, which we will go over in detail. |
||||
|
||||
* If `grpc_error` is returned by a function, the caller owns a ref to that |
||||
instance. |
||||
* If a `grpc_error` is passed to a `grpc_closure` callback function, then that |
||||
function does not own a ref to the error. |
||||
* if a `grpc_error` is passed to *any other function*, then that function |
||||
takes ownership of the error. |
||||
|
||||
### Rule 1 |
||||
|
||||
> If `grpc_error` is returned by a function, the caller owns a ref to that |
||||
> instance.* |
||||
|
||||
For example, in the following code block, error1 and error2 are owned by the |
||||
current function. |
||||
|
||||
```C |
||||
grpc_error_handle error1 = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Some error occurred"); |
||||
grpc_error_handle error2 = some_operation_that_might_fail(...); |
||||
``` |
||||
|
||||
The current function would have to explicitly call GRPC_ERROR_UNREF on the |
||||
errors, or pass them along to a function that would take over the ownership. |
||||
|
||||
### Rule 2 |
||||
|
||||
> If a `grpc_error` is passed to a `grpc_closure` callback function, then that |
||||
> function does not own a ref to the error. |
||||
|
||||
A `grpc_closure` callback function is any function that has the signature: |
||||
|
||||
```C |
||||
void (*cb)(void *arg, grpc_error_handle error); |
||||
``` |
||||
|
||||
This means that the error ownership is NOT transferred when a functions calls: |
||||
|
||||
```C |
||||
c->cb(c->cb_arg, err); |
||||
``` |
||||
|
||||
The caller is still responsible for unref-ing the error. |
||||
|
||||
Note that you'll likely never need to run `c->cb(...)` yourself; the idiomatic |
||||
way to execute callbacks is via the `Closure::Run` method, which takes ownership |
||||
of the error variable. |
||||
|
||||
```C |
||||
grpc_error_handle error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Some error occurred"); |
||||
grpc_core::Closure::Run(DEBUG_LOCATION, c->cb, error); |
||||
// current function no longer has ownership of the error |
||||
``` |
||||
|
||||
If you schedule or run a closure, but still need ownership of the error, then |
||||
you must explicitly take a reference. |
||||
|
||||
```C |
||||
grpc_error_handle error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Some error occurred"); |
||||
grpc_core::Closure::Run(DEBUG_LOCATION, c->cb, GRPC_ERROR_REF(error)); |
||||
// do some other things with the error |
||||
GRPC_ERROR_UNREF(error); |
||||
``` |
||||
|
||||
Rule 2 is more important to keep in mind when **implementing** `grpc_closure` |
||||
callback functions. You must keep in mind that you do not own the error, and |
||||
must not unref it. More importantly, you cannot pass it to any function that |
||||
would take ownership of the error, without explicitly taking ownership yourself. |
||||
For example: |
||||
|
||||
```C |
||||
void on_some_action(void *arg, grpc_error_handle error) { |
||||
// this would cause a crash, because some_function will unref the error, |
||||
// and the caller of this callback will also unref it. |
||||
some_function(error); |
||||
|
||||
// this callback function must take ownership, so it can give that |
||||
// ownership to the function it is calling. |
||||
some_function(GRPC_ERROR_REF(error)); |
||||
} |
||||
``` |
||||
|
||||
### Rule 3 |
||||
|
||||
> if a `grpc_error` is passed to *any other function*, then that function takes |
||||
> ownership of the error. |
||||
|
||||
Take the following example: |
||||
|
||||
```C |
||||
grpc_error_handle error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Some error occurred"); |
||||
// do some things |
||||
some_function(error); |
||||
// can't use error anymore! might be gone. |
||||
``` |
||||
|
||||
When some_function is called, it takes over the ownership of the error, and it |
||||
will eventually unref it. So the caller can no longer safely use the error. |
||||
|
||||
If the caller needed to keep using the error (or passing it to other functions), |
||||
if would have to take on a reference to it. This is a common pattern seen. |
||||
|
||||
```C |
||||
void func() { |
||||
grpc_error_handle error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Some error"); |
||||
some_function(GRPC_ERROR_REF(error)); |
||||
// do things |
||||
some_other_function(GRPC_ERROR_REF(error)); |
||||
// do more things |
||||
some_last_function(error); |
||||
} |
||||
``` |
||||
|
||||
The last call takes ownership and will eventually give the error its final |
||||
unref. |
||||
|
||||
When **implementing** a function that takes an error (and is not a |
||||
`grpc_closure` callback function), you must ensure the error is unref-ed either |
||||
by doing it explicitly with GRPC_ERROR_UNREF, or by passing the error to a |
||||
function that takes over the ownership. |
Loading…
Reference in new issue