| # 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* error1 = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Some error occured"); |
| grpc_error* 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)(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error); |
| ``` |
| |
| This means that the error ownership is NOT transferred when a functions calls: |
| |
| ```C |
| c->cb(exec_ctx, c->cb_arg, err); |
| ``` |
| |
| The caller is still responsible for unref-ing the error. |
| |
| However, the above line is currently being phased out! It is safer to invoke |
| callbacks with `GRPC_CLOSURE_RUN` and `GRPC_CLOSURE_SCHED`. These functions are |
| not callbacks, so they will take ownership of the error passed to them. |
| |
| ```C |
| grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Some error occured"); |
| GRPC_CLOSURE_RUN(exec_ctx, 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* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Some error occured"); |
| GRPC_CLOSURE_RUN(exec_ctx, 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(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *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* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Some error occured"); |
| // 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* 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. |