blob: 489c20cbc82e539737f64fb521ce6a7c86262eab [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 Tillerb2b42612015-11-20 12:02:17 -0800133 shard->elems = gpr_malloc(sizeof(*shard->elems) * shard->capacity);
134 memset(shard->elems, 0, sizeof(*shard->elems) * shard->capacity);
Craig Tiller0e72ede2015-11-19 07:48:53 -0800135 }
136}
137
Craig Tillera59c16c2016-10-31 07:25:01 -0700138void grpc_mdctx_global_shutdown(grpc_exec_ctx *exec_ctx) {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800139 for (size_t i = 0; i < SHARD_COUNT; i++) {
140 mdtab_shard *shard = &g_shards[i];
Craig Tillerb2b42612015-11-20 12:02:17 -0800141 gpr_mu_destroy(&shard->mu);
Craig Tillera59c16c2016-10-31 07:25:01 -0700142 gc_mdtab(exec_ctx, shard);
Craig Tiller34075442015-11-23 16:16:54 -0800143 /* TODO(ctiller): GPR_ASSERT(shard->count == 0); */
144 if (shard->count != 0) {
Yuchen Zeng64c0e8d2016-06-10 11:19:51 -0700145 gpr_log(GPR_DEBUG, "WARNING: %" PRIuPTR " metadata elements were leaked",
yang-gd88e1d82015-12-02 13:23:33 -0800146 shard->count);
Craig Tiller0cb803d2016-03-02 22:17:24 -0800147 if (grpc_iomgr_abort_on_leaks()) {
148 abort();
149 }
Craig Tiller34075442015-11-23 16:16:54 -0800150 }
Craig Tillerb2b42612015-11-20 12:02:17 -0800151 gpr_free(shard->elems);
152 }
Craig Tiller0e72ede2015-11-19 07:48:53 -0800153}
154
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800155static int is_mdelem_static(grpc_mdelem e) {
156 return GRPC_MDELEM_DATA(e) >= &grpc_static_mdelem_table[0] &&
157 GRPC_MDELEM_DATA(e) <
158 &grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
Craig Tiller0e72ede2015-11-19 07:48:53 -0800159}
160
Craig Tillerb2b42612015-11-20 12:02:17 -0800161static void ref_md_locked(mdtab_shard *shard,
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800162 interned_metadata *md DEBUG_ARGS) {
Craig Tiller1a65a232015-07-06 10:22:32 -0700163#ifdef GRPC_METADATA_REFCOUNT_DEBUG
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800164 char *key_str = grpc_slice_to_c_string(md->key);
165 char *value_str = grpc_slice_to_c_string(md->value);
Craig Tiller1a65a232015-07-06 10:22:32 -0700166 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
David Garcia Quintas331b9c02016-09-12 18:37:05 -0700167 "ELM REF:%p:%zu->%zu: '%s' = '%s'", (void *)md,
Craig Tiller1a65a232015-07-06 10:22:32 -0700168 gpr_atm_no_barrier_load(&md->refcnt),
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800169 gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str);
170 gpr_free(key_str);
171 gpr_free(value_str);
Craig Tiller1a65a232015-07-06 10:22:32 -0700172#endif
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700173 if (0 == gpr_atm_no_barrier_fetch_add(&md->refcnt, 1)) {
174 gpr_atm_no_barrier_fetch_add(&shard->free_estimate, -1);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800175 }
176}
177
Craig Tillera59c16c2016-10-31 07:25:01 -0700178static void gc_mdtab(grpc_exec_ctx *exec_ctx, mdtab_shard *shard) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800179 size_t i;
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800180 interned_metadata **prev_next;
181 interned_metadata *md, *next;
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700182 gpr_atm num_freed = 0;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800183
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800184 GPR_TIMER_BEGIN("gc_mdtab", 0);
Craig Tillerb2b42612015-11-20 12:02:17 -0800185 for (i = 0; i < shard->capacity; i++) {
186 prev_next = &shard->elems[i];
187 for (md = shard->elems[i]; md; md = next) {
Craig Tiller8344daa2015-10-09 18:10:57 -0700188 void *user_data = (void *)gpr_atm_no_barrier_load(&md->user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800189 next = md->bucket_next;
Craig Tiller9fa41b92015-04-10 15:08:03 -0700190 if (gpr_atm_acq_load(&md->refcnt) == 0) {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800191 grpc_slice_unref_internal(exec_ctx, md->key);
192 grpc_slice_unref_internal(exec_ctx, md->value);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800193 if (md->user_data) {
Craig Tiller8344daa2015-10-09 18:10:57 -0700194 ((destroy_user_data_func)gpr_atm_no_barrier_load(
195 &md->destroy_user_data))(user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800196 }
197 gpr_free(md);
198 *prev_next = next;
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700199 num_freed++;
Craig Tillerb2b42612015-11-20 12:02:17 -0800200 shard->count--;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800201 } else {
202 prev_next = &md->bucket_next;
203 }
204 }
205 }
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700206 gpr_atm_no_barrier_fetch_add(&shard->free_estimate, -num_freed);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800207 GPR_TIMER_END("gc_mdtab", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800208}
209
Craig Tillerb2b42612015-11-20 12:02:17 -0800210static void grow_mdtab(mdtab_shard *shard) {
211 size_t capacity = shard->capacity * 2;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800212 size_t i;
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800213 interned_metadata **mdtab;
214 interned_metadata *md, *next;
Craig Tiller7536af02015-12-22 13:49:30 -0800215 uint32_t hash;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800216
217 GPR_TIMER_BEGIN("grow_mdtab", 0);
218
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800219 mdtab = gpr_malloc(sizeof(interned_metadata *) * capacity);
220 memset(mdtab, 0, sizeof(interned_metadata *) * capacity);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800221
Craig Tillerb2b42612015-11-20 12:02:17 -0800222 for (i = 0; i < shard->capacity; i++) {
223 for (md = shard->elems[i]; md; md = next) {
224 size_t idx;
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800225 hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash(md->key),
226 grpc_slice_hash(md->value));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800227 next = md->bucket_next;
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800228 idx = TABLE_IDX(hash, capacity);
Craig Tillerb2b42612015-11-20 12:02:17 -0800229 md->bucket_next = mdtab[idx];
230 mdtab[idx] = md;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800231 }
232 }
233
Craig Tillerb2b42612015-11-20 12:02:17 -0800234 gpr_free(shard->elems);
235 shard->elems = mdtab;
236 shard->capacity = capacity;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800237
238 GPR_TIMER_END("grow_mdtab", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800239}
240
Craig Tillera59c16c2016-10-31 07:25:01 -0700241static void rehash_mdtab(grpc_exec_ctx *exec_ctx, mdtab_shard *shard) {
Craig Tiller139098c2016-06-06 08:10:08 -0700242 if (gpr_atm_no_barrier_load(&shard->free_estimate) >
243 (gpr_atm)(shard->capacity / 4)) {
Craig Tillera59c16c2016-10-31 07:25:01 -0700244 gc_mdtab(exec_ctx, shard);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800245 } else {
Craig Tillerb2b42612015-11-20 12:02:17 -0800246 grow_mdtab(shard);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800247 }
248}
249
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800250grpc_mdelem grpc_mdelem_create(
251 grpc_exec_ctx *exec_ctx, grpc_slice key, grpc_slice value,
252 grpc_mdelem_data *compatible_external_backing_store) {
253 if (!grpc_slice_is_interned(key) || !grpc_slice_is_interned(value)) {
254 if (compatible_external_backing_store != NULL) {
255 return GRPC_MAKE_MDELEM(compatible_external_backing_store,
256 GRPC_MDELEM_STORAGE_EXTERNAL);
257 }
258
259 allocated_metadata *allocated = gpr_malloc(sizeof(*allocated));
260 allocated->key = grpc_slice_ref_internal(key);
261 allocated->value = grpc_slice_ref_internal(value);
262 gpr_atm_rel_store(&allocated->refcnt, 1);
263#ifdef GRPC_METADATA_REFCOUNT_DEBUG
264 char *key_str = grpc_slice_to_c_string(allocated->key);
265 char *value_str = grpc_slice_to_c_string(allocated->value);
266 gpr_log(GPR_DEBUG, "ELM ALLOC:%p:%zu: '%s' = '%s'", (void *)allocated,
267 gpr_atm_no_barrier_load(&allocated->refcnt), key_str, value_str);
268 gpr_free(key_str);
269 gpr_free(value_str);
270#endif
271 return GRPC_MAKE_MDELEM(allocated, GRPC_MDELEM_STORAGE_ALLOCATED);
272 }
273
274 if (GRPC_IS_STATIC_METADATA_STRING(key) &&
275 GRPC_IS_STATIC_METADATA_STRING(value)) {
276 grpc_mdelem static_elem = grpc_static_mdelem_for_static_strings(
277 GRPC_STATIC_METADATA_INDEX(key), GRPC_STATIC_METADATA_INDEX(value));
278 if (!GRPC_MDISNULL(static_elem)) {
279 return static_elem;
280 }
281 }
282
283 uint32_t hash =
284 GRPC_MDSTR_KV_HASH(grpc_slice_hash(key), grpc_slice_hash(value));
285 interned_metadata *md;
286 mdtab_shard *shard = &g_shards[SHARD_IDX(hash)];
Craig Tillerb2b42612015-11-20 12:02:17 -0800287 size_t idx;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800288
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800289 GPR_TIMER_BEGIN("grpc_mdelem_from_metadata_strings", 0);
290
Craig Tillerb2b42612015-11-20 12:02:17 -0800291 gpr_mu_lock(&shard->mu);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800292
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800293 idx = TABLE_IDX(hash, shard->capacity);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800294 /* search for an existing pair */
Craig Tillerb2b42612015-11-20 12:02:17 -0800295 for (md = shard->elems[idx]; md; md = md->bucket_next) {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800296 if (grpc_slice_eq(key, md->key) && grpc_slice_eq(value, md->value)) {
Craig Tillerb2b42612015-11-20 12:02:17 -0800297 REF_MD_LOCKED(shard, md);
Craig Tillerb2b42612015-11-20 12:02:17 -0800298 gpr_mu_unlock(&shard->mu);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800299 GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800300 return GRPC_MAKE_MDELEM(md, GRPC_MDELEM_STORAGE_INTERNED);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800301 }
302 }
303
304 /* not found: create a new pair */
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800305 md = gpr_malloc(sizeof(interned_metadata));
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700306 gpr_atm_rel_store(&md->refcnt, 1);
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800307 md->key = grpc_slice_ref_internal(key);
308 md->value = grpc_slice_ref_internal(value);
Craig Tiller8344daa2015-10-09 18:10:57 -0700309 md->user_data = 0;
310 md->destroy_user_data = 0;
Craig Tillerb2b42612015-11-20 12:02:17 -0800311 md->bucket_next = shard->elems[idx];
312 shard->elems[idx] = md;
Craig Tiller83901532015-07-10 14:02:45 -0700313 gpr_mu_init(&md->mu_user_data);
Craig Tiller1a65a232015-07-06 10:22:32 -0700314#ifdef GRPC_METADATA_REFCOUNT_DEBUG
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800315 char *key_str = grpc_slice_to_c_string(md->key);
316 char *value_str = grpc_slice_to_c_string(md->value);
David Garcia Quintas331b9c02016-09-12 18:37:05 -0700317 gpr_log(GPR_DEBUG, "ELM NEW:%p:%zu: '%s' = '%s'", (void *)md,
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800318 gpr_atm_no_barrier_load(&md->refcnt), key_str, value_str);
319 gpr_free(key_str);
320 gpr_free(value_str);
Craig Tiller1a65a232015-07-06 10:22:32 -0700321#endif
Craig Tillerb2b42612015-11-20 12:02:17 -0800322 shard->count++;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800323
Craig Tillerb2b42612015-11-20 12:02:17 -0800324 if (shard->count > shard->capacity * 2) {
Craig Tillera59c16c2016-10-31 07:25:01 -0700325 rehash_mdtab(exec_ctx, shard);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800326 }
327
Craig Tillerb2b42612015-11-20 12:02:17 -0800328 gpr_mu_unlock(&shard->mu);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800329
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800330 GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
331
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800332 return GRPC_MAKE_MDELEM(md, GRPC_MDELEM_STORAGE_INTERNED);
Craig Tiller9ecadce2016-11-18 14:52:25 -0800333}
334
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800335grpc_mdelem grpc_mdelem_from_slices(grpc_exec_ctx *exec_ctx, grpc_slice key,
336 grpc_slice value) {
337 grpc_mdelem out = grpc_mdelem_create(exec_ctx, key, value, NULL);
338 grpc_slice_unref_internal(exec_ctx, key);
339 grpc_slice_unref_internal(exec_ctx, value);
340 return out;
Craig Tiller9ecadce2016-11-18 14:52:25 -0800341}
342
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800343grpc_mdelem grpc_mdelem_from_grpc_metadata(grpc_exec_ctx *exec_ctx,
344 grpc_metadata *metadata) {
345 bool changed = false;
346 grpc_slice key_slice =
347 grpc_slice_maybe_static_intern(metadata->key, &changed);
348 grpc_slice value_slice =
349 grpc_slice_maybe_static_intern(metadata->value, &changed);
350 return grpc_mdelem_create(exec_ctx, key_slice, value_slice,
351 changed ? NULL : (grpc_mdelem_data *)metadata);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800352}
353
yang-g24ba7c12016-03-30 16:19:43 -0700354static size_t get_base64_encoded_size(size_t raw_length) {
355 static const uint8_t tail_xtra[3] = {0, 2, 3};
356 return raw_length / 3 * 4 + tail_xtra[raw_length % 3];
357}
358
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800359size_t grpc_mdelem_get_size_in_hpack_table(grpc_mdelem elem) {
360 size_t overhead_and_key = 32 + GRPC_SLICE_LENGTH(GRPC_MDKEY(elem));
361 size_t value_len = GRPC_SLICE_LENGTH(GRPC_MDVALUE(elem));
362 if (grpc_is_binary_header(GRPC_MDKEY(elem))) {
363 return overhead_and_key + get_base64_encoded_size(value_len);
yang-gcb7a8022016-03-30 14:58:53 -0700364 } else {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800365 return overhead_and_key + value_len;
yang-gcb7a8022016-03-30 14:58:53 -0700366 }
367}
368
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800369grpc_mdelem grpc_mdelem_ref(grpc_mdelem gmd DEBUG_ARGS) {
370 switch (GRPC_MDELEM_STORAGE(gmd)) {
371 case GRPC_MDELEM_STORAGE_EXTERNAL:
372 case GRPC_MDELEM_STORAGE_STATIC:
373 break;
374 case GRPC_MDELEM_STORAGE_INTERNED: {
375 interned_metadata *md = (interned_metadata *)GRPC_MDELEM_DATA(gmd);
Craig Tiller1a65a232015-07-06 10:22:32 -0700376#ifdef GRPC_METADATA_REFCOUNT_DEBUG
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800377 char *key_str = grpc_slice_to_c_string(md->key);
378 char *value_str = grpc_slice_to_c_string(md->value);
379 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
380 "ELM REF:%p:%zu->%zu: '%s' = '%s'", (void *)md,
381 gpr_atm_no_barrier_load(&md->refcnt),
382 gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str);
383 gpr_free(key_str);
384 gpr_free(value_str);
Craig Tiller1a65a232015-07-06 10:22:32 -0700385#endif
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800386 /* we can assume the ref count is >= 1 as the application is calling
387 this function - meaning that no adjustment to mdtab_free is necessary,
388 simplifying the logic here to be just an atomic increment */
389 /* use C assert to have this removed in opt builds */
390 GPR_ASSERT(gpr_atm_no_barrier_load(&md->refcnt) >= 1);
391 gpr_atm_no_barrier_fetch_add(&md->refcnt, 1);
392 break;
393 }
394 case GRPC_MDELEM_STORAGE_ALLOCATED: {
395 allocated_metadata *md = (allocated_metadata *)GRPC_MDELEM_DATA(gmd);
396#ifdef GRPC_METADATA_REFCOUNT_DEBUG
397 char *key_str = grpc_slice_to_c_string(md->key);
398 char *value_str = grpc_slice_to_c_string(md->value);
399 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
400 "ELM REF:%p:%zu->%zu: '%s' = '%s'", (void *)md,
401 gpr_atm_no_barrier_load(&md->refcnt),
402 gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str);
403 gpr_free(key_str);
404 gpr_free(value_str);
405#endif
406 /* we can assume the ref count is >= 1 as the application is calling
407 this function - meaning that no adjustment to mdtab_free is necessary,
408 simplifying the logic here to be just an atomic increment */
409 /* use C assert to have this removed in opt builds */
410 gpr_atm_no_barrier_fetch_add(&md->refcnt, 1);
411 break;
412 }
413 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800414 return gmd;
415}
416
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800417void grpc_mdelem_unref(grpc_exec_ctx *exec_ctx, grpc_mdelem gmd DEBUG_ARGS) {
418 switch (GRPC_MDELEM_STORAGE(gmd)) {
419 case GRPC_MDELEM_STORAGE_EXTERNAL:
420 case GRPC_MDELEM_STORAGE_STATIC:
421 break;
422 case GRPC_MDELEM_STORAGE_INTERNED: {
423 interned_metadata *md = (interned_metadata *)GRPC_MDELEM_DATA(gmd);
Craig Tiller1a65a232015-07-06 10:22:32 -0700424#ifdef GRPC_METADATA_REFCOUNT_DEBUG
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800425 char *key_str = grpc_slice_to_c_string(md->key);
426 char *value_str = grpc_slice_to_c_string(md->value);
427 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
428 "ELM UNREF:%p:%zu->%zu: '%s' = '%s'", (void *)md,
429 gpr_atm_no_barrier_load(&md->refcnt),
430 gpr_atm_no_barrier_load(&md->refcnt) - 1, key_str, value_str);
431 gpr_free(key_str);
432 gpr_free(value_str);
Craig Tiller1a65a232015-07-06 10:22:32 -0700433#endif
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800434 uint32_t hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash(md->key),
435 grpc_slice_hash(md->value));
436 const gpr_atm prev_refcount = gpr_atm_full_fetch_add(&md->refcnt, -1);
437 GPR_ASSERT(prev_refcount >= 1);
438 if (1 == prev_refcount) {
439 /* once the refcount hits zero, some other thread can come along and
440 free md at any time: it's unsafe from this point on to access it */
441 mdtab_shard *shard = &g_shards[SHARD_IDX(hash)];
442 gpr_atm_no_barrier_fetch_add(&shard->free_estimate, 1);
443 }
444 break;
Craig Tiller83901532015-07-10 14:02:45 -0700445 }
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800446 case GRPC_MDELEM_STORAGE_ALLOCATED: {
447 allocated_metadata *md = (allocated_metadata *)GRPC_MDELEM_DATA(gmd);
448#ifdef GRPC_METADATA_REFCOUNT_DEBUG
449 char *key_str = grpc_slice_to_c_string(md->key);
450 char *value_str = grpc_slice_to_c_string(md->value);
451 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
452 "ELM UNREF:%p:%zu->%zu: '%s' = '%s'", (void *)md,
453 gpr_atm_no_barrier_load(&md->refcnt),
454 gpr_atm_no_barrier_load(&md->refcnt) - 1, key_str, value_str);
455 gpr_free(key_str);
456 gpr_free(value_str);
457#endif
458 const gpr_atm prev_refcount = gpr_atm_full_fetch_add(&md->refcnt, -1);
459 GPR_ASSERT(prev_refcount >= 1);
460 if (1 == prev_refcount) {
461 grpc_slice_unref_internal(exec_ctx, md->key);
462 grpc_slice_unref_internal(exec_ctx, md->value);
463 gpr_free(md);
464 }
465 break;
466 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800467 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800468}
Craig Tiller0160de92016-11-18 08:46:46 -0800469
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800470void *grpc_mdelem_get_user_data(grpc_mdelem md, void (*destroy_func)(void *)) {
471 switch (GRPC_MDELEM_STORAGE(md)) {
472 case GRPC_MDELEM_STORAGE_EXTERNAL:
473 case GRPC_MDELEM_STORAGE_ALLOCATED:
474 return NULL;
475 case GRPC_MDELEM_STORAGE_STATIC:
476 return (void *)grpc_static_mdelem_user_data[GRPC_MDELEM_DATA(md) -
477 grpc_static_mdelem_table];
478 case GRPC_MDELEM_STORAGE_INTERNED: {
479 interned_metadata *im = (interned_metadata *)GRPC_MDELEM_DATA(md);
480 void *result;
481 if (gpr_atm_acq_load(&im->destroy_user_data) == (gpr_atm)destroy_func) {
482 return (void *)gpr_atm_no_barrier_load(&im->user_data);
483 } else {
484 return NULL;
485 }
486 return result;
487 }
Craig Tiller5e01e2a2017-01-20 18:11:52 -0800488 }
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800489 GPR_UNREACHABLE_CODE(return NULL);
490}
491
492void *grpc_mdelem_set_user_data(grpc_mdelem md, void (*destroy_func)(void *),
493 void *user_data) {
494 switch (GRPC_MDELEM_STORAGE(md)) {
495 case GRPC_MDELEM_STORAGE_EXTERNAL:
496 case GRPC_MDELEM_STORAGE_ALLOCATED:
497 destroy_func(user_data);
498 return NULL;
499 case GRPC_MDELEM_STORAGE_STATIC:
500 destroy_func(user_data);
501 return (void *)grpc_static_mdelem_user_data[GRPC_MDELEM_DATA(md) -
502 grpc_static_mdelem_table];
503 case GRPC_MDELEM_STORAGE_INTERNED: {
504 interned_metadata *im = (interned_metadata *)GRPC_MDELEM_DATA(md);
505 GPR_ASSERT(!is_mdelem_static(md));
506 GPR_ASSERT((user_data == NULL) == (destroy_func == NULL));
507 gpr_mu_lock(&im->mu_user_data);
508 if (gpr_atm_no_barrier_load(&im->destroy_user_data)) {
509 /* user data can only be set once */
510 gpr_mu_unlock(&im->mu_user_data);
511 if (destroy_func != NULL) {
512 destroy_func(user_data);
513 }
514 return (void *)gpr_atm_no_barrier_load(&im->user_data);
515 }
516 gpr_atm_no_barrier_store(&im->user_data, (gpr_atm)user_data);
517 gpr_atm_rel_store(&im->destroy_user_data, (gpr_atm)destroy_func);
518 gpr_mu_unlock(&im->mu_user_data);
519 return user_data;
520 }
521 }
522 GPR_UNREACHABLE_CODE(return NULL);
523}
524
525bool grpc_mdelem_eq(grpc_mdelem a, grpc_mdelem b) {
526 if (a.payload == b.payload) return true;
527 if (GRPC_MDELEM_IS_INTERNED(a) && GRPC_MDELEM_IS_INTERNED(b)) return false;
528 if (GRPC_MDISNULL(a) || GRPC_MDISNULL(b)) return false;
529 return grpc_slice_eq(GRPC_MDKEY(a), GRPC_MDKEY(b)) &&
530 grpc_slice_eq(GRPC_MDVALUE(a), GRPC_MDVALUE(b));
Craig Tiller0160de92016-11-18 08:46:46 -0800531}