• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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