ec89a92020150b0ea1671905fc130acf597ae2d9
[platform/upstream/grpc.git] / src / core / lib / iomgr / resource_quota.h
1 /*
2  *
3  * Copyright 2016 gRPC authors.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18
19 #ifndef GRPC_CORE_LIB_IOMGR_RESOURCE_QUOTA_H
20 #define GRPC_CORE_LIB_IOMGR_RESOURCE_QUOTA_H
21
22 #include <grpc/support/port_platform.h>
23
24 #include <grpc/grpc.h>
25
26 #include "src/core/lib/debug/trace.h"
27 #include "src/core/lib/iomgr/closure.h"
28
29 /** \file Tracks resource usage against a pool.
30
31     The current implementation tracks only memory usage, but in the future
32     this may be extended to (for example) threads and file descriptors.
33
34     A grpc_resource_quota represents the pooled resources, and
35     grpc_resource_user instances attach to the quota and consume those
36     resources. They also offer a vector for reclamation: if we become
37     resource constrained, grpc_resource_user instances are asked (in turn) to
38     free up whatever they can so that the system as a whole can make progress.
39
40     There are three kinds of reclamation that take place, in order of increasing
41     invasiveness:
42     - an internal reclamation, where cached resource at the resource user level
43       is returned to the quota
44     - a benign reclamation phase, whereby resources that are in use but are not
45       helping anything make progress are reclaimed
46     - a destructive reclamation, whereby resources that are helping something
47       make progress may be enacted so that at least one part of the system can
48       complete.
49
50     Only one reclamation will be outstanding for a given quota at a given time.
51     On each reclamation attempt, the kinds of reclamation are tried in order of
52     increasing invasiveness, stopping at the first one that succeeds. Thus, on a
53     given reclamation attempt, if internal and benign reclamation both fail, it
54     will wind up doing a destructive reclamation. However, the next reclamation
55     attempt may then be able to get what it needs via internal or benign
56     reclamation, due to resources that may have been freed up by the destructive
57     reclamation in the previous attempt.
58
59     Future work will be to expose the current resource pressure so that back
60     pressure can be applied to avoid reclamation phases starting.
61
62     Resource users own references to resource quotas, and resource quotas
63     maintain lists of users (which users arrange to leave before they are
64     destroyed) */
65
66 extern grpc_core::TraceFlag grpc_resource_quota_trace;
67
68 // TODO(juanlishen): This is a hack. We need to do real accounting instead of
69 // hard coding.
70 constexpr size_t GRPC_RESOURCE_QUOTA_CALL_SIZE = 15 * 1024;
71 constexpr size_t GRPC_RESOURCE_QUOTA_CHANNEL_SIZE = 50 * 1024;
72
73 grpc_resource_quota* grpc_resource_quota_ref_internal(
74     grpc_resource_quota* resource_quota);
75 void grpc_resource_quota_unref_internal(grpc_resource_quota* resource_quota);
76 grpc_resource_quota* grpc_resource_quota_from_channel_args(
77     const grpc_channel_args* channel_args, bool create = true);
78
79 /* Return a number indicating current memory pressure:
80    0.0 ==> no memory usage
81    1.0 ==> maximum memory usage */
82 double grpc_resource_quota_get_memory_pressure(
83     grpc_resource_quota* resource_quota);
84
85 size_t grpc_resource_quota_peek_size(grpc_resource_quota* resource_quota);
86
87 typedef struct grpc_resource_user grpc_resource_user;
88
89 grpc_resource_user* grpc_resource_user_create(
90     grpc_resource_quota* resource_quota, const char* name);
91
92 /* Returns a borrowed reference to the underlying resource quota for this
93    resource user. */
94 grpc_resource_quota* grpc_resource_user_quota(
95     grpc_resource_user* resource_user);
96
97 void grpc_resource_user_ref(grpc_resource_user* resource_user);
98 void grpc_resource_user_unref(grpc_resource_user* resource_user);
99 void grpc_resource_user_shutdown(grpc_resource_user* resource_user);
100
101 /* Attempts to get quota from the resource_user to create 'thread_count' number
102  * of threads. Returns true if successful (i.e the caller is now free to create
103  * 'thread_count' number of threads) or false if quota is not available */
104 bool grpc_resource_user_allocate_threads(grpc_resource_user* resource_user,
105                                          int thread_count);
106 /* Releases 'thread_count' worth of quota back to the resource user. The quota
107  * should have been previously obtained successfully by calling
108  * grpc_resource_user_allocate_threads().
109  *
110  * Note: There need not be an exact one-to-one correspondence between
111  * grpc_resource_user_allocate_threads() and grpc_resource_user_free_threads()
112  * calls. The only requirement is that the number of threads allocated should
113  * all be eventually released */
114 void grpc_resource_user_free_threads(grpc_resource_user* resource_user,
115                                      int thread_count);
116
117 /* Allocates from the resource user 'size' worth of memory if this won't exceed
118  * the resource quota's total size. Returns whether the allocation is done
119  * successfully. If allocated successfully, the memory should be freed by the
120  * caller eventually. */
121 bool grpc_resource_user_safe_alloc(grpc_resource_user* resource_user,
122                                    size_t size);
123 /* Allocates from the resource user 'size' worth of memory.
124  * If optional_on_done is NULL, then allocate immediately. This may push the
125  * quota over-limit, at which point reclamation will kick in. The caller is
126  * always responsible to free the memory eventually.
127  * Returns true if the allocation was successful. Otherwise, if optional_on_done
128  * is non-NULL, it will be scheduled without error when the allocation has been
129  * granted by the quota, and the caller is responsible to free the memory
130  * eventually. Or it may be scheduled with an error, in which case the caller
131  * fails to allocate the memory and shouldn't free the memory.
132  */
133 bool grpc_resource_user_alloc(grpc_resource_user* resource_user, size_t size,
134                               grpc_closure* optional_on_done)
135     GRPC_MUST_USE_RESULT;
136 /* Release memory back to the quota */
137 void grpc_resource_user_free(grpc_resource_user* resource_user, size_t size);
138 /* Post a memory reclaimer to the resource user. Only one benign and one
139    destructive reclaimer can be posted at once. When executed, the reclaimer
140    MUST call grpc_resource_user_finish_reclamation before it completes, to
141    return control to the resource quota. */
142 void grpc_resource_user_post_reclaimer(grpc_resource_user* resource_user,
143                                        bool destructive, grpc_closure* closure);
144 /* Finish a reclamation step */
145 void grpc_resource_user_finish_reclamation(grpc_resource_user* resource_user);
146
147 /* Helper to allocate slices from a resource user */
148 typedef struct grpc_resource_user_slice_allocator {
149   /* Closure for when a resource user allocation completes */
150   grpc_closure on_allocated;
151   /* Closure to call when slices have been allocated */
152   grpc_closure on_done;
153   /* Length of slices to allocate on the current request */
154   size_t length;
155   /* Number of slices to allocate on the current request */
156   size_t count;
157   /* Destination for slices to allocate on the current request */
158   grpc_slice_buffer* dest;
159   /* Parent resource user */
160   grpc_resource_user* resource_user;
161 } grpc_resource_user_slice_allocator;
162
163 /* Initialize a slice allocator.
164    When an allocation is completed, calls \a cb with arg \p. */
165 void grpc_resource_user_slice_allocator_init(
166     grpc_resource_user_slice_allocator* slice_allocator,
167     grpc_resource_user* resource_user, grpc_iomgr_cb_func cb, void* p);
168
169 /* Allocate \a count slices of length \a length into \a dest. Only one request
170    can be outstanding at a time.
171    Returns whether the slice was allocated inline in the function. If true,
172    the \a slice_allocator->on_allocated callback will not be called. */
173 bool grpc_resource_user_alloc_slices(
174     grpc_resource_user_slice_allocator* slice_allocator, size_t length,
175     size_t count, grpc_slice_buffer* dest) GRPC_MUST_USE_RESULT;
176
177 #endif /* GRPC_CORE_LIB_IOMGR_RESOURCE_QUOTA_H */