blob: f2417d8c4e8327f292d840680a90446694f8fae2 [file] [log] [blame]
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001/*
2 *
Craig Tiller6169d5f2016-03-31 07:46:18 -07003 * Copyright 2015, Google Inc.
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08004 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
Craig Tiller9533d042016-03-25 17:11:06 -070034#include "src/core/lib/transport/metadata.h"
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080035
Craig Tiller9fa41b92015-04-10 15:08:03 -070036#include <assert.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080037#include <stddef.h>
38#include <string.h>
39
Craig Tillerebdef9d2015-11-19 17:09:49 -080040#include <grpc/compression.h>
yang-gcb7a8022016-03-30 14:58:53 -070041#include <grpc/grpc.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080042#include <grpc/support/alloc.h>
Craig Tiller9fa41b92015-04-10 15:08:03 -070043#include <grpc/support/atm.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080044#include <grpc/support/log.h>
Craig Tillerebdef9d2015-11-19 17:09:49 -080045#include <grpc/support/string_util.h>
Craig Tiller9d35a1f2015-11-02 14:16:12 -080046#include <grpc/support/time.h>
Craig Tiller0cb803d2016-03-02 22:17:24 -080047
Craig Tiller9533d042016-03-25 17:11:06 -070048#include "src/core/lib/iomgr/iomgr_internal.h"
49#include "src/core/lib/profiling/timers.h"
Craig Tillera59c16c2016-10-31 07:25:01 -070050#include "src/core/lib/slice/slice_internal.h"
Craig Tiller7c70b6c2017-01-23 07:48:42 -080051#include "src/core/lib/slice/slice_string_helpers.h"
Craig Tiller9533d042016-03-25 17:11:06 -070052#include "src/core/lib/support/murmur_hash.h"
53#include "src/core/lib/support/string.h"
Craig Tiller9533d042016-03-25 17:11:06 -070054#include "src/core/lib/transport/static_metadata.h"
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080055
Craig Tiller70b080d2015-11-19 08:04:48 -080056/* There are two kinds of mdelem and mdstr instances.
57 * Static instances are declared in static_metadata.{h,c} and
58 * are initialized by grpc_mdctx_global_init().
59 * Dynamic instances are stored in hash tables on grpc_mdctx, and are backed
60 * by internal_string and internal_element structures.
61 * Internal helper functions here-in (is_mdstr_static, is_mdelem_static) are
62 * used to determine which kind of element a pointer refers to.
63 */
64
Craig Tiller1a65a232015-07-06 10:22:32 -070065#ifdef GRPC_METADATA_REFCOUNT_DEBUG
66#define DEBUG_ARGS , const char *file, int line
67#define FWD_DEBUG_ARGS , file, line
Craig Tillerb2b42612015-11-20 12:02:17 -080068#define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s), __FILE__, __LINE__)
Craig Tiller1a65a232015-07-06 10:22:32 -070069#else
70#define DEBUG_ARGS
71#define FWD_DEBUG_ARGS
Craig Tillerb2b42612015-11-20 12:02:17 -080072#define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s))
Craig Tiller1a65a232015-07-06 10:22:32 -070073#endif
74
Craig Tiller7c70b6c2017-01-23 07:48:42 -080075#define INITIAL_SHARD_CAPACITY 8
76#define LOG2_SHARD_COUNT 4
77#define SHARD_COUNT ((size_t)(1 << LOG2_SHARD_COUNT))
78
79#define TABLE_IDX(hash, capacity) (((hash) >> (LOG2_SHARD_COUNT)) % (capacity))
80#define SHARD_IDX(hash) ((hash) & ((1 << (LOG2_SHARD_COUNT)) - 1))
Craig Tillerb2b42612015-11-20 12:02:17 -080081
Craig Tiller8344daa2015-10-09 18:10:57 -070082typedef void (*destroy_user_data_func)(void *user_data);
83
Craig Tiller7c70b6c2017-01-23 07:48:42 -080084/* Shadow structure for grpc_mdelem_data for interned elements */
85typedef struct interned_metadata {
86 /* must be byte compatible with grpc_mdelem_data */
87 grpc_slice key;
88 grpc_slice value;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080089
Craig Tillerb2b42612015-11-20 12:02:17 -080090 /* private only data */
Craig Tiller9fa41b92015-04-10 15:08:03 -070091 gpr_atm refcnt;
92
Craig Tiller83901532015-07-10 14:02:45 -070093 gpr_mu mu_user_data;
Craig Tiller8344daa2015-10-09 18:10:57 -070094 gpr_atm destroy_user_data;
95 gpr_atm user_data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080096
Craig Tiller7c70b6c2017-01-23 07:48:42 -080097 struct interned_metadata *bucket_next;
98} interned_metadata;
Craig Tiller9ecadce2016-11-18 14:52:25 -080099
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800100/* Shadow structure for grpc_mdelem_data for allocated elements */
101typedef struct allocated_metadata {
102 /* must be byte compatible with grpc_mdelem_data */
103 grpc_slice key;
104 grpc_slice value;
105
106 /* private only data */
107 gpr_atm refcnt;
108} allocated_metadata;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800109
Craig Tillerb2b42612015-11-20 12:02:17 -0800110typedef struct mdtab_shard {
111 gpr_mu mu;
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800112 interned_metadata **elems;
Craig Tillerb2b42612015-11-20 12:02:17 -0800113 size_t count;
114 size_t capacity;
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700115 /** Estimate of the number of unreferenced mdelems in the hash table.
116 This will eventually converge to the exact number, but it's instantaneous
117 accuracy is not guaranteed */
118 gpr_atm free_estimate;
Craig Tillerb2b42612015-11-20 12:02:17 -0800119} mdtab_shard;
Craig Tiller0e72ede2015-11-19 07:48:53 -0800120
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800121static mdtab_shard g_shards[SHARD_COUNT];
Craig Tillerb2b42612015-11-20 12:02:17 -0800122
Craig Tillera59c16c2016-10-31 07:25:01 -0700123static void gc_mdtab(grpc_exec_ctx *exec_ctx, mdtab_shard *shard);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800124
Craig Tiller0e72ede2015-11-19 07:48:53 -0800125void grpc_mdctx_global_init(void) {
Craig Tillerb2b42612015-11-20 12:02:17 -0800126 /* initialize shards */
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800127 for (size_t i = 0; i < SHARD_COUNT; i++) {
128 mdtab_shard *shard = &g_shards[i];
Craig Tillerb2b42612015-11-20 12:02:17 -0800129 gpr_mu_init(&shard->mu);
130 shard->count = 0;
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700131 gpr_atm_no_barrier_store(&shard->free_estimate, 0);
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800132 shard->capacity = INITIAL_SHARD_CAPACITY;
Craig Tiller6f417882017-02-16 14:09:39 -0800133 shard->elems = gpr_zalloc(sizeof(*shard->elems) * shard->capacity);
Craig Tiller0e72ede2015-11-19 07:48:53 -0800134 }
135}
136
Craig Tillera59c16c2016-10-31 07:25:01 -0700137void grpc_mdctx_global_shutdown(grpc_exec_ctx *exec_ctx) {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800138 for (size_t i = 0; i < SHARD_COUNT; i++) {
139 mdtab_shard *shard = &g_shards[i];
Craig Tillerb2b42612015-11-20 12:02:17 -0800140 gpr_mu_destroy(&shard->mu);
Craig Tillera59c16c2016-10-31 07:25:01 -0700141 gc_mdtab(exec_ctx, shard);
Craig Tiller34075442015-11-23 16:16:54 -0800142 /* TODO(ctiller): GPR_ASSERT(shard->count == 0); */
143 if (shard->count != 0) {
Yuchen Zeng64c0e8d2016-06-10 11:19:51 -0700144 gpr_log(GPR_DEBUG, "WARNING: %" PRIuPTR " metadata elements were leaked",
yang-gd88e1d82015-12-02 13:23:33 -0800145 shard->count);
Craig Tiller0cb803d2016-03-02 22:17:24 -0800146 if (grpc_iomgr_abort_on_leaks()) {
147 abort();
148 }
Craig Tiller34075442015-11-23 16:16:54 -0800149 }
Craig Tillerb2b42612015-11-20 12:02:17 -0800150 gpr_free(shard->elems);
151 }
Craig Tiller0e72ede2015-11-19 07:48:53 -0800152}
153
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800154static int is_mdelem_static(grpc_mdelem e) {
155 return GRPC_MDELEM_DATA(e) >= &grpc_static_mdelem_table[0] &&
156 GRPC_MDELEM_DATA(e) <
157 &grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
Craig Tiller0e72ede2015-11-19 07:48:53 -0800158}
159
Craig Tillerb2b42612015-11-20 12:02:17 -0800160static void ref_md_locked(mdtab_shard *shard,
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800161 interned_metadata *md DEBUG_ARGS) {
Craig Tiller1a65a232015-07-06 10:22:32 -0700162#ifdef GRPC_METADATA_REFCOUNT_DEBUG
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800163 char *key_str = grpc_slice_to_c_string(md->key);
164 char *value_str = grpc_slice_to_c_string(md->value);
Craig Tiller1a65a232015-07-06 10:22:32 -0700165 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
David Garcia Quintas331b9c02016-09-12 18:37:05 -0700166 "ELM REF:%p:%zu->%zu: '%s' = '%s'", (void *)md,
Craig Tiller1a65a232015-07-06 10:22:32 -0700167 gpr_atm_no_barrier_load(&md->refcnt),
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800168 gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str);
169 gpr_free(key_str);
170 gpr_free(value_str);
Craig Tiller1a65a232015-07-06 10:22:32 -0700171#endif
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700172 if (0 == gpr_atm_no_barrier_fetch_add(&md->refcnt, 1)) {
173 gpr_atm_no_barrier_fetch_add(&shard->free_estimate, -1);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800174 }
175}
176
Craig Tillera59c16c2016-10-31 07:25:01 -0700177static void gc_mdtab(grpc_exec_ctx *exec_ctx, mdtab_shard *shard) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800178 size_t i;
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800179 interned_metadata **prev_next;
180 interned_metadata *md, *next;
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700181 gpr_atm num_freed = 0;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800182
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800183 GPR_TIMER_BEGIN("gc_mdtab", 0);
Craig Tillerb2b42612015-11-20 12:02:17 -0800184 for (i = 0; i < shard->capacity; i++) {
185 prev_next = &shard->elems[i];
186 for (md = shard->elems[i]; md; md = next) {
Craig Tiller8344daa2015-10-09 18:10:57 -0700187 void *user_data = (void *)gpr_atm_no_barrier_load(&md->user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800188 next = md->bucket_next;
Craig Tiller9fa41b92015-04-10 15:08:03 -0700189 if (gpr_atm_acq_load(&md->refcnt) == 0) {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800190 grpc_slice_unref_internal(exec_ctx, md->key);
191 grpc_slice_unref_internal(exec_ctx, md->value);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800192 if (md->user_data) {
Craig Tiller8344daa2015-10-09 18:10:57 -0700193 ((destroy_user_data_func)gpr_atm_no_barrier_load(
194 &md->destroy_user_data))(user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800195 }
196 gpr_free(md);
197 *prev_next = next;
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700198 num_freed++;
Craig Tillerb2b42612015-11-20 12:02:17 -0800199 shard->count--;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800200 } else {
201 prev_next = &md->bucket_next;
202 }
203 }
204 }
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700205 gpr_atm_no_barrier_fetch_add(&shard->free_estimate, -num_freed);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800206 GPR_TIMER_END("gc_mdtab", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800207}
208
Craig Tillerb2b42612015-11-20 12:02:17 -0800209static void grow_mdtab(mdtab_shard *shard) {
210 size_t capacity = shard->capacity * 2;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800211 size_t i;
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800212 interned_metadata **mdtab;
213 interned_metadata *md, *next;
Craig Tiller7536af02015-12-22 13:49:30 -0800214 uint32_t hash;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800215
216 GPR_TIMER_BEGIN("grow_mdtab", 0);
217
Craig Tiller6f417882017-02-16 14:09:39 -0800218 mdtab = gpr_zalloc(sizeof(interned_metadata *) * capacity);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800219
Craig Tillerb2b42612015-11-20 12:02:17 -0800220 for (i = 0; i < shard->capacity; i++) {
221 for (md = shard->elems[i]; md; md = next) {
222 size_t idx;
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800223 hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash(md->key),
224 grpc_slice_hash(md->value));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800225 next = md->bucket_next;
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800226 idx = TABLE_IDX(hash, capacity);
Craig Tillerb2b42612015-11-20 12:02:17 -0800227 md->bucket_next = mdtab[idx];
228 mdtab[idx] = md;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800229 }
230 }
231
Craig Tillerb2b42612015-11-20 12:02:17 -0800232 gpr_free(shard->elems);
233 shard->elems = mdtab;
234 shard->capacity = capacity;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800235
236 GPR_TIMER_END("grow_mdtab", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800237}
238
Craig Tillera59c16c2016-10-31 07:25:01 -0700239static void rehash_mdtab(grpc_exec_ctx *exec_ctx, mdtab_shard *shard) {
Craig Tiller139098c2016-06-06 08:10:08 -0700240 if (gpr_atm_no_barrier_load(&shard->free_estimate) >
241 (gpr_atm)(shard->capacity / 4)) {
Craig Tillera59c16c2016-10-31 07:25:01 -0700242 gc_mdtab(exec_ctx, shard);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800243 } else {
Craig Tillerb2b42612015-11-20 12:02:17 -0800244 grow_mdtab(shard);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800245 }
246}
247
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800248grpc_mdelem grpc_mdelem_create(
249 grpc_exec_ctx *exec_ctx, grpc_slice key, grpc_slice value,
250 grpc_mdelem_data *compatible_external_backing_store) {
251 if (!grpc_slice_is_interned(key) || !grpc_slice_is_interned(value)) {
252 if (compatible_external_backing_store != NULL) {
253 return GRPC_MAKE_MDELEM(compatible_external_backing_store,
254 GRPC_MDELEM_STORAGE_EXTERNAL);
255 }
256
257 allocated_metadata *allocated = gpr_malloc(sizeof(*allocated));
258 allocated->key = grpc_slice_ref_internal(key);
259 allocated->value = grpc_slice_ref_internal(value);
260 gpr_atm_rel_store(&allocated->refcnt, 1);
261#ifdef GRPC_METADATA_REFCOUNT_DEBUG
262 char *key_str = grpc_slice_to_c_string(allocated->key);
263 char *value_str = grpc_slice_to_c_string(allocated->value);
264 gpr_log(GPR_DEBUG, "ELM ALLOC:%p:%zu: '%s' = '%s'", (void *)allocated,
265 gpr_atm_no_barrier_load(&allocated->refcnt), key_str, value_str);
266 gpr_free(key_str);
267 gpr_free(value_str);
268#endif
269 return GRPC_MAKE_MDELEM(allocated, GRPC_MDELEM_STORAGE_ALLOCATED);
270 }
271
272 if (GRPC_IS_STATIC_METADATA_STRING(key) &&
273 GRPC_IS_STATIC_METADATA_STRING(value)) {
274 grpc_mdelem static_elem = grpc_static_mdelem_for_static_strings(
275 GRPC_STATIC_METADATA_INDEX(key), GRPC_STATIC_METADATA_INDEX(value));
276 if (!GRPC_MDISNULL(static_elem)) {
277 return static_elem;
278 }
279 }
280
281 uint32_t hash =
282 GRPC_MDSTR_KV_HASH(grpc_slice_hash(key), grpc_slice_hash(value));
283 interned_metadata *md;
284 mdtab_shard *shard = &g_shards[SHARD_IDX(hash)];
Craig Tillerb2b42612015-11-20 12:02:17 -0800285 size_t idx;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800286
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800287 GPR_TIMER_BEGIN("grpc_mdelem_from_metadata_strings", 0);
288
Craig Tillerb2b42612015-11-20 12:02:17 -0800289 gpr_mu_lock(&shard->mu);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800290
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800291 idx = TABLE_IDX(hash, shard->capacity);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800292 /* search for an existing pair */
Craig Tillerb2b42612015-11-20 12:02:17 -0800293 for (md = shard->elems[idx]; md; md = md->bucket_next) {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800294 if (grpc_slice_eq(key, md->key) && grpc_slice_eq(value, md->value)) {
Craig Tillerb2b42612015-11-20 12:02:17 -0800295 REF_MD_LOCKED(shard, md);
Craig Tillerb2b42612015-11-20 12:02:17 -0800296 gpr_mu_unlock(&shard->mu);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800297 GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800298 return GRPC_MAKE_MDELEM(md, GRPC_MDELEM_STORAGE_INTERNED);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800299 }
300 }
301
302 /* not found: create a new pair */
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800303 md = gpr_malloc(sizeof(interned_metadata));
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700304 gpr_atm_rel_store(&md->refcnt, 1);
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800305 md->key = grpc_slice_ref_internal(key);
306 md->value = grpc_slice_ref_internal(value);
Craig Tiller8344daa2015-10-09 18:10:57 -0700307 md->user_data = 0;
308 md->destroy_user_data = 0;
Craig Tillerb2b42612015-11-20 12:02:17 -0800309 md->bucket_next = shard->elems[idx];
310 shard->elems[idx] = md;
Craig Tiller83901532015-07-10 14:02:45 -0700311 gpr_mu_init(&md->mu_user_data);
Craig Tiller1a65a232015-07-06 10:22:32 -0700312#ifdef GRPC_METADATA_REFCOUNT_DEBUG
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800313 char *key_str = grpc_slice_to_c_string(md->key);
314 char *value_str = grpc_slice_to_c_string(md->value);
David Garcia Quintas331b9c02016-09-12 18:37:05 -0700315 gpr_log(GPR_DEBUG, "ELM NEW:%p:%zu: '%s' = '%s'", (void *)md,
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800316 gpr_atm_no_barrier_load(&md->refcnt), key_str, value_str);
317 gpr_free(key_str);
318 gpr_free(value_str);
Craig Tiller1a65a232015-07-06 10:22:32 -0700319#endif
Craig Tillerb2b42612015-11-20 12:02:17 -0800320 shard->count++;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800321
Craig Tillerb2b42612015-11-20 12:02:17 -0800322 if (shard->count > shard->capacity * 2) {
Craig Tillera59c16c2016-10-31 07:25:01 -0700323 rehash_mdtab(exec_ctx, shard);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800324 }
325
Craig Tillerb2b42612015-11-20 12:02:17 -0800326 gpr_mu_unlock(&shard->mu);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800327
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800328 GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
329
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800330 return GRPC_MAKE_MDELEM(md, GRPC_MDELEM_STORAGE_INTERNED);
Craig Tiller9ecadce2016-11-18 14:52:25 -0800331}
332
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800333grpc_mdelem grpc_mdelem_from_slices(grpc_exec_ctx *exec_ctx, grpc_slice key,
334 grpc_slice value) {
335 grpc_mdelem out = grpc_mdelem_create(exec_ctx, key, value, NULL);
336 grpc_slice_unref_internal(exec_ctx, key);
337 grpc_slice_unref_internal(exec_ctx, value);
338 return out;
Craig Tiller9ecadce2016-11-18 14:52:25 -0800339}
340
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800341grpc_mdelem grpc_mdelem_from_grpc_metadata(grpc_exec_ctx *exec_ctx,
342 grpc_metadata *metadata) {
343 bool changed = false;
344 grpc_slice key_slice =
345 grpc_slice_maybe_static_intern(metadata->key, &changed);
346 grpc_slice value_slice =
347 grpc_slice_maybe_static_intern(metadata->value, &changed);
348 return grpc_mdelem_create(exec_ctx, key_slice, value_slice,
349 changed ? NULL : (grpc_mdelem_data *)metadata);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800350}
351
yang-g24ba7c12016-03-30 16:19:43 -0700352static size_t get_base64_encoded_size(size_t raw_length) {
353 static const uint8_t tail_xtra[3] = {0, 2, 3};
354 return raw_length / 3 * 4 + tail_xtra[raw_length % 3];
355}
356
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800357size_t grpc_mdelem_get_size_in_hpack_table(grpc_mdelem elem) {
358 size_t overhead_and_key = 32 + GRPC_SLICE_LENGTH(GRPC_MDKEY(elem));
359 size_t value_len = GRPC_SLICE_LENGTH(GRPC_MDVALUE(elem));
360 if (grpc_is_binary_header(GRPC_MDKEY(elem))) {
361 return overhead_and_key + get_base64_encoded_size(value_len);
yang-gcb7a8022016-03-30 14:58:53 -0700362 } else {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800363 return overhead_and_key + value_len;
yang-gcb7a8022016-03-30 14:58:53 -0700364 }
365}
366
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800367grpc_mdelem grpc_mdelem_ref(grpc_mdelem gmd DEBUG_ARGS) {
368 switch (GRPC_MDELEM_STORAGE(gmd)) {
369 case GRPC_MDELEM_STORAGE_EXTERNAL:
370 case GRPC_MDELEM_STORAGE_STATIC:
371 break;
372 case GRPC_MDELEM_STORAGE_INTERNED: {
373 interned_metadata *md = (interned_metadata *)GRPC_MDELEM_DATA(gmd);
Craig Tiller1a65a232015-07-06 10:22:32 -0700374#ifdef GRPC_METADATA_REFCOUNT_DEBUG
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800375 char *key_str = grpc_slice_to_c_string(md->key);
376 char *value_str = grpc_slice_to_c_string(md->value);
377 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
378 "ELM REF:%p:%zu->%zu: '%s' = '%s'", (void *)md,
379 gpr_atm_no_barrier_load(&md->refcnt),
380 gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str);
381 gpr_free(key_str);
382 gpr_free(value_str);
Craig Tiller1a65a232015-07-06 10:22:32 -0700383#endif
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800384 /* we can assume the ref count is >= 1 as the application is calling
385 this function - meaning that no adjustment to mdtab_free is necessary,
386 simplifying the logic here to be just an atomic increment */
387 /* use C assert to have this removed in opt builds */
388 GPR_ASSERT(gpr_atm_no_barrier_load(&md->refcnt) >= 1);
389 gpr_atm_no_barrier_fetch_add(&md->refcnt, 1);
390 break;
391 }
392 case GRPC_MDELEM_STORAGE_ALLOCATED: {
393 allocated_metadata *md = (allocated_metadata *)GRPC_MDELEM_DATA(gmd);
394#ifdef GRPC_METADATA_REFCOUNT_DEBUG
395 char *key_str = grpc_slice_to_c_string(md->key);
396 char *value_str = grpc_slice_to_c_string(md->value);
397 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
398 "ELM REF:%p:%zu->%zu: '%s' = '%s'", (void *)md,
399 gpr_atm_no_barrier_load(&md->refcnt),
400 gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str);
401 gpr_free(key_str);
402 gpr_free(value_str);
403#endif
404 /* we can assume the ref count is >= 1 as the application is calling
405 this function - meaning that no adjustment to mdtab_free is necessary,
406 simplifying the logic here to be just an atomic increment */
407 /* use C assert to have this removed in opt builds */
408 gpr_atm_no_barrier_fetch_add(&md->refcnt, 1);
409 break;
410 }
411 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800412 return gmd;
413}
414
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800415void grpc_mdelem_unref(grpc_exec_ctx *exec_ctx, grpc_mdelem gmd DEBUG_ARGS) {
416 switch (GRPC_MDELEM_STORAGE(gmd)) {
417 case GRPC_MDELEM_STORAGE_EXTERNAL:
418 case GRPC_MDELEM_STORAGE_STATIC:
419 break;
420 case GRPC_MDELEM_STORAGE_INTERNED: {
421 interned_metadata *md = (interned_metadata *)GRPC_MDELEM_DATA(gmd);
Craig Tiller1a65a232015-07-06 10:22:32 -0700422#ifdef GRPC_METADATA_REFCOUNT_DEBUG
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800423 char *key_str = grpc_slice_to_c_string(md->key);
424 char *value_str = grpc_slice_to_c_string(md->value);
425 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
426 "ELM UNREF:%p:%zu->%zu: '%s' = '%s'", (void *)md,
427 gpr_atm_no_barrier_load(&md->refcnt),
428 gpr_atm_no_barrier_load(&md->refcnt) - 1, key_str, value_str);
429 gpr_free(key_str);
430 gpr_free(value_str);
Craig Tiller1a65a232015-07-06 10:22:32 -0700431#endif
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800432 uint32_t hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash(md->key),
433 grpc_slice_hash(md->value));
434 const gpr_atm prev_refcount = gpr_atm_full_fetch_add(&md->refcnt, -1);
435 GPR_ASSERT(prev_refcount >= 1);
436 if (1 == prev_refcount) {
437 /* once the refcount hits zero, some other thread can come along and
438 free md at any time: it's unsafe from this point on to access it */
439 mdtab_shard *shard = &g_shards[SHARD_IDX(hash)];
440 gpr_atm_no_barrier_fetch_add(&shard->free_estimate, 1);
441 }
442 break;
Craig Tiller83901532015-07-10 14:02:45 -0700443 }
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800444 case GRPC_MDELEM_STORAGE_ALLOCATED: {
445 allocated_metadata *md = (allocated_metadata *)GRPC_MDELEM_DATA(gmd);
446#ifdef GRPC_METADATA_REFCOUNT_DEBUG
447 char *key_str = grpc_slice_to_c_string(md->key);
448 char *value_str = grpc_slice_to_c_string(md->value);
449 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
450 "ELM UNREF:%p:%zu->%zu: '%s' = '%s'", (void *)md,
451 gpr_atm_no_barrier_load(&md->refcnt),
452 gpr_atm_no_barrier_load(&md->refcnt) - 1, key_str, value_str);
453 gpr_free(key_str);
454 gpr_free(value_str);
455#endif
456 const gpr_atm prev_refcount = gpr_atm_full_fetch_add(&md->refcnt, -1);
457 GPR_ASSERT(prev_refcount >= 1);
458 if (1 == prev_refcount) {
459 grpc_slice_unref_internal(exec_ctx, md->key);
460 grpc_slice_unref_internal(exec_ctx, md->value);
461 gpr_free(md);
462 }
463 break;
464 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800465 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800466}
Craig Tiller0160de92016-11-18 08:46:46 -0800467
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800468void *grpc_mdelem_get_user_data(grpc_mdelem md, void (*destroy_func)(void *)) {
469 switch (GRPC_MDELEM_STORAGE(md)) {
470 case GRPC_MDELEM_STORAGE_EXTERNAL:
471 case GRPC_MDELEM_STORAGE_ALLOCATED:
472 return NULL;
473 case GRPC_MDELEM_STORAGE_STATIC:
474 return (void *)grpc_static_mdelem_user_data[GRPC_MDELEM_DATA(md) -
475 grpc_static_mdelem_table];
476 case GRPC_MDELEM_STORAGE_INTERNED: {
477 interned_metadata *im = (interned_metadata *)GRPC_MDELEM_DATA(md);
478 void *result;
479 if (gpr_atm_acq_load(&im->destroy_user_data) == (gpr_atm)destroy_func) {
480 return (void *)gpr_atm_no_barrier_load(&im->user_data);
481 } else {
482 return NULL;
483 }
484 return result;
485 }
Craig Tiller5e01e2a2017-01-20 18:11:52 -0800486 }
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800487 GPR_UNREACHABLE_CODE(return NULL);
488}
489
490void *grpc_mdelem_set_user_data(grpc_mdelem md, void (*destroy_func)(void *),
491 void *user_data) {
492 switch (GRPC_MDELEM_STORAGE(md)) {
493 case GRPC_MDELEM_STORAGE_EXTERNAL:
494 case GRPC_MDELEM_STORAGE_ALLOCATED:
495 destroy_func(user_data);
496 return NULL;
497 case GRPC_MDELEM_STORAGE_STATIC:
498 destroy_func(user_data);
499 return (void *)grpc_static_mdelem_user_data[GRPC_MDELEM_DATA(md) -
500 grpc_static_mdelem_table];
501 case GRPC_MDELEM_STORAGE_INTERNED: {
502 interned_metadata *im = (interned_metadata *)GRPC_MDELEM_DATA(md);
503 GPR_ASSERT(!is_mdelem_static(md));
504 GPR_ASSERT((user_data == NULL) == (destroy_func == NULL));
505 gpr_mu_lock(&im->mu_user_data);
506 if (gpr_atm_no_barrier_load(&im->destroy_user_data)) {
507 /* user data can only be set once */
508 gpr_mu_unlock(&im->mu_user_data);
509 if (destroy_func != NULL) {
510 destroy_func(user_data);
511 }
512 return (void *)gpr_atm_no_barrier_load(&im->user_data);
513 }
514 gpr_atm_no_barrier_store(&im->user_data, (gpr_atm)user_data);
515 gpr_atm_rel_store(&im->destroy_user_data, (gpr_atm)destroy_func);
516 gpr_mu_unlock(&im->mu_user_data);
517 return user_data;
518 }
519 }
520 GPR_UNREACHABLE_CODE(return NULL);
521}
522
523bool grpc_mdelem_eq(grpc_mdelem a, grpc_mdelem b) {
524 if (a.payload == b.payload) return true;
525 if (GRPC_MDELEM_IS_INTERNED(a) && GRPC_MDELEM_IS_INTERNED(b)) return false;
526 if (GRPC_MDISNULL(a) || GRPC_MDISNULL(b)) return false;
527 return grpc_slice_eq(GRPC_MDKEY(a), GRPC_MDKEY(b)) &&
528 grpc_slice_eq(GRPC_MDVALUE(a), GRPC_MDVALUE(b));
Craig Tiller0160de92016-11-18 08:46:46 -0800529}