1 /*
2 *
3 * Copyright 2015 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 #include <grpc/support/port_platform.h>
20
21 #include "src/core/lib/transport/metadata.h"
22
23 #include <assert.h>
24 #include <inttypes.h>
25 #include <stddef.h>
26 #include <string.h>
27
28 #include <grpc/compression.h>
29 #include <grpc/grpc.h>
30 #include <grpc/support/alloc.h>
31 #include <grpc/support/atm.h>
32 #include <grpc/support/log.h>
33 #include <grpc/support/string_util.h>
34 #include <grpc/support/time.h>
35
36 #include "src/core/lib/gpr/murmur_hash.h"
37 #include "src/core/lib/gpr/string.h"
38 #include "src/core/lib/iomgr/iomgr_internal.h"
39 #include "src/core/lib/profiling/timers.h"
40 #include "src/core/lib/slice/slice_internal.h"
41 #include "src/core/lib/slice/slice_string_helpers.h"
42 #include "src/core/lib/transport/static_metadata.h"
43
44 using grpc_core::AllocatedMetadata;
45 using grpc_core::InternedMetadata;
46 using grpc_core::StaticMetadata;
47 using grpc_core::UserData;
48
49 /* There are two kinds of mdelem and mdstr instances.
50 * Static instances are declared in static_metadata.{h,c} and
51 * are initialized by grpc_mdctx_global_init().
52 * Dynamic instances are stored in hash tables on grpc_mdctx, and are backed
53 * by internal_string and internal_element structures.
54 * Internal helper functions here-in (is_mdstr_static, is_mdelem_static) are
55 * used to determine which kind of element a pointer refers to.
56 */
57
58 grpc_core::DebugOnlyTraceFlag grpc_trace_metadata(false, "metadata");
59
60 #ifndef NDEBUG
61 #define DEBUG_ARGS , const char *file, int line
62 #define FWD_DEBUG_ARGS file, line
63
grpc_mdelem_trace_ref(void * md,const grpc_slice & key,const grpc_slice & value,intptr_t refcnt,const char * file,int line)64 void grpc_mdelem_trace_ref(void* md, const grpc_slice& key,
65 const grpc_slice& value, intptr_t refcnt,
66 const char* file, int line) {
67 if (grpc_trace_metadata.enabled()) {
68 char* key_str = grpc_slice_to_c_string(key);
69 char* value_str = grpc_slice_to_c_string(value);
70 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
71 "mdelem REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", md,
72 refcnt, refcnt + 1, key_str, value_str);
73 gpr_free(key_str);
74 gpr_free(value_str);
75 }
76 }
77
grpc_mdelem_trace_unref(void * md,const grpc_slice & key,const grpc_slice & value,intptr_t refcnt,const char * file,int line)78 void grpc_mdelem_trace_unref(void* md, const grpc_slice& key,
79 const grpc_slice& value, intptr_t refcnt,
80 const char* file, int line) {
81 if (grpc_trace_metadata.enabled()) {
82 char* key_str = grpc_slice_to_c_string(key);
83 char* value_str = grpc_slice_to_c_string(value);
84 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
85 "mdelem UNREF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", md,
86 refcnt, refcnt - 1, key_str, value_str);
87 gpr_free(key_str);
88 gpr_free(value_str);
89 }
90 }
91
92 #else // ifndef NDEBUG
93 #define DEBUG_ARGS
94 #define FWD_DEBUG_ARGS
95 #endif // ifndef NDEBUG
96
97 #define INITIAL_SHARD_CAPACITY 8
98 #define LOG2_SHARD_COUNT 4
99 #define SHARD_COUNT ((size_t)(1 << LOG2_SHARD_COUNT))
100
101 #define TABLE_IDX(hash, capacity) (((hash) >> (LOG2_SHARD_COUNT)) % (capacity))
102 #define SHARD_IDX(hash) ((hash) & ((1 << (LOG2_SHARD_COUNT)) - 1))
103
HashInit()104 void StaticMetadata::HashInit() {
105 uint32_t k_hash = grpc_slice_hash_internal(kv_.key);
106 uint32_t v_hash = grpc_slice_hash_internal(kv_.value);
107 hash_ = GRPC_MDSTR_KV_HASH(k_hash, v_hash);
108 }
109
AllocatedMetadata(const grpc_slice & key,const grpc_slice & value)110 AllocatedMetadata::AllocatedMetadata(const grpc_slice& key,
111 const grpc_slice& value)
112 : RefcountedMdBase(grpc_slice_ref_internal(key),
113 grpc_slice_ref_internal(value)) {
114 #ifndef NDEBUG
115 TraceAtStart("ALLOC_MD");
116 #endif
117 }
118
AllocatedMetadata(const grpc_slice & key,const grpc_slice & value,const NoRefKey *)119 AllocatedMetadata::AllocatedMetadata(const grpc_slice& key,
120 const grpc_slice& value, const NoRefKey*)
121 : RefcountedMdBase(key, grpc_slice_ref_internal(value)) {
122 #ifndef NDEBUG
123 TraceAtStart("ALLOC_MD_NOREF_KEY");
124 #endif
125 }
126
AllocatedMetadata(const grpc_core::ManagedMemorySlice & key,const grpc_core::UnmanagedMemorySlice & value)127 AllocatedMetadata::AllocatedMetadata(
128 const grpc_core::ManagedMemorySlice& key,
129 const grpc_core::UnmanagedMemorySlice& value)
130 : RefcountedMdBase(key, value) {
131 #ifndef NDEBUG
132 TraceAtStart("ALLOC_MD_NOREF_KEY_VAL");
133 #endif
134 }
135
AllocatedMetadata(const grpc_core::ExternallyManagedSlice & key,const grpc_core::UnmanagedMemorySlice & value)136 AllocatedMetadata::AllocatedMetadata(
137 const grpc_core::ExternallyManagedSlice& key,
138 const grpc_core::UnmanagedMemorySlice& value)
139 : RefcountedMdBase(key, value) {
140 #ifndef NDEBUG
141 TraceAtStart("ALLOC_MD_NOREF_KEY_VAL");
142 #endif
143 }
144
~AllocatedMetadata()145 AllocatedMetadata::~AllocatedMetadata() {
146 grpc_slice_unref_internal(key());
147 grpc_slice_unref_internal(value());
148 void* user_data = user_data_.data.Load(grpc_core::MemoryOrder::RELAXED);
149 if (user_data) {
150 destroy_user_data_func destroy_user_data =
151 user_data_.destroy_user_data.Load(grpc_core::MemoryOrder::RELAXED);
152 destroy_user_data(user_data);
153 }
154 }
155
156 #ifndef NDEBUG
TraceAtStart(const char * tag)157 void grpc_core::RefcountedMdBase::TraceAtStart(const char* tag) {
158 if (grpc_trace_metadata.enabled()) {
159 char* key_str = grpc_slice_to_c_string(key());
160 char* value_str = grpc_slice_to_c_string(value());
161 gpr_log(GPR_DEBUG, "mdelem %s:%p:%" PRIdPTR ": '%s' = '%s'", tag, this,
162 RefValue(), key_str, value_str);
163 gpr_free(key_str);
164 gpr_free(value_str);
165 }
166 }
167 #endif
168
InternedMetadata(const grpc_slice & key,const grpc_slice & value,uint32_t hash,InternedMetadata * next)169 InternedMetadata::InternedMetadata(const grpc_slice& key,
170 const grpc_slice& value, uint32_t hash,
171 InternedMetadata* next)
172 : RefcountedMdBase(grpc_slice_ref_internal(key),
173 grpc_slice_ref_internal(value), hash),
174 link_(next) {
175 #ifndef NDEBUG
176 TraceAtStart("INTERNED_MD");
177 #endif
178 }
179
InternedMetadata(const grpc_slice & key,const grpc_slice & value,uint32_t hash,InternedMetadata * next,const NoRefKey *)180 InternedMetadata::InternedMetadata(const grpc_slice& key,
181 const grpc_slice& value, uint32_t hash,
182 InternedMetadata* next, const NoRefKey*)
183 : RefcountedMdBase(key, grpc_slice_ref_internal(value), hash), link_(next) {
184 #ifndef NDEBUG
185 TraceAtStart("INTERNED_MD_NOREF_KEY");
186 #endif
187 }
188
~InternedMetadata()189 InternedMetadata::~InternedMetadata() {
190 grpc_slice_unref_internal(key());
191 grpc_slice_unref_internal(value());
192 void* user_data = user_data_.data.Load(grpc_core::MemoryOrder::RELAXED);
193 if (user_data) {
194 destroy_user_data_func destroy_user_data =
195 user_data_.destroy_user_data.Load(grpc_core::MemoryOrder::RELAXED);
196 destroy_user_data(user_data);
197 }
198 }
199
CleanupLinkedMetadata(InternedMetadata::BucketLink * head)200 size_t InternedMetadata::CleanupLinkedMetadata(
201 InternedMetadata::BucketLink* head) {
202 size_t num_freed = 0;
203 InternedMetadata::BucketLink* prev_next = head;
204 InternedMetadata *md, *next;
205
206 for (md = head->next; md; md = next) {
207 next = md->link_.next;
208 if (md->AllRefsDropped()) {
209 prev_next->next = next;
210 delete md;
211 num_freed++;
212 } else {
213 prev_next = &md->link_;
214 }
215 }
216 return num_freed;
217 }
218
219 typedef struct mdtab_shard {
220 gpr_mu mu;
221 InternedMetadata::BucketLink* elems;
222 size_t count;
223 size_t capacity;
224 /** Estimate of the number of unreferenced mdelems in the hash table.
225 This will eventually converge to the exact number, but it's instantaneous
226 accuracy is not guaranteed */
227 gpr_atm free_estimate;
228 } mdtab_shard;
229
230 static mdtab_shard g_shards[SHARD_COUNT];
231
232 static void gc_mdtab(mdtab_shard* shard);
233
grpc_mdctx_global_init(void)234 void grpc_mdctx_global_init(void) {
235 /* initialize shards */
236 for (size_t i = 0; i < SHARD_COUNT; i++) {
237 mdtab_shard* shard = &g_shards[i];
238 gpr_mu_init(&shard->mu);
239 shard->count = 0;
240 gpr_atm_no_barrier_store(&shard->free_estimate, 0);
241 shard->capacity = INITIAL_SHARD_CAPACITY;
242 shard->elems = static_cast<InternedMetadata::BucketLink*>(
243 gpr_zalloc(sizeof(*shard->elems) * shard->capacity));
244 }
245 }
246
grpc_mdctx_global_shutdown()247 void grpc_mdctx_global_shutdown() {
248 for (size_t i = 0; i < SHARD_COUNT; i++) {
249 mdtab_shard* shard = &g_shards[i];
250 gpr_mu_destroy(&shard->mu);
251 gc_mdtab(shard);
252 if (shard->count != 0) {
253 gpr_log(GPR_ERROR, "WARNING: %" PRIuPTR " metadata elements were leaked",
254 shard->count);
255 for (size_t i = 0; i < shard->capacity; i++) {
256 for (InternedMetadata* md = shard->elems[i].next; md;
257 md = md->bucket_next()) {
258 char* key_str = grpc_slice_to_c_string(md->key());
259 char* value_str = grpc_slice_to_c_string(md->value());
260 gpr_log(GPR_ERROR, "mdelem '%s' = '%s'", key_str, value_str);
261 gpr_free(key_str);
262 gpr_free(value_str);
263 }
264 }
265 if (grpc_iomgr_abort_on_leaks()) {
266 abort();
267 }
268 }
269 // For ASAN builds, we don't want to crash here, because that will
270 // prevent ASAN from providing leak detection information, which is
271 // far more useful than this simple assertion.
272 #ifndef GRPC_ASAN_ENABLED
273 GPR_DEBUG_ASSERT(shard->count == 0);
274 #endif
275 gpr_free(shard->elems);
276 }
277 }
278
279 #ifndef NDEBUG
is_mdelem_static(grpc_mdelem e)280 static int is_mdelem_static(grpc_mdelem e) {
281 return reinterpret_cast<grpc_core::StaticMetadata*>(GRPC_MDELEM_DATA(e)) >=
282 &grpc_static_mdelem_table()[0] &&
283 reinterpret_cast<grpc_core::StaticMetadata*>(GRPC_MDELEM_DATA(e)) <
284 &grpc_static_mdelem_table()[GRPC_STATIC_MDELEM_COUNT];
285 }
286 #endif
287
RefWithShardLocked(mdtab_shard * shard)288 void InternedMetadata::RefWithShardLocked(mdtab_shard* shard) {
289 #ifndef NDEBUG
290 if (grpc_trace_metadata.enabled()) {
291 char* key_str = grpc_slice_to_c_string(key());
292 char* value_str = grpc_slice_to_c_string(value());
293 intptr_t value = RefValue();
294 gpr_log(__FILE__, __LINE__, GPR_LOG_SEVERITY_DEBUG,
295 "mdelem REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", this,
296 value, value + 1, key_str, value_str);
297 gpr_free(key_str);
298 gpr_free(value_str);
299 }
300 #endif
301 if (FirstRef()) {
302 gpr_atm_no_barrier_fetch_add(&shard->free_estimate, -1);
303 }
304 }
305
gc_mdtab(mdtab_shard * shard)306 static void gc_mdtab(mdtab_shard* shard) {
307 GPR_TIMER_SCOPE("gc_mdtab", 0);
308 size_t num_freed = 0;
309 for (size_t i = 0; i < shard->capacity; ++i) {
310 intptr_t freed = InternedMetadata::CleanupLinkedMetadata(&shard->elems[i]);
311 num_freed += freed;
312 shard->count -= freed;
313 }
314 gpr_atm_no_barrier_fetch_add(&shard->free_estimate,
315 -static_cast<intptr_t>(num_freed));
316 }
317
grow_mdtab(mdtab_shard * shard)318 static void grow_mdtab(mdtab_shard* shard) {
319 GPR_TIMER_SCOPE("grow_mdtab", 0);
320
321 size_t capacity = shard->capacity * 2;
322 size_t i;
323 InternedMetadata::BucketLink* mdtab;
324 InternedMetadata *md, *next;
325 uint32_t hash;
326
327 mdtab = static_cast<InternedMetadata::BucketLink*>(
328 gpr_zalloc(sizeof(InternedMetadata::BucketLink) * capacity));
329
330 for (i = 0; i < shard->capacity; i++) {
331 for (md = shard->elems[i].next; md; md = next) {
332 size_t idx;
333 hash = md->hash();
334 next = md->bucket_next();
335 idx = TABLE_IDX(hash, capacity);
336 md->set_bucket_next(mdtab[idx].next);
337 mdtab[idx].next = md;
338 }
339 }
340 gpr_free(shard->elems);
341 shard->elems = mdtab;
342 shard->capacity = capacity;
343 }
344
rehash_mdtab(mdtab_shard * shard)345 static void rehash_mdtab(mdtab_shard* shard) {
346 if (gpr_atm_no_barrier_load(&shard->free_estimate) >
347 static_cast<gpr_atm>(shard->capacity / 4)) {
348 gc_mdtab(shard);
349 } else {
350 grow_mdtab(shard);
351 }
352 }
353
354 template <bool key_definitely_static, bool value_definitely_static = false>
355 static grpc_mdelem md_create_maybe_static(const grpc_slice& key,
356 const grpc_slice& value);
357 template <bool key_definitely_static>
358 static grpc_mdelem md_create_must_intern(const grpc_slice& key,
359 const grpc_slice& value,
360 uint32_t hash);
361
362 template <bool key_definitely_static, bool value_definitely_static = false>
md_create(const grpc_slice & key,const grpc_slice & value,grpc_mdelem_data * compatible_external_backing_store)363 static grpc_mdelem md_create(
364 const grpc_slice& key, const grpc_slice& value,
365 grpc_mdelem_data* compatible_external_backing_store) {
366 // Ensure slices are, in fact, static if we claimed they were.
367 GPR_DEBUG_ASSERT(!key_definitely_static ||
368 GRPC_IS_STATIC_METADATA_STRING(key));
369 GPR_DEBUG_ASSERT(!value_definitely_static ||
370 GRPC_IS_STATIC_METADATA_STRING(value));
371 const bool key_is_interned =
372 key_definitely_static || grpc_slice_is_interned(key);
373 const bool value_is_interned =
374 value_definitely_static || grpc_slice_is_interned(value);
375 // External storage if either slice is not interned and the caller already
376 // created a backing store. If no backing store, we allocate one.
377 if (!key_is_interned || !value_is_interned) {
378 if (compatible_external_backing_store != nullptr) {
379 // Caller provided backing store.
380 return GRPC_MAKE_MDELEM(compatible_external_backing_store,
381 GRPC_MDELEM_STORAGE_EXTERNAL);
382 } else {
383 // We allocate backing store.
384 return key_definitely_static
385 ? GRPC_MAKE_MDELEM(
386 new AllocatedMetadata(
387 key, value,
388 static_cast<const AllocatedMetadata::NoRefKey*>(
389 nullptr)),
390 GRPC_MDELEM_STORAGE_ALLOCATED)
391 : GRPC_MAKE_MDELEM(new AllocatedMetadata(key, value),
392 GRPC_MDELEM_STORAGE_ALLOCATED);
393 }
394 }
395 return md_create_maybe_static<key_definitely_static, value_definitely_static>(
396 key, value);
397 }
398
399 template <bool key_definitely_static, bool value_definitely_static>
md_create_maybe_static(const grpc_slice & key,const grpc_slice & value)400 static grpc_mdelem md_create_maybe_static(const grpc_slice& key,
401 const grpc_slice& value) {
402 // Ensure slices are, in fact, static if we claimed they were.
403 GPR_DEBUG_ASSERT(!key_definitely_static ||
404 GRPC_IS_STATIC_METADATA_STRING(key));
405 GPR_DEBUG_ASSERT(!value_definitely_static ||
406 GRPC_IS_STATIC_METADATA_STRING(value));
407 GPR_DEBUG_ASSERT(key.refcount != nullptr);
408 GPR_DEBUG_ASSERT(value.refcount != nullptr);
409
410 const bool key_is_static_mdstr =
411 key_definitely_static ||
412 key.refcount->GetType() == grpc_slice_refcount::Type::STATIC;
413 const bool value_is_static_mdstr =
414 value_definitely_static ||
415 value.refcount->GetType() == grpc_slice_refcount::Type::STATIC;
416
417 const intptr_t kidx = GRPC_STATIC_METADATA_INDEX(key);
418
419 // Not all static slice input yields a statically stored metadata element.
420 if (key_is_static_mdstr && value_is_static_mdstr) {
421 grpc_mdelem static_elem = grpc_static_mdelem_for_static_strings(
422 kidx, GRPC_STATIC_METADATA_INDEX(value));
423 if (!GRPC_MDISNULL(static_elem)) {
424 return static_elem;
425 }
426 }
427
428 uint32_t khash = key_definitely_static
429 ? grpc_static_metadata_hash_values[kidx]
430 : grpc_slice_hash_refcounted(key);
431
432 uint32_t hash = GRPC_MDSTR_KV_HASH(khash, grpc_slice_hash_refcounted(value));
433 return md_create_must_intern<key_definitely_static>(key, value, hash);
434 }
435
436 template <bool key_definitely_static>
md_create_must_intern(const grpc_slice & key,const grpc_slice & value,uint32_t hash)437 static grpc_mdelem md_create_must_intern(const grpc_slice& key,
438 const grpc_slice& value,
439 uint32_t hash) {
440 // Here, we know both key and value are both at least interned, and both
441 // possibly static. We know that anything inside the shared interned table is
442 // also at least interned (and maybe static). Note that equality for a static
443 // and interned slice implies that they are both the same exact slice.
444 // The same applies to a pair of interned slices, or a pair of static slices.
445 // Rather than run the full equality check, we can therefore just do a pointer
446 // comparison of the refcounts.
447 InternedMetadata* md;
448 mdtab_shard* shard = &g_shards[SHARD_IDX(hash)];
449 size_t idx;
450
451 GPR_TIMER_SCOPE("grpc_mdelem_from_metadata_strings", 0);
452
453 gpr_mu_lock(&shard->mu);
454
455 idx = TABLE_IDX(hash, shard->capacity);
456 /* search for an existing pair */
457 for (md = shard->elems[idx].next; md; md = md->bucket_next()) {
458 if (grpc_slice_static_interned_equal(key, md->key()) &&
459 grpc_slice_static_interned_equal(value, md->value())) {
460 md->RefWithShardLocked(shard);
461 gpr_mu_unlock(&shard->mu);
462 return GRPC_MAKE_MDELEM(md, GRPC_MDELEM_STORAGE_INTERNED);
463 }
464 }
465
466 /* not found: create a new pair */
467 md = key_definitely_static
468 ? new InternedMetadata(
469 key, value, hash, shard->elems[idx].next,
470 static_cast<const InternedMetadata::NoRefKey*>(nullptr))
471 : new InternedMetadata(key, value, hash, shard->elems[idx].next);
472 shard->elems[idx].next = md;
473 shard->count++;
474
475 if (shard->count > shard->capacity * 2) {
476 rehash_mdtab(shard);
477 }
478
479 gpr_mu_unlock(&shard->mu);
480
481 return GRPC_MAKE_MDELEM(md, GRPC_MDELEM_STORAGE_INTERNED);
482 }
483
grpc_mdelem_create(const grpc_slice & key,const grpc_slice & value,grpc_mdelem_data * compatible_external_backing_store)484 grpc_mdelem grpc_mdelem_create(
485 const grpc_slice& key, const grpc_slice& value,
486 grpc_mdelem_data* compatible_external_backing_store) {
487 return md_create<false>(key, value, compatible_external_backing_store);
488 }
489
grpc_mdelem_create(const grpc_core::StaticMetadataSlice & key,const grpc_slice & value,grpc_mdelem_data * compatible_external_backing_store)490 grpc_mdelem grpc_mdelem_create(
491 const grpc_core::StaticMetadataSlice& key, const grpc_slice& value,
492 grpc_mdelem_data* compatible_external_backing_store) {
493 return md_create<true>(key, value, compatible_external_backing_store);
494 }
495
496 /* Create grpc_mdelem from provided slices. We specify via template parameter
497 whether we know that the input key is static or not. If it is, we short
498 circuit various comparisons and a no-op unref. */
499 template <bool key_definitely_static>
md_from_slices(const grpc_slice & key,const grpc_slice & value)500 static grpc_mdelem md_from_slices(const grpc_slice& key,
501 const grpc_slice& value) {
502 // Ensure key is, in fact, static if we claimed it was.
503 GPR_DEBUG_ASSERT(!key_definitely_static ||
504 GRPC_IS_STATIC_METADATA_STRING(key));
505 grpc_mdelem out = md_create<key_definitely_static>(key, value, nullptr);
506 if (!key_definitely_static) {
507 grpc_slice_unref_internal(key);
508 }
509 grpc_slice_unref_internal(value);
510 return out;
511 }
512
grpc_mdelem_from_slices(const grpc_slice & key,const grpc_slice & value)513 grpc_mdelem grpc_mdelem_from_slices(const grpc_slice& key,
514 const grpc_slice& value) {
515 return md_from_slices</*key_definitely_static=*/false>(key, value);
516 }
517
grpc_mdelem_from_slices(const grpc_core::StaticMetadataSlice & key,const grpc_slice & value)518 grpc_mdelem grpc_mdelem_from_slices(const grpc_core::StaticMetadataSlice& key,
519 const grpc_slice& value) {
520 return md_from_slices</*key_definitely_static=*/true>(key, value);
521 }
522
grpc_mdelem_from_slices(const grpc_core::StaticMetadataSlice & key,const grpc_core::StaticMetadataSlice & value)523 grpc_mdelem grpc_mdelem_from_slices(
524 const grpc_core::StaticMetadataSlice& key,
525 const grpc_core::StaticMetadataSlice& value) {
526 grpc_mdelem out = md_create_maybe_static<true, true>(key, value);
527 return out;
528 }
529
grpc_mdelem_from_slices(const grpc_core::StaticMetadataSlice & key,const grpc_core::ManagedMemorySlice & value)530 grpc_mdelem grpc_mdelem_from_slices(
531 const grpc_core::StaticMetadataSlice& key,
532 const grpc_core::ManagedMemorySlice& value) {
533 // TODO(arjunroy): We can save the unref if md_create_maybe_static ended up
534 // creating a new interned metadata. But otherwise - we need this here.
535 grpc_mdelem out = md_create_maybe_static<true>(key, value);
536 grpc_slice_unref_internal(value);
537 return out;
538 }
539
grpc_mdelem_from_slices(const grpc_core::ManagedMemorySlice & key,const grpc_core::ManagedMemorySlice & value)540 grpc_mdelem grpc_mdelem_from_slices(
541 const grpc_core::ManagedMemorySlice& key,
542 const grpc_core::ManagedMemorySlice& value) {
543 grpc_mdelem out = md_create_maybe_static<false>(key, value);
544 // TODO(arjunroy): We can save the unref if md_create_maybe_static ended up
545 // creating a new interned metadata. But otherwise - we need this here.
546 grpc_slice_unref_internal(key);
547 grpc_slice_unref_internal(value);
548 return out;
549 }
550
grpc_mdelem_from_grpc_metadata(grpc_metadata * metadata)551 grpc_mdelem grpc_mdelem_from_grpc_metadata(grpc_metadata* metadata) {
552 bool changed = false;
553 grpc_slice key_slice =
554 grpc_slice_maybe_static_intern(metadata->key, &changed);
555 grpc_slice value_slice =
556 grpc_slice_maybe_static_intern(metadata->value, &changed);
557 return grpc_mdelem_create(
558 key_slice, value_slice,
559 changed ? nullptr : reinterpret_cast<grpc_mdelem_data*>(metadata));
560 }
561
get_user_data(UserData * user_data,void (* destroy_func)(void *))562 static void* get_user_data(UserData* user_data, void (*destroy_func)(void*)) {
563 if (user_data->destroy_user_data.Load(grpc_core::MemoryOrder::ACQUIRE) ==
564 destroy_func) {
565 return user_data->data.Load(grpc_core::MemoryOrder::RELAXED);
566 } else {
567 return nullptr;
568 }
569 }
570
grpc_mdelem_get_user_data(grpc_mdelem md,void (* destroy_func)(void *))571 void* grpc_mdelem_get_user_data(grpc_mdelem md, void (*destroy_func)(void*)) {
572 switch (GRPC_MDELEM_STORAGE(md)) {
573 case GRPC_MDELEM_STORAGE_EXTERNAL:
574 return nullptr;
575 case GRPC_MDELEM_STORAGE_STATIC:
576 return reinterpret_cast<void*>(
577 grpc_static_mdelem_user_data
578 [reinterpret_cast<grpc_core::StaticMetadata*>(
579 GRPC_MDELEM_DATA(md)) -
580 grpc_static_mdelem_table()]);
581 case GRPC_MDELEM_STORAGE_ALLOCATED: {
582 auto* am = reinterpret_cast<AllocatedMetadata*>(GRPC_MDELEM_DATA(md));
583 return get_user_data(am->user_data(), destroy_func);
584 }
585 case GRPC_MDELEM_STORAGE_INTERNED: {
586 auto* im = reinterpret_cast<InternedMetadata*> GRPC_MDELEM_DATA(md);
587 return get_user_data(im->user_data(), destroy_func);
588 }
589 }
590 GPR_UNREACHABLE_CODE(return nullptr);
591 }
592
set_user_data(UserData * ud,void (* destroy_func)(void *),void * data)593 static void* set_user_data(UserData* ud, void (*destroy_func)(void*),
594 void* data) {
595 GPR_ASSERT((data == nullptr) == (destroy_func == nullptr));
596 grpc_core::ReleasableMutexLock lock(&ud->mu_user_data);
597 if (ud->destroy_user_data.Load(grpc_core::MemoryOrder::RELAXED)) {
598 /* user data can only be set once */
599 lock.Release();
600 if (destroy_func != nullptr) {
601 destroy_func(data);
602 }
603 return ud->data.Load(grpc_core::MemoryOrder::RELAXED);
604 }
605 ud->data.Store(data, grpc_core::MemoryOrder::RELAXED);
606 ud->destroy_user_data.Store(destroy_func, grpc_core::MemoryOrder::RELEASE);
607 return data;
608 }
609
grpc_mdelem_set_user_data(grpc_mdelem md,void (* destroy_func)(void *),void * data)610 void* grpc_mdelem_set_user_data(grpc_mdelem md, void (*destroy_func)(void*),
611 void* data) {
612 switch (GRPC_MDELEM_STORAGE(md)) {
613 case GRPC_MDELEM_STORAGE_EXTERNAL:
614 destroy_func(data);
615 return nullptr;
616 case GRPC_MDELEM_STORAGE_STATIC:
617 destroy_func(data);
618 return reinterpret_cast<void*>(
619 grpc_static_mdelem_user_data
620 [reinterpret_cast<grpc_core::StaticMetadata*>(
621 GRPC_MDELEM_DATA(md)) -
622 grpc_static_mdelem_table()]);
623 case GRPC_MDELEM_STORAGE_ALLOCATED: {
624 auto* am = reinterpret_cast<AllocatedMetadata*>(GRPC_MDELEM_DATA(md));
625 return set_user_data(am->user_data(), destroy_func, data);
626 }
627 case GRPC_MDELEM_STORAGE_INTERNED: {
628 auto* im = reinterpret_cast<InternedMetadata*> GRPC_MDELEM_DATA(md);
629 GPR_DEBUG_ASSERT(!is_mdelem_static(md));
630 return set_user_data(im->user_data(), destroy_func, data);
631 }
632 }
633 GPR_UNREACHABLE_CODE(return nullptr);
634 }
635
grpc_mdelem_eq(grpc_mdelem a,grpc_mdelem b)636 bool grpc_mdelem_eq(grpc_mdelem a, grpc_mdelem b) {
637 if (a.payload == b.payload) return true;
638 if (GRPC_MDELEM_IS_INTERNED(a) && GRPC_MDELEM_IS_INTERNED(b)) return false;
639 if (GRPC_MDISNULL(a) || GRPC_MDISNULL(b)) return false;
640 return grpc_slice_eq(GRPC_MDKEY(a), GRPC_MDKEY(b)) &&
641 grpc_slice_eq(GRPC_MDVALUE(a), GRPC_MDVALUE(b));
642 }
643
note_disposed_interned_metadata(uint32_t hash)644 static void note_disposed_interned_metadata(uint32_t hash) {
645 mdtab_shard* shard = &g_shards[SHARD_IDX(hash)];
646 gpr_atm_no_barrier_fetch_add(&shard->free_estimate, 1);
647 }
648
grpc_mdelem_do_unref(grpc_mdelem gmd DEBUG_ARGS)649 void grpc_mdelem_do_unref(grpc_mdelem gmd DEBUG_ARGS) {
650 switch (GRPC_MDELEM_STORAGE(gmd)) {
651 case GRPC_MDELEM_STORAGE_EXTERNAL:
652 case GRPC_MDELEM_STORAGE_STATIC:
653 return;
654 case GRPC_MDELEM_STORAGE_INTERNED: {
655 auto* md = reinterpret_cast<InternedMetadata*> GRPC_MDELEM_DATA(gmd);
656 uint32_t hash = md->hash();
657 if (GPR_UNLIKELY(md->Unref(FWD_DEBUG_ARGS))) {
658 /* once the refcount hits zero, some other thread can come along and
659 free md at any time: it's unsafe from this point on to access it */
660 note_disposed_interned_metadata(hash);
661 }
662 break;
663 }
664 case GRPC_MDELEM_STORAGE_ALLOCATED: {
665 auto* md = reinterpret_cast<AllocatedMetadata*> GRPC_MDELEM_DATA(gmd);
666 if (GPR_UNLIKELY(md->Unref(FWD_DEBUG_ARGS))) {
667 delete md;
668 }
669 break;
670 }
671 }
672 }
673
grpc_mdelem_on_final_unref(grpc_mdelem_data_storage storage,void * ptr,uint32_t hash DEBUG_ARGS)674 void grpc_mdelem_on_final_unref(grpc_mdelem_data_storage storage, void* ptr,
675 uint32_t hash DEBUG_ARGS) {
676 #ifndef NDEBUG
677 (void)file;
678 (void)line;
679 #endif
680 switch (storage) {
681 case GRPC_MDELEM_STORAGE_EXTERNAL:
682 case GRPC_MDELEM_STORAGE_STATIC:
683 return;
684 case GRPC_MDELEM_STORAGE_INTERNED: {
685 note_disposed_interned_metadata(hash);
686 break;
687 }
688 case GRPC_MDELEM_STORAGE_ALLOCATED: {
689 delete reinterpret_cast<AllocatedMetadata*>(ptr);
690 break;
691 }
692 }
693 }
694