3 * Copyright 2015 gRPC authors.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #ifndef GRPC_CORE_LIB_TRANSPORT_METADATA_H
20 #define GRPC_CORE_LIB_TRANSPORT_METADATA_H
22 #include <grpc/support/port_platform.h>
24 #include "include/grpc/impl/codegen/log.h"
26 #include <grpc/grpc.h>
27 #include <grpc/slice.h>
29 #include "src/core/lib/debug/trace.h"
30 #include "src/core/lib/gpr/useful.h"
31 #include "src/core/lib/gprpp/atomic.h"
32 #include "src/core/lib/gprpp/sync.h"
34 extern grpc_core::DebugOnlyTraceFlag grpc_trace_metadata;
36 /* This file provides a mechanism for tracking metadata through the grpc stack.
37 It's not intended for consumption outside of the library.
39 Metadata is tracked in the context of a grpc_mdctx. For the time being there
40 is one of these per-channel, avoiding cross channel interference with memory
41 use and lock contention.
43 The context tracks unique strings (grpc_mdstr) and pairs of strings
44 (grpc_mdelem). Any of these objects can be checked for equality by comparing
45 their pointers. These objects are reference counted.
47 grpc_mdelem can additionally store a (non-NULL) user data pointer. This
48 pointer is intended to be used to cache semantic meaning of a metadata
49 element. For example, an OAuth token may cache the credentials it represents
50 and the time at which it expires in the mdelem user data.
52 Combining this metadata cache and the hpack compression table allows us to
53 simply lookup complete preparsed objects quickly, incurring a few atomic
54 ops per metadata element on the fast path.
56 grpc_mdelem instances MAY live longer than their refcount implies, and are
57 garbage collected periodically, meaning cached data can easily outlive a
60 STATIC METADATA: in static_metadata.h we declare a set of static metadata.
61 These mdelems and mdstrs are available via pre-declared code generated macros
62 and are available to code anywhere between grpc_init() and grpc_shutdown().
63 They are not refcounted, but can be passed to _ref and _unref functions
64 declared here - in which case those functions are effectively no-ops. */
66 /* Forward declarations */
67 typedef struct grpc_mdelem grpc_mdelem;
69 /* if changing this, make identical changes in:
70 - grpc_core::{InternedMetadata, AllocatedMetadata}
71 - grpc_metadata in grpc_types.h */
72 typedef struct grpc_mdelem_data {
74 const grpc_slice value;
75 /* there is a private part to this in metadata.c */
78 /* GRPC_MDELEM_STORAGE_* enum values that can be treated as interned always have
79 this bit set in their integer value */
80 #define GRPC_MDELEM_STORAGE_INTERNED_BIT 1
83 /* memory pointed to by grpc_mdelem::payload is owned by an external system */
84 GRPC_MDELEM_STORAGE_EXTERNAL = 0,
85 /* memory pointed to by grpc_mdelem::payload is interned by the metadata
87 GRPC_MDELEM_STORAGE_INTERNED = GRPC_MDELEM_STORAGE_INTERNED_BIT,
88 /* memory pointed to by grpc_mdelem::payload is allocated by the metadata
90 GRPC_MDELEM_STORAGE_ALLOCATED = 2,
91 /* memory is in the static metadata table */
92 GRPC_MDELEM_STORAGE_STATIC = 2 | GRPC_MDELEM_STORAGE_INTERNED_BIT,
93 } grpc_mdelem_data_storage;
96 /* a grpc_mdelem_data* generally, with the two lower bits signalling memory
97 ownership as per grpc_mdelem_data_storage */
101 #define GRPC_MDELEM_DATA(md) ((grpc_mdelem_data*)((md).payload & ~(uintptr_t)3))
102 #define GRPC_MDELEM_STORAGE(md) \
103 ((grpc_mdelem_data_storage)((md).payload & (uintptr_t)3))
105 #define GRPC_MAKE_MDELEM(data, storage) \
106 (grpc_mdelem{((uintptr_t)(data)) | ((uintptr_t)storage)})
108 #define GRPC_MAKE_MDELEM(data, storage) \
109 ((grpc_mdelem){((uintptr_t)(data)) | ((uintptr_t)storage)})
111 #define GRPC_MDELEM_IS_INTERNED(md) \
112 ((grpc_mdelem_data_storage)((md).payload & \
113 (uintptr_t)GRPC_MDELEM_STORAGE_INTERNED_BIT))
115 /* Unrefs the slices. */
116 grpc_mdelem grpc_mdelem_from_slices(const grpc_slice& key,
117 const grpc_slice& value);
119 /* Cheaply convert a grpc_metadata to a grpc_mdelem; may use the grpc_metadata
120 object as backing storage (so lifetimes should align) */
121 grpc_mdelem grpc_mdelem_from_grpc_metadata(grpc_metadata* metadata);
123 /* Does not unref the slices; if a new non-interned mdelem is needed, allocates
124 one if compatible_external_backing_store is NULL, or uses
125 compatible_external_backing_store if it is non-NULL (in which case it's the
126 users responsibility to ensure that it outlives usage) */
127 grpc_mdelem grpc_mdelem_create(
128 const grpc_slice& key, const grpc_slice& value,
129 grpc_mdelem_data* compatible_external_backing_store);
131 #define GRPC_MDKEY(md) (GRPC_MDELEM_DATA(md)->key)
132 #define GRPC_MDVALUE(md) (GRPC_MDELEM_DATA(md)->value)
134 bool grpc_mdelem_eq(grpc_mdelem a, grpc_mdelem b);
135 /* Often we compare metadata where we know a-priori that the second parameter is
136 * static, and that the keys match. This most commonly happens when processing
137 * metadata batch callouts in initial/trailing filters. In this case, fastpath
138 * grpc_mdelem_eq and remove unnecessary checks. */
139 inline bool grpc_mdelem_static_value_eq(grpc_mdelem a, grpc_mdelem b_static) {
140 if (a.payload == b_static.payload) return true;
141 return grpc_slice_eq(GRPC_MDVALUE(a), GRPC_MDVALUE(b_static));
144 /* Mutator and accessor for grpc_mdelem user data. The destructor function
145 is used as a type tag and is checked during user_data fetch. */
146 void* grpc_mdelem_get_user_data(grpc_mdelem md, void (*if_destroy_func)(void*));
147 void* grpc_mdelem_set_user_data(grpc_mdelem md, void (*destroy_func)(void*),
150 // Defined in metadata.cc.
154 void grpc_mdelem_trace_ref(void* md, const grpc_slice& key,
155 const grpc_slice& value, intptr_t refcnt,
156 const char* file, int line);
157 void grpc_mdelem_trace_unref(void* md, const grpc_slice& key,
158 const grpc_slice& value, intptr_t refcnt,
159 const char* file, int line);
161 namespace grpc_core {
163 typedef void (*destroy_user_data_func)(void* data);
167 grpc_core::Atomic<destroy_user_data_func> destroy_user_data;
168 grpc_core::Atomic<void*> data;
171 class InternedMetadata {
174 explicit BucketLink(InternedMetadata* md) : next(md) {}
176 InternedMetadata* next = nullptr;
179 InternedMetadata(const grpc_slice& key, const grpc_slice& value,
180 uint32_t hash, InternedMetadata* next);
184 void Ref(const char* file, int line) {
185 grpc_mdelem_trace_ref(this, key_, value_, RefValue(), file, line);
186 const intptr_t prior = refcnt_.FetchAdd(1, MemoryOrder::RELAXED);
187 GPR_ASSERT(prior > 0);
189 bool Unref(const char* file, int line) {
190 grpc_mdelem_trace_unref(this, key_, value_, RefValue(), file, line);
194 // We define a naked Ref() in the else-clause to make sure we don't
195 // inadvertently skip the assert on debug builds.
197 /* we can assume the ref count is >= 1 as the application is calling
198 this function - meaning that no adjustment to mdtab_free is necessary,
199 simplifying the logic here to be just an atomic increment */
200 refcnt_.FetchAdd(1, MemoryOrder::RELAXED);
202 #endif // ifndef NDEBUG
204 const intptr_t prior = refcnt_.FetchSub(1, MemoryOrder::ACQ_REL);
205 GPR_DEBUG_ASSERT(prior > 0);
209 void RefWithShardLocked(mdtab_shard* shard);
210 const grpc_slice& key() const { return key_; }
211 const grpc_slice& value() const { return value_; }
212 UserData* user_data() { return &user_data_; }
213 uint32_t hash() { return hash_; }
214 InternedMetadata* bucket_next() { return link_.next; }
215 void set_bucket_next(InternedMetadata* md) { link_.next = md; }
217 static size_t CleanupLinkedMetadata(BucketLink* head);
220 bool AllRefsDropped() { return refcnt_.Load(MemoryOrder::ACQUIRE) == 0; }
221 bool FirstRef() { return refcnt_.FetchAdd(1, MemoryOrder::RELAXED) == 0; }
222 intptr_t RefValue() { return refcnt_.Load(MemoryOrder::RELAXED); }
224 /* must be byte compatible with grpc_mdelem_data */
228 /* private only data */
229 grpc_core::Atomic<intptr_t> refcnt_;
237 /* Shadow structure for grpc_mdelem_data for allocated elements */
238 class AllocatedMetadata {
240 AllocatedMetadata(const grpc_slice& key, const grpc_slice& value);
241 ~AllocatedMetadata();
243 const grpc_slice& key() const { return key_; }
244 const grpc_slice& value() const { return value_; }
245 UserData* user_data() { return &user_data_; }
248 void Ref(const char* file, int line) {
249 grpc_mdelem_trace_ref(this, key_, value_, RefValue(), file, line);
252 bool Unref(const char* file, int line) {
253 grpc_mdelem_trace_unref(this, key_, value_, RefValue(), file, line);
256 #endif // ifndef NDEBUG
258 /* we can assume the ref count is >= 1 as the application is calling
259 this function - meaning that no adjustment to mdtab_free is necessary,
260 simplifying the logic here to be just an atomic increment */
261 refcnt_.FetchAdd(1, MemoryOrder::RELAXED);
264 const intptr_t prior = refcnt_.FetchSub(1, MemoryOrder::ACQ_REL);
265 GPR_DEBUG_ASSERT(prior > 0);
270 intptr_t RefValue() { return refcnt_.Load(MemoryOrder::RELAXED); }
272 /* must be byte compatible with grpc_mdelem_data */
276 /* private only data */
277 grpc_core::Atomic<intptr_t> refcnt_;
282 } // namespace grpc_core
285 #define GRPC_MDELEM_REF(s) grpc_mdelem_ref((s), __FILE__, __LINE__)
286 inline grpc_mdelem grpc_mdelem_ref(grpc_mdelem gmd, const char* file,
288 #else // ifndef NDEBUG
289 #define GRPC_MDELEM_REF(s) grpc_mdelem_ref((s))
290 inline grpc_mdelem grpc_mdelem_ref(grpc_mdelem gmd) {
291 #endif // ifndef NDEBUG
292 switch (GRPC_MDELEM_STORAGE(gmd)) {
293 case GRPC_MDELEM_STORAGE_EXTERNAL:
294 case GRPC_MDELEM_STORAGE_STATIC:
296 case GRPC_MDELEM_STORAGE_INTERNED: {
298 reinterpret_cast<grpc_core::InternedMetadata*> GRPC_MDELEM_DATA(gmd);
299 /* use C assert to have this removed in opt builds */
307 case GRPC_MDELEM_STORAGE_ALLOCATED: {
309 reinterpret_cast<grpc_core::AllocatedMetadata*> GRPC_MDELEM_DATA(gmd);
322 #define GRPC_MDELEM_UNREF(s) grpc_mdelem_unref((s), __FILE__, __LINE__)
323 void grpc_mdelem_do_unref(grpc_mdelem gmd, const char* file, int line);
324 inline void grpc_mdelem_unref(grpc_mdelem gmd, const char* file, int line) {
326 #define GRPC_MDELEM_UNREF(s) grpc_mdelem_unref((s))
327 void grpc_mdelem_do_unref(grpc_mdelem gmd);
328 inline void grpc_mdelem_unref(grpc_mdelem gmd) {
330 switch (GRPC_MDELEM_STORAGE(gmd)) {
331 case GRPC_MDELEM_STORAGE_EXTERNAL:
332 case GRPC_MDELEM_STORAGE_STATIC:
334 case GRPC_MDELEM_STORAGE_INTERNED:
335 case GRPC_MDELEM_STORAGE_ALLOCATED:
337 grpc_mdelem_do_unref(gmd, file, line);
339 grpc_mdelem_do_unref(gmd);
345 #define GRPC_MDNULL GRPC_MAKE_MDELEM(NULL, GRPC_MDELEM_STORAGE_EXTERNAL)
346 #define GRPC_MDISNULL(md) (GRPC_MDELEM_DATA(md) == NULL)
348 /* We add 32 bytes of padding as per RFC-7540 section 6.5.2. */
349 #define GRPC_MDELEM_LENGTH(e) \
350 (GRPC_SLICE_LENGTH(GRPC_MDKEY((e))) + GRPC_SLICE_LENGTH(GRPC_MDVALUE((e))) + \
353 #define GRPC_MDSTR_KV_HASH(k_hash, v_hash) (GPR_ROTL((k_hash), 2) ^ (v_hash))
355 void grpc_mdctx_global_init(void);
356 void grpc_mdctx_global_shutdown();
358 #endif /* GRPC_CORE_LIB_TRANSPORT_METADATA_H */