|  | @@ -41,6 +41,10 @@
 | 
	
		
			
				|  |  |  #include "src/core/lib/slice/slice_string_helpers.h"
 | 
	
		
			
				|  |  |  #include "src/core/lib/transport/static_metadata.h"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +using grpc_core::AllocatedMetadata;
 | 
	
		
			
				|  |  | +using grpc_core::InternedMetadata;
 | 
	
		
			
				|  |  | +using grpc_core::UserData;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  /* There are two kinds of mdelem and mdstr instances.
 | 
	
		
			
				|  |  |   * Static instances are declared in static_metadata.{h,c} and
 | 
	
		
			
				|  |  |   * are initialized by grpc_mdctx_global_init().
 | 
	
	
		
			
				|  | @@ -54,13 +58,40 @@ grpc_core::DebugOnlyTraceFlag grpc_trace_metadata(false, "metadata");
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #ifndef NDEBUG
 | 
	
		
			
				|  |  |  #define DEBUG_ARGS , const char *file, int line
 | 
	
		
			
				|  |  | -#define FWD_DEBUG_ARGS , file, line
 | 
	
		
			
				|  |  | -#define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s), __FILE__, __LINE__)
 | 
	
		
			
				|  |  | -#else
 | 
	
		
			
				|  |  | +#define FWD_DEBUG_ARGS file, line
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void grpc_mdelem_trace_ref(void* md, const grpc_slice& key,
 | 
	
		
			
				|  |  | +                           const grpc_slice& value, intptr_t refcnt,
 | 
	
		
			
				|  |  | +                           const char* file, int line) {
 | 
	
		
			
				|  |  | +  if (grpc_trace_metadata.enabled()) {
 | 
	
		
			
				|  |  | +    char* key_str = grpc_slice_to_c_string(key);
 | 
	
		
			
				|  |  | +    char* value_str = grpc_slice_to_c_string(value);
 | 
	
		
			
				|  |  | +    gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
 | 
	
		
			
				|  |  | +            "ELM   REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", md, refcnt,
 | 
	
		
			
				|  |  | +            refcnt + 1, key_str, value_str);
 | 
	
		
			
				|  |  | +    gpr_free(key_str);
 | 
	
		
			
				|  |  | +    gpr_free(value_str);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void grpc_mdelem_trace_unref(void* md, const grpc_slice& key,
 | 
	
		
			
				|  |  | +                             const grpc_slice& value, intptr_t refcnt,
 | 
	
		
			
				|  |  | +                             const char* file, int line) {
 | 
	
		
			
				|  |  | +  if (grpc_trace_metadata.enabled()) {
 | 
	
		
			
				|  |  | +    char* key_str = grpc_slice_to_c_string(key);
 | 
	
		
			
				|  |  | +    char* value_str = grpc_slice_to_c_string(value);
 | 
	
		
			
				|  |  | +    gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
 | 
	
		
			
				|  |  | +            "ELM   UNREF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", md,
 | 
	
		
			
				|  |  | +            refcnt, refcnt - 1, key_str, value_str);
 | 
	
		
			
				|  |  | +    gpr_free(key_str);
 | 
	
		
			
				|  |  | +    gpr_free(value_str);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#else  // ifndef NDEBUG
 | 
	
		
			
				|  |  |  #define DEBUG_ARGS
 | 
	
		
			
				|  |  |  #define FWD_DEBUG_ARGS
 | 
	
		
			
				|  |  | -#define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s))
 | 
	
		
			
				|  |  | -#endif
 | 
	
		
			
				|  |  | +#endif  // ifndef NDEBUG
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #define INITIAL_SHARD_CAPACITY 8
 | 
	
		
			
				|  |  |  #define LOG2_SHARD_COUNT 4
 | 
	
	
		
			
				|  | @@ -69,43 +100,87 @@ grpc_core::DebugOnlyTraceFlag grpc_trace_metadata(false, "metadata");
 | 
	
		
			
				|  |  |  #define TABLE_IDX(hash, capacity) (((hash) >> (LOG2_SHARD_COUNT)) % (capacity))
 | 
	
		
			
				|  |  |  #define SHARD_IDX(hash) ((hash) & ((1 << (LOG2_SHARD_COUNT)) - 1))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -typedef void (*destroy_user_data_func)(void* user_data);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -struct UserData {
 | 
	
		
			
				|  |  | -  gpr_mu mu_user_data;
 | 
	
		
			
				|  |  | -  gpr_atm destroy_user_data;
 | 
	
		
			
				|  |  | -  gpr_atm user_data;
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/* Shadow structure for grpc_mdelem_data for interned elements */
 | 
	
		
			
				|  |  | -typedef struct interned_metadata {
 | 
	
		
			
				|  |  | -  /* must be byte compatible with grpc_mdelem_data */
 | 
	
		
			
				|  |  | -  grpc_slice key;
 | 
	
		
			
				|  |  | -  grpc_slice value;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  /* private only data */
 | 
	
		
			
				|  |  | -  gpr_atm refcnt;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  UserData user_data;
 | 
	
		
			
				|  |  | +AllocatedMetadata::AllocatedMetadata(const grpc_slice& key,
 | 
	
		
			
				|  |  | +                                     const grpc_slice& value)
 | 
	
		
			
				|  |  | +    : key_(grpc_slice_ref_internal(key)),
 | 
	
		
			
				|  |  | +      value_(grpc_slice_ref_internal(value)),
 | 
	
		
			
				|  |  | +      refcnt_(1) {
 | 
	
		
			
				|  |  | +#ifndef NDEBUG
 | 
	
		
			
				|  |  | +  if (grpc_trace_metadata.enabled()) {
 | 
	
		
			
				|  |  | +    char* key_str = grpc_slice_to_c_string(key_);
 | 
	
		
			
				|  |  | +    char* value_str = grpc_slice_to_c_string(value_);
 | 
	
		
			
				|  |  | +    gpr_log(GPR_DEBUG, "ELM ALLOC:%p:%" PRIdPTR ": '%s' = '%s'", this,
 | 
	
		
			
				|  |  | +            RefValue(), key_str, value_str);
 | 
	
		
			
				|  |  | +    gpr_free(key_str);
 | 
	
		
			
				|  |  | +    gpr_free(value_str);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  struct interned_metadata* bucket_next;
 | 
	
		
			
				|  |  | -} interned_metadata;
 | 
	
		
			
				|  |  | +AllocatedMetadata::~AllocatedMetadata() {
 | 
	
		
			
				|  |  | +  grpc_slice_unref_internal(key_);
 | 
	
		
			
				|  |  | +  grpc_slice_unref_internal(value_);
 | 
	
		
			
				|  |  | +  if (user_data_.user_data) {
 | 
	
		
			
				|  |  | +    destroy_user_data_func destroy_user_data =
 | 
	
		
			
				|  |  | +        (destroy_user_data_func)gpr_atm_no_barrier_load(
 | 
	
		
			
				|  |  | +            &user_data_.destroy_user_data);
 | 
	
		
			
				|  |  | +    destroy_user_data((void*)user_data_.user_data);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -/* Shadow structure for grpc_mdelem_data for allocated elements */
 | 
	
		
			
				|  |  | -typedef struct allocated_metadata {
 | 
	
		
			
				|  |  | -  /* must be byte compatible with grpc_mdelem_data */
 | 
	
		
			
				|  |  | -  grpc_slice key;
 | 
	
		
			
				|  |  | -  grpc_slice value;
 | 
	
		
			
				|  |  | +InternedMetadata::InternedMetadata(const grpc_slice& key,
 | 
	
		
			
				|  |  | +                                   const grpc_slice& value, uint32_t hash,
 | 
	
		
			
				|  |  | +                                   InternedMetadata* next)
 | 
	
		
			
				|  |  | +    : key_(grpc_slice_ref_internal(key)),
 | 
	
		
			
				|  |  | +      value_(grpc_slice_ref_internal(value)),
 | 
	
		
			
				|  |  | +      refcnt_(1),
 | 
	
		
			
				|  |  | +      hash_(hash),
 | 
	
		
			
				|  |  | +      link_(next) {
 | 
	
		
			
				|  |  | +#ifndef NDEBUG
 | 
	
		
			
				|  |  | +  if (grpc_trace_metadata.enabled()) {
 | 
	
		
			
				|  |  | +    char* key_str = grpc_slice_to_c_string(key_);
 | 
	
		
			
				|  |  | +    char* value_str = grpc_slice_to_c_string(value_);
 | 
	
		
			
				|  |  | +    gpr_log(GPR_DEBUG, "ELM   NEW:%p:%" PRIdPTR ": '%s' = '%s'", this,
 | 
	
		
			
				|  |  | +            RefValue(), key_str, value_str);
 | 
	
		
			
				|  |  | +    gpr_free(key_str);
 | 
	
		
			
				|  |  | +    gpr_free(value_str);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /* private only data */
 | 
	
		
			
				|  |  | -  gpr_atm refcnt;
 | 
	
		
			
				|  |  | +InternedMetadata::~InternedMetadata() {
 | 
	
		
			
				|  |  | +  grpc_slice_unref_internal(key_);
 | 
	
		
			
				|  |  | +  grpc_slice_unref_internal(value_);
 | 
	
		
			
				|  |  | +  if (user_data_.user_data) {
 | 
	
		
			
				|  |  | +    destroy_user_data_func destroy_user_data =
 | 
	
		
			
				|  |  | +        (destroy_user_data_func)gpr_atm_no_barrier_load(
 | 
	
		
			
				|  |  | +            &user_data_.destroy_user_data);
 | 
	
		
			
				|  |  | +    destroy_user_data((void*)user_data_.user_data);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  UserData user_data;
 | 
	
		
			
				|  |  | -} allocated_metadata;
 | 
	
		
			
				|  |  | +intptr_t InternedMetadata::CleanupLinkedMetadata(
 | 
	
		
			
				|  |  | +    InternedMetadata::BucketLink* head) {
 | 
	
		
			
				|  |  | +  intptr_t num_freed = 0;
 | 
	
		
			
				|  |  | +  InternedMetadata::BucketLink* prev_next = head;
 | 
	
		
			
				|  |  | +  InternedMetadata *md, *next;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  for (md = head->next; md; md = next) {
 | 
	
		
			
				|  |  | +    next = md->link_.next;
 | 
	
		
			
				|  |  | +    if (md->AllRefsDropped()) {
 | 
	
		
			
				|  |  | +      prev_next->next = next;
 | 
	
		
			
				|  |  | +      grpc_core::Delete(md);
 | 
	
		
			
				|  |  | +      num_freed++;
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      prev_next = &md->link_;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  return num_freed;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  typedef struct mdtab_shard {
 | 
	
		
			
				|  |  |    gpr_mu mu;
 | 
	
		
			
				|  |  | -  interned_metadata** elems;
 | 
	
		
			
				|  |  | +  InternedMetadata::BucketLink* elems;
 | 
	
		
			
				|  |  |    size_t count;
 | 
	
		
			
				|  |  |    size_t capacity;
 | 
	
		
			
				|  |  |    /** Estimate of the number of unreferenced mdelems in the hash table.
 | 
	
	
		
			
				|  | @@ -126,7 +201,7 @@ void grpc_mdctx_global_init(void) {
 | 
	
		
			
				|  |  |      shard->count = 0;
 | 
	
		
			
				|  |  |      gpr_atm_no_barrier_store(&shard->free_estimate, 0);
 | 
	
		
			
				|  |  |      shard->capacity = INITIAL_SHARD_CAPACITY;
 | 
	
		
			
				|  |  | -    shard->elems = static_cast<interned_metadata**>(
 | 
	
		
			
				|  |  | +    shard->elems = static_cast<InternedMetadata::BucketLink*>(
 | 
	
		
			
				|  |  |          gpr_zalloc(sizeof(*shard->elems) * shard->capacity));
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  }
 | 
	
	
		
			
				|  | @@ -154,55 +229,29 @@ static int is_mdelem_static(grpc_mdelem e) {
 | 
	
		
			
				|  |  |               &grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static void ref_md_locked(mdtab_shard* shard,
 | 
	
		
			
				|  |  | -                          interned_metadata* md DEBUG_ARGS) {
 | 
	
		
			
				|  |  | +void InternedMetadata::RefWithShardLocked(mdtab_shard* shard) {
 | 
	
		
			
				|  |  |  #ifndef NDEBUG
 | 
	
		
			
				|  |  |    if (grpc_trace_metadata.enabled()) {
 | 
	
		
			
				|  |  | -    char* key_str = grpc_slice_to_c_string(md->key);
 | 
	
		
			
				|  |  | -    char* value_str = grpc_slice_to_c_string(md->value);
 | 
	
		
			
				|  |  | -    gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
 | 
	
		
			
				|  |  | -            "ELM   REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", (void*)md,
 | 
	
		
			
				|  |  | -            gpr_atm_no_barrier_load(&md->refcnt),
 | 
	
		
			
				|  |  | -            gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str);
 | 
	
		
			
				|  |  | +    char* key_str = grpc_slice_to_c_string(key_);
 | 
	
		
			
				|  |  | +    char* value_str = grpc_slice_to_c_string(value_);
 | 
	
		
			
				|  |  | +    intptr_t value = RefValue();
 | 
	
		
			
				|  |  | +    gpr_log(__FILE__, __LINE__, GPR_LOG_SEVERITY_DEBUG,
 | 
	
		
			
				|  |  | +            "ELM   REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", this, value,
 | 
	
		
			
				|  |  | +            value + 1, key_str, value_str);
 | 
	
		
			
				|  |  |      gpr_free(key_str);
 | 
	
		
			
				|  |  |      gpr_free(value_str);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  #endif
 | 
	
		
			
				|  |  | -  if (0 == gpr_atm_no_barrier_fetch_add(&md->refcnt, 1)) {
 | 
	
		
			
				|  |  | +  if (FirstRef()) {
 | 
	
		
			
				|  |  |      gpr_atm_no_barrier_fetch_add(&shard->free_estimate, -1);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static void gc_mdtab(mdtab_shard* shard) {
 | 
	
		
			
				|  |  |    GPR_TIMER_SCOPE("gc_mdtab", 0);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  size_t i;
 | 
	
		
			
				|  |  | -  interned_metadata** prev_next;
 | 
	
		
			
				|  |  | -  interned_metadata *md, *next;
 | 
	
		
			
				|  |  | -  gpr_atm num_freed = 0;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  for (i = 0; i < shard->capacity; i++) {
 | 
	
		
			
				|  |  | -    prev_next = &shard->elems[i];
 | 
	
		
			
				|  |  | -    for (md = shard->elems[i]; md; md = next) {
 | 
	
		
			
				|  |  | -      void* user_data =
 | 
	
		
			
				|  |  | -          (void*)gpr_atm_no_barrier_load(&md->user_data.user_data);
 | 
	
		
			
				|  |  | -      next = md->bucket_next;
 | 
	
		
			
				|  |  | -      if (gpr_atm_acq_load(&md->refcnt) == 0) {
 | 
	
		
			
				|  |  | -        grpc_slice_unref_internal(md->key);
 | 
	
		
			
				|  |  | -        grpc_slice_unref_internal(md->value);
 | 
	
		
			
				|  |  | -        if (md->user_data.user_data) {
 | 
	
		
			
				|  |  | -          ((destroy_user_data_func)gpr_atm_no_barrier_load(
 | 
	
		
			
				|  |  | -              &md->user_data.destroy_user_data))(user_data);
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        gpr_mu_destroy(&md->user_data.mu_user_data);
 | 
	
		
			
				|  |  | -        gpr_free(md);
 | 
	
		
			
				|  |  | -        *prev_next = next;
 | 
	
		
			
				|  |  | -        num_freed++;
 | 
	
		
			
				|  |  | -        shard->count--;
 | 
	
		
			
				|  |  | -      } else {
 | 
	
		
			
				|  |  | -        prev_next = &md->bucket_next;
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  intptr_t num_freed = 0;
 | 
	
		
			
				|  |  | +  for (size_t i = 0; i < shard->capacity; ++i) {
 | 
	
		
			
				|  |  | +    num_freed += InternedMetadata::CleanupLinkedMetadata(&shard->elems[i]);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    gpr_atm_no_barrier_fetch_add(&shard->free_estimate, -num_freed);
 | 
	
		
			
				|  |  |  }
 | 
	
	
		
			
				|  | @@ -212,22 +261,21 @@ static void grow_mdtab(mdtab_shard* shard) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    size_t capacity = shard->capacity * 2;
 | 
	
		
			
				|  |  |    size_t i;
 | 
	
		
			
				|  |  | -  interned_metadata** mdtab;
 | 
	
		
			
				|  |  | -  interned_metadata *md, *next;
 | 
	
		
			
				|  |  | +  InternedMetadata::BucketLink* mdtab;
 | 
	
		
			
				|  |  | +  InternedMetadata *md, *next;
 | 
	
		
			
				|  |  |    uint32_t hash;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  mdtab = static_cast<interned_metadata**>(
 | 
	
		
			
				|  |  | -      gpr_zalloc(sizeof(interned_metadata*) * capacity));
 | 
	
		
			
				|  |  | +  mdtab = static_cast<InternedMetadata::BucketLink*>(
 | 
	
		
			
				|  |  | +      gpr_zalloc(sizeof(InternedMetadata::BucketLink) * capacity));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    for (i = 0; i < shard->capacity; i++) {
 | 
	
		
			
				|  |  | -    for (md = shard->elems[i]; md; md = next) {
 | 
	
		
			
				|  |  | +    for (md = shard->elems[i].next; md; md = next) {
 | 
	
		
			
				|  |  |        size_t idx;
 | 
	
		
			
				|  |  | -      hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash(md->key),
 | 
	
		
			
				|  |  | -                                grpc_slice_hash(md->value));
 | 
	
		
			
				|  |  | -      next = md->bucket_next;
 | 
	
		
			
				|  |  | +      hash = md->hash();
 | 
	
		
			
				|  |  | +      next = md->bucket_next();
 | 
	
		
			
				|  |  |        idx = TABLE_IDX(hash, capacity);
 | 
	
		
			
				|  |  | -      md->bucket_next = mdtab[idx];
 | 
	
		
			
				|  |  | -      mdtab[idx] = md;
 | 
	
		
			
				|  |  | +      md->set_bucket_next(mdtab[idx].next);
 | 
	
		
			
				|  |  | +      mdtab[idx].next = md;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    gpr_free(shard->elems);
 | 
	
	
		
			
				|  | @@ -247,34 +295,22 @@ static void rehash_mdtab(mdtab_shard* shard) {
 | 
	
		
			
				|  |  |  grpc_mdelem grpc_mdelem_create(
 | 
	
		
			
				|  |  |      const grpc_slice& key, const grpc_slice& value,
 | 
	
		
			
				|  |  |      grpc_mdelem_data* compatible_external_backing_store) {
 | 
	
		
			
				|  |  | +  // External storage if either slice is not interned and the caller already
 | 
	
		
			
				|  |  | +  // created a backing store. If no backing store, we allocate one.
 | 
	
		
			
				|  |  |    if (!grpc_slice_is_interned(key) || !grpc_slice_is_interned(value)) {
 | 
	
		
			
				|  |  |      if (compatible_external_backing_store != nullptr) {
 | 
	
		
			
				|  |  | +      // Caller provided backing store.
 | 
	
		
			
				|  |  |        return GRPC_MAKE_MDELEM(compatible_external_backing_store,
 | 
	
		
			
				|  |  |                                GRPC_MDELEM_STORAGE_EXTERNAL);
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      // We allocate backing store.
 | 
	
		
			
				|  |  | +      return GRPC_MAKE_MDELEM(grpc_core::New<AllocatedMetadata>(key, value),
 | 
	
		
			
				|  |  | +                              GRPC_MDELEM_STORAGE_ALLOCATED);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    allocated_metadata* allocated =
 | 
	
		
			
				|  |  | -        static_cast<allocated_metadata*>(gpr_malloc(sizeof(*allocated)));
 | 
	
		
			
				|  |  | -    allocated->key = grpc_slice_ref_internal(key);
 | 
	
		
			
				|  |  | -    allocated->value = grpc_slice_ref_internal(value);
 | 
	
		
			
				|  |  | -    gpr_atm_rel_store(&allocated->refcnt, 1);
 | 
	
		
			
				|  |  | -    allocated->user_data.user_data = 0;
 | 
	
		
			
				|  |  | -    allocated->user_data.destroy_user_data = 0;
 | 
	
		
			
				|  |  | -    gpr_mu_init(&allocated->user_data.mu_user_data);
 | 
	
		
			
				|  |  | -#ifndef NDEBUG
 | 
	
		
			
				|  |  | -    if (grpc_trace_metadata.enabled()) {
 | 
	
		
			
				|  |  | -      char* key_str = grpc_slice_to_c_string(allocated->key);
 | 
	
		
			
				|  |  | -      char* value_str = grpc_slice_to_c_string(allocated->value);
 | 
	
		
			
				|  |  | -      gpr_log(GPR_DEBUG, "ELM ALLOC:%p:%" PRIdPTR ": '%s' = '%s'",
 | 
	
		
			
				|  |  | -              (void*)allocated, gpr_atm_no_barrier_load(&allocated->refcnt),
 | 
	
		
			
				|  |  | -              key_str, value_str);
 | 
	
		
			
				|  |  | -      gpr_free(key_str);
 | 
	
		
			
				|  |  | -      gpr_free(value_str);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -#endif
 | 
	
		
			
				|  |  | -    return GRPC_MAKE_MDELEM(allocated, GRPC_MDELEM_STORAGE_ALLOCATED);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  // Not all static slice input yields a statically stored metadata element.
 | 
	
		
			
				|  |  | +  // It may be worth documenting why.
 | 
	
		
			
				|  |  |    if (GRPC_IS_STATIC_METADATA_STRING(key) &&
 | 
	
		
			
				|  |  |        GRPC_IS_STATIC_METADATA_STRING(value)) {
 | 
	
		
			
				|  |  |      grpc_mdelem static_elem = grpc_static_mdelem_for_static_strings(
 | 
	
	
		
			
				|  | @@ -286,7 +322,7 @@ grpc_mdelem grpc_mdelem_create(
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    uint32_t hash =
 | 
	
		
			
				|  |  |        GRPC_MDSTR_KV_HASH(grpc_slice_hash(key), grpc_slice_hash(value));
 | 
	
		
			
				|  |  | -  interned_metadata* md;
 | 
	
		
			
				|  |  | +  InternedMetadata* md;
 | 
	
		
			
				|  |  |    mdtab_shard* shard = &g_shards[SHARD_IDX(hash)];
 | 
	
		
			
				|  |  |    size_t idx;
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -296,34 +332,18 @@ grpc_mdelem grpc_mdelem_create(
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    idx = TABLE_IDX(hash, shard->capacity);
 | 
	
		
			
				|  |  |    /* search for an existing pair */
 | 
	
		
			
				|  |  | -  for (md = shard->elems[idx]; md; md = md->bucket_next) {
 | 
	
		
			
				|  |  | -    if (grpc_slice_eq(key, md->key) && grpc_slice_eq(value, md->value)) {
 | 
	
		
			
				|  |  | -      REF_MD_LOCKED(shard, md);
 | 
	
		
			
				|  |  | +  for (md = shard->elems[idx].next; md; md = md->bucket_next()) {
 | 
	
		
			
				|  |  | +    if (grpc_slice_eq(key, md->key()) && grpc_slice_eq(value, md->value())) {
 | 
	
		
			
				|  |  | +      md->RefWithShardLocked(shard);
 | 
	
		
			
				|  |  |        gpr_mu_unlock(&shard->mu);
 | 
	
		
			
				|  |  |        return GRPC_MAKE_MDELEM(md, GRPC_MDELEM_STORAGE_INTERNED);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    /* not found: create a new pair */
 | 
	
		
			
				|  |  | -  md = static_cast<interned_metadata*>(gpr_malloc(sizeof(interned_metadata)));
 | 
	
		
			
				|  |  | -  gpr_atm_rel_store(&md->refcnt, 1);
 | 
	
		
			
				|  |  | -  md->key = grpc_slice_ref_internal(key);
 | 
	
		
			
				|  |  | -  md->value = grpc_slice_ref_internal(value);
 | 
	
		
			
				|  |  | -  md->user_data.user_data = 0;
 | 
	
		
			
				|  |  | -  md->user_data.destroy_user_data = 0;
 | 
	
		
			
				|  |  | -  md->bucket_next = shard->elems[idx];
 | 
	
		
			
				|  |  | -  shard->elems[idx] = md;
 | 
	
		
			
				|  |  | -  gpr_mu_init(&md->user_data.mu_user_data);
 | 
	
		
			
				|  |  | -#ifndef NDEBUG
 | 
	
		
			
				|  |  | -  if (grpc_trace_metadata.enabled()) {
 | 
	
		
			
				|  |  | -    char* key_str = grpc_slice_to_c_string(md->key);
 | 
	
		
			
				|  |  | -    char* value_str = grpc_slice_to_c_string(md->value);
 | 
	
		
			
				|  |  | -    gpr_log(GPR_DEBUG, "ELM   NEW:%p:%" PRIdPTR ": '%s' = '%s'", (void*)md,
 | 
	
		
			
				|  |  | -            gpr_atm_no_barrier_load(&md->refcnt), key_str, value_str);
 | 
	
		
			
				|  |  | -    gpr_free(key_str);
 | 
	
		
			
				|  |  | -    gpr_free(value_str);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -#endif
 | 
	
		
			
				|  |  | +  md = grpc_core::New<InternedMetadata>(key, value, hash,
 | 
	
		
			
				|  |  | +                                        shard->elems[idx].next);
 | 
	
		
			
				|  |  | +  shard->elems[idx].next = md;
 | 
	
		
			
				|  |  |    shard->count++;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    if (shard->count > shard->capacity * 2) {
 | 
	
	
		
			
				|  | @@ -354,126 +374,6 @@ grpc_mdelem grpc_mdelem_from_grpc_metadata(grpc_metadata* metadata) {
 | 
	
		
			
				|  |  |        changed ? nullptr : reinterpret_cast<grpc_mdelem_data*>(metadata));
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -grpc_mdelem grpc_mdelem_ref(grpc_mdelem gmd DEBUG_ARGS) {
 | 
	
		
			
				|  |  | -  switch (GRPC_MDELEM_STORAGE(gmd)) {
 | 
	
		
			
				|  |  | -    case GRPC_MDELEM_STORAGE_EXTERNAL:
 | 
	
		
			
				|  |  | -    case GRPC_MDELEM_STORAGE_STATIC:
 | 
	
		
			
				|  |  | -      break;
 | 
	
		
			
				|  |  | -    case GRPC_MDELEM_STORAGE_INTERNED: {
 | 
	
		
			
				|  |  | -      interned_metadata* md =
 | 
	
		
			
				|  |  | -          reinterpret_cast<interned_metadata*> GRPC_MDELEM_DATA(gmd);
 | 
	
		
			
				|  |  | -#ifndef NDEBUG
 | 
	
		
			
				|  |  | -      if (grpc_trace_metadata.enabled()) {
 | 
	
		
			
				|  |  | -        char* key_str = grpc_slice_to_c_string(md->key);
 | 
	
		
			
				|  |  | -        char* value_str = grpc_slice_to_c_string(md->value);
 | 
	
		
			
				|  |  | -        gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
 | 
	
		
			
				|  |  | -                "ELM   REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'",
 | 
	
		
			
				|  |  | -                (void*)md, gpr_atm_no_barrier_load(&md->refcnt),
 | 
	
		
			
				|  |  | -                gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str);
 | 
	
		
			
				|  |  | -        gpr_free(key_str);
 | 
	
		
			
				|  |  | -        gpr_free(value_str);
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -#endif
 | 
	
		
			
				|  |  | -      /* we can assume the ref count is >= 1 as the application is calling
 | 
	
		
			
				|  |  | -         this function - meaning that no adjustment to mdtab_free is necessary,
 | 
	
		
			
				|  |  | -         simplifying the logic here to be just an atomic increment */
 | 
	
		
			
				|  |  | -      /* use C assert to have this removed in opt builds */
 | 
	
		
			
				|  |  | -      GPR_ASSERT(gpr_atm_no_barrier_load(&md->refcnt) >= 1);
 | 
	
		
			
				|  |  | -      gpr_atm_no_barrier_fetch_add(&md->refcnt, 1);
 | 
	
		
			
				|  |  | -      break;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    case GRPC_MDELEM_STORAGE_ALLOCATED: {
 | 
	
		
			
				|  |  | -      allocated_metadata* md =
 | 
	
		
			
				|  |  | -          reinterpret_cast<allocated_metadata*> GRPC_MDELEM_DATA(gmd);
 | 
	
		
			
				|  |  | -#ifndef NDEBUG
 | 
	
		
			
				|  |  | -      if (grpc_trace_metadata.enabled()) {
 | 
	
		
			
				|  |  | -        char* key_str = grpc_slice_to_c_string(md->key);
 | 
	
		
			
				|  |  | -        char* value_str = grpc_slice_to_c_string(md->value);
 | 
	
		
			
				|  |  | -        gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
 | 
	
		
			
				|  |  | -                "ELM   REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'",
 | 
	
		
			
				|  |  | -                (void*)md, gpr_atm_no_barrier_load(&md->refcnt),
 | 
	
		
			
				|  |  | -                gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str);
 | 
	
		
			
				|  |  | -        gpr_free(key_str);
 | 
	
		
			
				|  |  | -        gpr_free(value_str);
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -#endif
 | 
	
		
			
				|  |  | -      /* we can assume the ref count is >= 1 as the application is calling
 | 
	
		
			
				|  |  | -         this function - meaning that no adjustment to mdtab_free is necessary,
 | 
	
		
			
				|  |  | -         simplifying the logic here to be just an atomic increment */
 | 
	
		
			
				|  |  | -      /* use C assert to have this removed in opt builds */
 | 
	
		
			
				|  |  | -      gpr_atm_no_barrier_fetch_add(&md->refcnt, 1);
 | 
	
		
			
				|  |  | -      break;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  return gmd;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -void grpc_mdelem_unref(grpc_mdelem gmd DEBUG_ARGS) {
 | 
	
		
			
				|  |  | -  switch (GRPC_MDELEM_STORAGE(gmd)) {
 | 
	
		
			
				|  |  | -    case GRPC_MDELEM_STORAGE_EXTERNAL:
 | 
	
		
			
				|  |  | -    case GRPC_MDELEM_STORAGE_STATIC:
 | 
	
		
			
				|  |  | -      break;
 | 
	
		
			
				|  |  | -    case GRPC_MDELEM_STORAGE_INTERNED: {
 | 
	
		
			
				|  |  | -      interned_metadata* md =
 | 
	
		
			
				|  |  | -          reinterpret_cast<interned_metadata*> GRPC_MDELEM_DATA(gmd);
 | 
	
		
			
				|  |  | -#ifndef NDEBUG
 | 
	
		
			
				|  |  | -      if (grpc_trace_metadata.enabled()) {
 | 
	
		
			
				|  |  | -        char* key_str = grpc_slice_to_c_string(md->key);
 | 
	
		
			
				|  |  | -        char* value_str = grpc_slice_to_c_string(md->value);
 | 
	
		
			
				|  |  | -        gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
 | 
	
		
			
				|  |  | -                "ELM UNREF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'",
 | 
	
		
			
				|  |  | -                (void*)md, gpr_atm_no_barrier_load(&md->refcnt),
 | 
	
		
			
				|  |  | -                gpr_atm_no_barrier_load(&md->refcnt) - 1, key_str, value_str);
 | 
	
		
			
				|  |  | -        gpr_free(key_str);
 | 
	
		
			
				|  |  | -        gpr_free(value_str);
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -#endif
 | 
	
		
			
				|  |  | -      uint32_t hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash(md->key),
 | 
	
		
			
				|  |  | -                                         grpc_slice_hash(md->value));
 | 
	
		
			
				|  |  | -      const gpr_atm prev_refcount = gpr_atm_full_fetch_add(&md->refcnt, -1);
 | 
	
		
			
				|  |  | -      GPR_ASSERT(prev_refcount >= 1);
 | 
	
		
			
				|  |  | -      if (1 == prev_refcount) {
 | 
	
		
			
				|  |  | -        /* once the refcount hits zero, some other thread can come along and
 | 
	
		
			
				|  |  | -           free md at any time: it's unsafe from this point on to access it */
 | 
	
		
			
				|  |  | -        mdtab_shard* shard = &g_shards[SHARD_IDX(hash)];
 | 
	
		
			
				|  |  | -        gpr_atm_no_barrier_fetch_add(&shard->free_estimate, 1);
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -      break;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    case GRPC_MDELEM_STORAGE_ALLOCATED: {
 | 
	
		
			
				|  |  | -      allocated_metadata* md =
 | 
	
		
			
				|  |  | -          reinterpret_cast<allocated_metadata*> GRPC_MDELEM_DATA(gmd);
 | 
	
		
			
				|  |  | -#ifndef NDEBUG
 | 
	
		
			
				|  |  | -      if (grpc_trace_metadata.enabled()) {
 | 
	
		
			
				|  |  | -        char* key_str = grpc_slice_to_c_string(md->key);
 | 
	
		
			
				|  |  | -        char* value_str = grpc_slice_to_c_string(md->value);
 | 
	
		
			
				|  |  | -        gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
 | 
	
		
			
				|  |  | -                "ELM UNREF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'",
 | 
	
		
			
				|  |  | -                (void*)md, gpr_atm_no_barrier_load(&md->refcnt),
 | 
	
		
			
				|  |  | -                gpr_atm_no_barrier_load(&md->refcnt) - 1, key_str, value_str);
 | 
	
		
			
				|  |  | -        gpr_free(key_str);
 | 
	
		
			
				|  |  | -        gpr_free(value_str);
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -#endif
 | 
	
		
			
				|  |  | -      const gpr_atm prev_refcount = gpr_atm_full_fetch_add(&md->refcnt, -1);
 | 
	
		
			
				|  |  | -      GPR_ASSERT(prev_refcount >= 1);
 | 
	
		
			
				|  |  | -      if (1 == prev_refcount) {
 | 
	
		
			
				|  |  | -        grpc_slice_unref_internal(md->key);
 | 
	
		
			
				|  |  | -        grpc_slice_unref_internal(md->value);
 | 
	
		
			
				|  |  | -        if (md->user_data.user_data) {
 | 
	
		
			
				|  |  | -          destroy_user_data_func destroy_user_data =
 | 
	
		
			
				|  |  | -              (destroy_user_data_func)gpr_atm_no_barrier_load(
 | 
	
		
			
				|  |  | -                  &md->user_data.destroy_user_data);
 | 
	
		
			
				|  |  | -          destroy_user_data((void*)md->user_data.user_data);
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        gpr_mu_destroy(&md->user_data.mu_user_data);
 | 
	
		
			
				|  |  | -        gpr_free(md);
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -      break;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  static void* get_user_data(UserData* user_data, void (*destroy_func)(void*)) {
 | 
	
		
			
				|  |  |    if (gpr_atm_acq_load(&user_data->destroy_user_data) ==
 | 
	
		
			
				|  |  |        (gpr_atm)destroy_func) {
 | 
	
	
		
			
				|  | @@ -491,14 +391,12 @@ void* grpc_mdelem_get_user_data(grpc_mdelem md, void (*destroy_func)(void*)) {
 | 
	
		
			
				|  |  |        return (void*)grpc_static_mdelem_user_data[GRPC_MDELEM_DATA(md) -
 | 
	
		
			
				|  |  |                                                   grpc_static_mdelem_table];
 | 
	
		
			
				|  |  |      case GRPC_MDELEM_STORAGE_ALLOCATED: {
 | 
	
		
			
				|  |  | -      allocated_metadata* am =
 | 
	
		
			
				|  |  | -          reinterpret_cast<allocated_metadata*>(GRPC_MDELEM_DATA(md));
 | 
	
		
			
				|  |  | -      return get_user_data(&am->user_data, destroy_func);
 | 
	
		
			
				|  |  | +      auto* am = reinterpret_cast<AllocatedMetadata*>(GRPC_MDELEM_DATA(md));
 | 
	
		
			
				|  |  | +      return get_user_data(am->user_data(), destroy_func);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      case GRPC_MDELEM_STORAGE_INTERNED: {
 | 
	
		
			
				|  |  | -      interned_metadata* im =
 | 
	
		
			
				|  |  | -          reinterpret_cast<interned_metadata*> GRPC_MDELEM_DATA(md);
 | 
	
		
			
				|  |  | -      return get_user_data(&im->user_data, destroy_func);
 | 
	
		
			
				|  |  | +      auto* im = reinterpret_cast<InternedMetadata*> GRPC_MDELEM_DATA(md);
 | 
	
		
			
				|  |  | +      return get_user_data(im->user_data(), destroy_func);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    GPR_UNREACHABLE_CODE(return nullptr);
 | 
	
	
		
			
				|  | @@ -507,10 +405,10 @@ void* grpc_mdelem_get_user_data(grpc_mdelem md, void (*destroy_func)(void*)) {
 | 
	
		
			
				|  |  |  static void* set_user_data(UserData* ud, void (*destroy_func)(void*),
 | 
	
		
			
				|  |  |                             void* user_data) {
 | 
	
		
			
				|  |  |    GPR_ASSERT((user_data == nullptr) == (destroy_func == nullptr));
 | 
	
		
			
				|  |  | -  gpr_mu_lock(&ud->mu_user_data);
 | 
	
		
			
				|  |  | +  grpc_core::ReleasableMutexLock lock(&ud->mu_user_data);
 | 
	
		
			
				|  |  |    if (gpr_atm_no_barrier_load(&ud->destroy_user_data)) {
 | 
	
		
			
				|  |  |      /* user data can only be set once */
 | 
	
		
			
				|  |  | -    gpr_mu_unlock(&ud->mu_user_data);
 | 
	
		
			
				|  |  | +    lock.Unlock();
 | 
	
		
			
				|  |  |      if (destroy_func != nullptr) {
 | 
	
		
			
				|  |  |        destroy_func(user_data);
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -518,7 +416,6 @@ static void* set_user_data(UserData* ud, void (*destroy_func)(void*),
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    gpr_atm_no_barrier_store(&ud->user_data, (gpr_atm)user_data);
 | 
	
		
			
				|  |  |    gpr_atm_rel_store(&ud->destroy_user_data, (gpr_atm)destroy_func);
 | 
	
		
			
				|  |  | -  gpr_mu_unlock(&ud->mu_user_data);
 | 
	
		
			
				|  |  |    return user_data;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -533,15 +430,13 @@ void* grpc_mdelem_set_user_data(grpc_mdelem md, void (*destroy_func)(void*),
 | 
	
		
			
				|  |  |        return (void*)grpc_static_mdelem_user_data[GRPC_MDELEM_DATA(md) -
 | 
	
		
			
				|  |  |                                                   grpc_static_mdelem_table];
 | 
	
		
			
				|  |  |      case GRPC_MDELEM_STORAGE_ALLOCATED: {
 | 
	
		
			
				|  |  | -      allocated_metadata* am =
 | 
	
		
			
				|  |  | -          reinterpret_cast<allocated_metadata*>(GRPC_MDELEM_DATA(md));
 | 
	
		
			
				|  |  | -      return set_user_data(&am->user_data, destroy_func, user_data);
 | 
	
		
			
				|  |  | +      auto* am = reinterpret_cast<AllocatedMetadata*>(GRPC_MDELEM_DATA(md));
 | 
	
		
			
				|  |  | +      return set_user_data(am->user_data(), destroy_func, user_data);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      case GRPC_MDELEM_STORAGE_INTERNED: {
 | 
	
		
			
				|  |  | -      interned_metadata* im =
 | 
	
		
			
				|  |  | -          reinterpret_cast<interned_metadata*> GRPC_MDELEM_DATA(md);
 | 
	
		
			
				|  |  | +      auto* im = reinterpret_cast<InternedMetadata*> GRPC_MDELEM_DATA(md);
 | 
	
		
			
				|  |  |        GPR_ASSERT(!is_mdelem_static(md));
 | 
	
		
			
				|  |  | -      return set_user_data(&im->user_data, destroy_func, user_data);
 | 
	
		
			
				|  |  | +      return set_user_data(im->user_data(), destroy_func, user_data);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    GPR_UNREACHABLE_CODE(return nullptr);
 | 
	
	
		
			
				|  | @@ -554,3 +449,33 @@ bool grpc_mdelem_eq(grpc_mdelem a, grpc_mdelem b) {
 | 
	
		
			
				|  |  |    return grpc_slice_eq(GRPC_MDKEY(a), GRPC_MDKEY(b)) &&
 | 
	
		
			
				|  |  |           grpc_slice_eq(GRPC_MDVALUE(a), GRPC_MDVALUE(b));
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void note_disposed_interned_metadata(uint32_t hash) {
 | 
	
		
			
				|  |  | +  mdtab_shard* shard = &g_shards[SHARD_IDX(hash)];
 | 
	
		
			
				|  |  | +  gpr_atm_no_barrier_fetch_add(&shard->free_estimate, 1);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void grpc_mdelem_do_unref(grpc_mdelem gmd DEBUG_ARGS) {
 | 
	
		
			
				|  |  | +  switch (GRPC_MDELEM_STORAGE(gmd)) {
 | 
	
		
			
				|  |  | +    case GRPC_MDELEM_STORAGE_EXTERNAL:
 | 
	
		
			
				|  |  | +    case GRPC_MDELEM_STORAGE_STATIC:
 | 
	
		
			
				|  |  | +      return;
 | 
	
		
			
				|  |  | +    case GRPC_MDELEM_STORAGE_INTERNED: {
 | 
	
		
			
				|  |  | +      auto* md = reinterpret_cast<InternedMetadata*> GRPC_MDELEM_DATA(gmd);
 | 
	
		
			
				|  |  | +      uint32_t hash = md->hash();
 | 
	
		
			
				|  |  | +      if (md->Unref(FWD_DEBUG_ARGS)) {
 | 
	
		
			
				|  |  | +        /* once the refcount hits zero, some other thread can come along and
 | 
	
		
			
				|  |  | +           free md at any time: it's unsafe from this point on to access it */
 | 
	
		
			
				|  |  | +        note_disposed_interned_metadata(hash);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      break;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    case GRPC_MDELEM_STORAGE_ALLOCATED: {
 | 
	
		
			
				|  |  | +      auto* md = reinterpret_cast<AllocatedMetadata*> GRPC_MDELEM_DATA(gmd);
 | 
	
		
			
				|  |  | +      if (md->Unref(FWD_DEBUG_ARGS)) {
 | 
	
		
			
				|  |  | +        grpc_core::Delete(md);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      break;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 |