aboutsummaryrefslogtreecommitdiffhomepage
path: root/doc/core/grpc-error.md
blob: 105a6482845d83532efea39735e9dde28692ec7b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# 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 occurred");
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 occurred");
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 occurred");
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 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* 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.