blob: f3f6b1311fc8b1f543a56abde3f8d5c105705050 [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 Tiller9533d042016-03-25 17:11:06 -070051#include "src/core/lib/support/murmur_hash.h"
52#include "src/core/lib/support/string.h"
Craig Tiller9533d042016-03-25 17:11:06 -070053#include "src/core/lib/transport/static_metadata.h"
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080054
Craig Tiller70b080d2015-11-19 08:04:48 -080055/* There are two kinds of mdelem and mdstr instances.
56 * Static instances are declared in static_metadata.{h,c} and
57 * are initialized by grpc_mdctx_global_init().
58 * Dynamic instances are stored in hash tables on grpc_mdctx, and are backed
59 * by internal_string and internal_element structures.
60 * Internal helper functions here-in (is_mdstr_static, is_mdelem_static) are
61 * used to determine which kind of element a pointer refers to.
62 */
63
Craig Tiller1a65a232015-07-06 10:22:32 -070064#ifdef GRPC_METADATA_REFCOUNT_DEBUG
65#define DEBUG_ARGS , const char *file, int line
66#define FWD_DEBUG_ARGS , file, line
Craig Tillerb2b42612015-11-20 12:02:17 -080067#define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s), __FILE__, __LINE__)
Craig Tiller1a65a232015-07-06 10:22:32 -070068#else
69#define DEBUG_ARGS
70#define FWD_DEBUG_ARGS
Craig Tillerb2b42612015-11-20 12:02:17 -080071#define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s))
Craig Tiller1a65a232015-07-06 10:22:32 -070072#endif
73
Craig Tillere52bbb12016-11-10 15:34:06 -080074#define INITIAL_SHARD_CAPACITY 8
75#define LOG2_SHARD_COUNT 4
76#define SHARD_COUNT ((size_t)(1 << LOG2_SHARD_COUNT))
77
78#define TABLE_IDX(hash, capacity) (((hash) >> (LOG2_SHARD_COUNT)) % (capacity))
79#define SHARD_IDX(hash) ((hash) & ((1 << (LOG2_SHARD_COUNT)) - 1))
Craig Tillerb2b42612015-11-20 12:02:17 -080080
Craig Tiller8344daa2015-10-09 18:10:57 -070081typedef void (*destroy_user_data_func)(void *user_data);
82
Craig Tiller9ecadce2016-11-18 14:52:25 -080083/* Shadow structure for grpc_mdelem_data for interned elements */
84typedef struct interned_metadata {
85 /* must be byte compatible with grpc_mdelem_data */
Craig Tillere52bbb12016-11-10 15:34:06 -080086 grpc_slice key;
87 grpc_slice value;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080088
Craig Tillerb2b42612015-11-20 12:02:17 -080089 /* private only data */
Craig Tiller9fa41b92015-04-10 15:08:03 -070090 gpr_atm refcnt;
91
Craig Tiller83901532015-07-10 14:02:45 -070092 gpr_mu mu_user_data;
Craig Tiller8344daa2015-10-09 18:10:57 -070093 gpr_atm destroy_user_data;
94 gpr_atm user_data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080095
Craig Tiller9ecadce2016-11-18 14:52:25 -080096 struct interned_metadata *bucket_next;
97} interned_metadata;
98
99/* Shadow structure for grpc_mdelem_data for allocated elements */
100typedef struct allocated_metadata {
101 /* must be byte compatible with grpc_mdelem_data */
102 grpc_slice key;
103 grpc_slice value;
104
105 /* private only data */
106 gpr_atm refcnt;
107} allocated_metadata;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800108
Craig Tillerb2b42612015-11-20 12:02:17 -0800109typedef struct mdtab_shard {
110 gpr_mu mu;
Craig Tiller9ecadce2016-11-18 14:52:25 -0800111 interned_metadata **elems;
Craig Tillerb2b42612015-11-20 12:02:17 -0800112 size_t count;
113 size_t capacity;
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700114 /** Estimate of the number of unreferenced mdelems in the hash table.
115 This will eventually converge to the exact number, but it's instantaneous
116 accuracy is not guaranteed */
117 gpr_atm free_estimate;
Craig Tillerb2b42612015-11-20 12:02:17 -0800118} mdtab_shard;
Craig Tiller0e72ede2015-11-19 07:48:53 -0800119
Craig Tillere52bbb12016-11-10 15:34:06 -0800120static mdtab_shard g_shards[SHARD_COUNT];
Craig Tillerb2b42612015-11-20 12:02:17 -0800121
Craig Tillera59c16c2016-10-31 07:25:01 -0700122static void gc_mdtab(grpc_exec_ctx *exec_ctx, mdtab_shard *shard);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800123
Craig Tiller0e72ede2015-11-19 07:48:53 -0800124void grpc_mdctx_global_init(void) {
Craig Tillerb2b42612015-11-20 12:02:17 -0800125 /* initialize shards */
Craig Tiller1ad51e02016-11-17 16:42:21 -0800126 for (size_t i = 0; i < SHARD_COUNT; i++) {
Craig Tillere52bbb12016-11-10 15:34:06 -0800127 mdtab_shard *shard = &g_shards[i];
Craig Tillerb2b42612015-11-20 12:02:17 -0800128 gpr_mu_init(&shard->mu);
129 shard->count = 0;
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700130 gpr_atm_no_barrier_store(&shard->free_estimate, 0);
Craig Tillere52bbb12016-11-10 15:34:06 -0800131 shard->capacity = INITIAL_SHARD_CAPACITY;
Craig Tillerb2b42612015-11-20 12:02:17 -0800132 shard->elems = gpr_malloc(sizeof(*shard->elems) * shard->capacity);
133 memset(shard->elems, 0, 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 Tiller1ad51e02016-11-17 16:42:21 -0800138 for (size_t i = 0; i < SHARD_COUNT; i++) {
Craig Tillere52bbb12016-11-10 15:34:06 -0800139 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 Tiller0160de92016-11-18 08:46:46 -0800154static int is_mdelem_static(grpc_mdelem e) {
Craig Tiller9ecadce2016-11-18 14:52:25 -0800155 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 Tiller9ecadce2016-11-18 14:52:25 -0800161 interned_metadata *md DEBUG_ARGS) {
Craig Tiller1a65a232015-07-06 10:22:32 -0700162#ifdef GRPC_METADATA_REFCOUNT_DEBUG
163 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
David Garcia Quintas331b9c02016-09-12 18:37:05 -0700164 "ELM REF:%p:%zu->%zu: '%s' = '%s'", (void *)md,
Craig Tiller1a65a232015-07-06 10:22:32 -0700165 gpr_atm_no_barrier_load(&md->refcnt),
166 gpr_atm_no_barrier_load(&md->refcnt) + 1,
Craig Tiller5ef31ab2016-11-10 16:27:48 -0800167 grpc_mdstr_as_c_string((grpc_slice)md->key),
168 grpc_mdstr_as_c_string((grpc_slice)md->value));
Craig Tiller1a65a232015-07-06 10:22:32 -0700169#endif
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700170 if (0 == gpr_atm_no_barrier_fetch_add(&md->refcnt, 1)) {
171 gpr_atm_no_barrier_fetch_add(&shard->free_estimate, -1);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800172 }
173}
174
Craig Tillera59c16c2016-10-31 07:25:01 -0700175static void gc_mdtab(grpc_exec_ctx *exec_ctx, mdtab_shard *shard) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800176 size_t i;
Craig Tiller9ecadce2016-11-18 14:52:25 -0800177 interned_metadata **prev_next;
178 interned_metadata *md, *next;
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700179 gpr_atm num_freed = 0;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800180
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800181 GPR_TIMER_BEGIN("gc_mdtab", 0);
Craig Tillerb2b42612015-11-20 12:02:17 -0800182 for (i = 0; i < shard->capacity; i++) {
183 prev_next = &shard->elems[i];
184 for (md = shard->elems[i]; md; md = next) {
Craig Tiller8344daa2015-10-09 18:10:57 -0700185 void *user_data = (void *)gpr_atm_no_barrier_load(&md->user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800186 next = md->bucket_next;
Craig Tiller9fa41b92015-04-10 15:08:03 -0700187 if (gpr_atm_acq_load(&md->refcnt) == 0) {
Craig Tillere52bbb12016-11-10 15:34:06 -0800188 grpc_slice_unref_internal(exec_ctx, md->key);
189 grpc_slice_unref_internal(exec_ctx, md->value);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800190 if (md->user_data) {
Craig Tiller8344daa2015-10-09 18:10:57 -0700191 ((destroy_user_data_func)gpr_atm_no_barrier_load(
192 &md->destroy_user_data))(user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800193 }
194 gpr_free(md);
195 *prev_next = next;
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700196 num_freed++;
Craig Tillerb2b42612015-11-20 12:02:17 -0800197 shard->count--;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800198 } else {
199 prev_next = &md->bucket_next;
200 }
201 }
202 }
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700203 gpr_atm_no_barrier_fetch_add(&shard->free_estimate, -num_freed);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800204 GPR_TIMER_END("gc_mdtab", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800205}
206
Craig Tillerb2b42612015-11-20 12:02:17 -0800207static void grow_mdtab(mdtab_shard *shard) {
208 size_t capacity = shard->capacity * 2;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800209 size_t i;
Craig Tiller9ecadce2016-11-18 14:52:25 -0800210 interned_metadata **mdtab;
211 interned_metadata *md, *next;
Craig Tiller7536af02015-12-22 13:49:30 -0800212 uint32_t hash;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800213
214 GPR_TIMER_BEGIN("grow_mdtab", 0);
215
Craig Tiller9ecadce2016-11-18 14:52:25 -0800216 mdtab = gpr_malloc(sizeof(interned_metadata *) * capacity);
217 memset(mdtab, 0, sizeof(interned_metadata *) * capacity);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800218
Craig Tillerb2b42612015-11-20 12:02:17 -0800219 for (i = 0; i < shard->capacity; i++) {
220 for (md = shard->elems[i]; md; md = next) {
221 size_t idx;
Craig Tillere52bbb12016-11-10 15:34:06 -0800222 hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash(md->key),
223 grpc_slice_hash(md->value));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800224 next = md->bucket_next;
Craig Tillere52bbb12016-11-10 15:34:06 -0800225 idx = TABLE_IDX(hash, capacity);
Craig Tillerb2b42612015-11-20 12:02:17 -0800226 md->bucket_next = mdtab[idx];
227 mdtab[idx] = md;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800228 }
229 }
230
Craig Tillerb2b42612015-11-20 12:02:17 -0800231 gpr_free(shard->elems);
232 shard->elems = mdtab;
233 shard->capacity = capacity;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800234
235 GPR_TIMER_END("grow_mdtab", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800236}
237
Craig Tillera59c16c2016-10-31 07:25:01 -0700238static void rehash_mdtab(grpc_exec_ctx *exec_ctx, mdtab_shard *shard) {
Craig Tiller139098c2016-06-06 08:10:08 -0700239 if (gpr_atm_no_barrier_load(&shard->free_estimate) >
240 (gpr_atm)(shard->capacity / 4)) {
Craig Tillera59c16c2016-10-31 07:25:01 -0700241 gc_mdtab(exec_ctx, shard);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800242 } else {
Craig Tillerb2b42612015-11-20 12:02:17 -0800243 grow_mdtab(shard);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800244 }
245}
246
Craig Tiller9ecadce2016-11-18 14:52:25 -0800247grpc_mdelem grpc_mdelem_create(
248 grpc_exec_ctx *exec_ctx, grpc_slice key, grpc_slice value,
249 grpc_mdelem_data *compatible_external_backing_store) {
250 if (!grpc_slice_is_interned(key) || !grpc_slice_is_interned(value)) {
251 if (compatible_external_backing_store != NULL) {
252 return GRPC_MAKE_MDELEM(compatible_external_backing_store,
253 GRPC_MDELEM_STORAGE_EXTERNAL);
254 }
255
256 allocated_metadata *allocated = gpr_malloc(sizeof(*allocated));
257 allocated->key = grpc_slice_ref_internal(key);
258 allocated->value = grpc_slice_ref_internal(value);
259 gpr_atm_rel_store(&allocated->refcnt, 1);
260 return GRPC_MAKE_MDELEM(allocated, GRPC_MDELEM_STORAGE_ALLOCATED);
261 }
Craig Tiller1ad51e02016-11-17 16:42:21 -0800262
Craig Tiller0160de92016-11-18 08:46:46 -0800263 grpc_mdelem static_elem = grpc_static_mdelem_for_static_strings(
Craig Tiller1ad51e02016-11-17 16:42:21 -0800264 grpc_static_metadata_index(key), grpc_static_metadata_index(value));
Craig Tiller0160de92016-11-18 08:46:46 -0800265 if (!GRPC_MDISNULL(static_elem)) {
Craig Tiller1ad51e02016-11-17 16:42:21 -0800266 return static_elem;
267 }
268
Craig Tillere52bbb12016-11-10 15:34:06 -0800269 uint32_t hash =
270 GRPC_MDSTR_KV_HASH(grpc_slice_hash(key), grpc_slice_hash(value));
Craig Tiller9ecadce2016-11-18 14:52:25 -0800271 interned_metadata *md;
Craig Tillere52bbb12016-11-10 15:34:06 -0800272 mdtab_shard *shard = &g_shards[SHARD_IDX(hash)];
Craig Tillerb2b42612015-11-20 12:02:17 -0800273 size_t idx;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800274
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800275 GPR_TIMER_BEGIN("grpc_mdelem_from_metadata_strings", 0);
276
Craig Tillerb2b42612015-11-20 12:02:17 -0800277 gpr_mu_lock(&shard->mu);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800278
Craig Tillere52bbb12016-11-10 15:34:06 -0800279 idx = TABLE_IDX(hash, shard->capacity);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800280 /* search for an existing pair */
Craig Tillerb2b42612015-11-20 12:02:17 -0800281 for (md = shard->elems[idx]; md; md = md->bucket_next) {
Craig Tillere52bbb12016-11-10 15:34:06 -0800282 if (grpc_slice_cmp(key, md->key) == 0 &&
283 grpc_slice_cmp(value, md->value) == 0) {
Craig Tillerb2b42612015-11-20 12:02:17 -0800284 REF_MD_LOCKED(shard, md);
Craig Tillerb2b42612015-11-20 12:02:17 -0800285 gpr_mu_unlock(&shard->mu);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800286 GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
Craig Tiller9ecadce2016-11-18 14:52:25 -0800287 return GRPC_MAKE_MDELEM(md, GRPC_MDELEM_STORAGE_INTERNED);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800288 }
289 }
290
291 /* not found: create a new pair */
Craig Tiller9ecadce2016-11-18 14:52:25 -0800292 md = gpr_malloc(sizeof(interned_metadata));
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700293 gpr_atm_rel_store(&md->refcnt, 1);
Craig Tiller9ecadce2016-11-18 14:52:25 -0800294 md->key = grpc_slice_ref_internal(key);
295 md->value = grpc_slice_ref_internal(value);
Craig Tiller8344daa2015-10-09 18:10:57 -0700296 md->user_data = 0;
297 md->destroy_user_data = 0;
Craig Tillerb2b42612015-11-20 12:02:17 -0800298 md->bucket_next = shard->elems[idx];
299 shard->elems[idx] = md;
Craig Tiller83901532015-07-10 14:02:45 -0700300 gpr_mu_init(&md->mu_user_data);
Craig Tiller1a65a232015-07-06 10:22:32 -0700301#ifdef GRPC_METADATA_REFCOUNT_DEBUG
David Garcia Quintas331b9c02016-09-12 18:37:05 -0700302 gpr_log(GPR_DEBUG, "ELM NEW:%p:%zu: '%s' = '%s'", (void *)md,
Craig Tiller1a65a232015-07-06 10:22:32 -0700303 gpr_atm_no_barrier_load(&md->refcnt),
Craig Tiller5ef31ab2016-11-10 16:27:48 -0800304 grpc_mdstr_as_c_string((grpc_slice)md->key),
305 grpc_mdstr_as_c_string((grpc_slice)md->value));
Craig Tiller1a65a232015-07-06 10:22:32 -0700306#endif
Craig Tillerb2b42612015-11-20 12:02:17 -0800307 shard->count++;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800308
Craig Tillerb2b42612015-11-20 12:02:17 -0800309 if (shard->count > shard->capacity * 2) {
Craig Tillera59c16c2016-10-31 07:25:01 -0700310 rehash_mdtab(exec_ctx, shard);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800311 }
312
Craig Tillerb2b42612015-11-20 12:02:17 -0800313 gpr_mu_unlock(&shard->mu);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800314
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800315 GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
316
Craig Tiller9ecadce2016-11-18 14:52:25 -0800317 return GRPC_MAKE_MDELEM(md, GRPC_MDELEM_STORAGE_INTERNED);
318}
319
320grpc_mdelem grpc_mdelem_from_slices(grpc_exec_ctx *exec_ctx, grpc_slice key,
321 grpc_slice value) {
322 grpc_mdelem out = grpc_mdelem_create(exec_ctx, key, value, NULL);
323 grpc_slice_unref_internal(exec_ctx, key);
324 grpc_slice_unref_internal(exec_ctx, value);
325 return out;
326}
327
328grpc_mdelem grpc_mdelem_from_grpc_metadata(grpc_exec_ctx *exec_ctx,
329 grpc_metadata *metadata) {
330 return grpc_mdelem_create(exec_ctx, metadata->key, metadata->value,
331 (grpc_mdelem_data *)metadata);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800332}
333
yang-g24ba7c12016-03-30 16:19:43 -0700334static size_t get_base64_encoded_size(size_t raw_length) {
335 static const uint8_t tail_xtra[3] = {0, 2, 3};
336 return raw_length / 3 * 4 + tail_xtra[raw_length % 3];
337}
338
Craig Tiller0160de92016-11-18 08:46:46 -0800339size_t grpc_mdelem_get_size_in_hpack_table(grpc_mdelem elem) {
340 size_t overhead_and_key = 32 + GRPC_SLICE_LENGTH(GRPC_MDKEY(elem));
341 size_t value_len = GRPC_SLICE_LENGTH(GRPC_MDVALUE(elem));
342 if (grpc_is_binary_header(GRPC_MDKEY(elem))) {
Craig Tillere52bbb12016-11-10 15:34:06 -0800343 return overhead_and_key + get_base64_encoded_size(value_len);
yang-gcb7a8022016-03-30 14:58:53 -0700344 } else {
Craig Tillere52bbb12016-11-10 15:34:06 -0800345 return overhead_and_key + value_len;
yang-gcb7a8022016-03-30 14:58:53 -0700346 }
347}
348
Craig Tiller0160de92016-11-18 08:46:46 -0800349grpc_mdelem grpc_mdelem_ref(grpc_mdelem gmd DEBUG_ARGS) {
Craig Tiller9ecadce2016-11-18 14:52:25 -0800350 switch (GRPC_MDELEM_STORAGE(gmd)) {
351 case GRPC_MDELEM_STORAGE_EXTERNAL:
352 case GRPC_MDELEM_STORAGE_STATIC:
353 break;
354 case GRPC_MDELEM_STORAGE_INTERNED: {
355 interned_metadata *md = (interned_metadata *)GRPC_MDELEM_DATA(gmd);
Craig Tiller1a65a232015-07-06 10:22:32 -0700356#ifdef GRPC_METADATA_REFCOUNT_DEBUG
Craig Tiller9ecadce2016-11-18 14:52:25 -0800357 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
358 "ELM REF:%p:%zu->%zu: '%s' = '%s'", (void *)md,
359 gpr_atm_no_barrier_load(&md->refcnt),
360 gpr_atm_no_barrier_load(&md->refcnt) + 1,
361 grpc_mdstr_as_c_string((grpc_slice)md->key),
362 grpc_mdstr_as_c_string((grpc_slice)md->value));
Craig Tiller1a65a232015-07-06 10:22:32 -0700363#endif
Craig Tiller9ecadce2016-11-18 14:52:25 -0800364 /* we can assume the ref count is >= 1 as the application is calling
365 this function - meaning that no adjustment to mdtab_free is necessary,
366 simplifying the logic here to be just an atomic increment */
367 /* use C assert to have this removed in opt builds */
368 GPR_ASSERT(gpr_atm_no_barrier_load(&md->refcnt) >= 1);
369 gpr_atm_no_barrier_fetch_add(&md->refcnt, 1);
370 break;
371 }
372 case GRPC_MDELEM_STORAGE_ALLOCATED: {
373 allocated_metadata *md = (allocated_metadata *)GRPC_MDELEM_DATA(gmd);
374#ifdef GRPC_METADATA_REFCOUNT_DEBUG
375 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
376 "ELM REF:%p:%zu->%zu: '%s' = '%s'", (void *)md,
377 gpr_atm_no_barrier_load(&md->refcnt),
378 gpr_atm_no_barrier_load(&md->refcnt) + 1,
379 grpc_mdstr_as_c_string((grpc_slice)md->key),
380 grpc_mdstr_as_c_string((grpc_slice)md->value));
381#endif
382 /* we can assume the ref count is >= 1 as the application is calling
383 this function - meaning that no adjustment to mdtab_free is necessary,
384 simplifying the logic here to be just an atomic increment */
385 /* use C assert to have this removed in opt builds */
386 gpr_atm_no_barrier_fetch_add(&md->refcnt, 1);
387 break;
388 }
389 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800390 return gmd;
391}
392
Craig Tiller0160de92016-11-18 08:46:46 -0800393void grpc_mdelem_unref(grpc_exec_ctx *exec_ctx, grpc_mdelem gmd DEBUG_ARGS) {
Craig Tiller9ecadce2016-11-18 14:52:25 -0800394 switch (GRPC_MDELEM_STORAGE(gmd)) {
395 case GRPC_MDELEM_STORAGE_EXTERNAL:
396 case GRPC_MDELEM_STORAGE_STATIC:
397 break;
398 case GRPC_MDELEM_STORAGE_INTERNED: {
399 interned_metadata *md = (interned_metadata *)GRPC_MDELEM_DATA(gmd);
Craig Tiller1a65a232015-07-06 10:22:32 -0700400#ifdef GRPC_METADATA_REFCOUNT_DEBUG
Craig Tiller9ecadce2016-11-18 14:52:25 -0800401 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
402 "ELM UNREF:%p:%zu->%zu: '%s' = '%s'", (void *)md,
403 gpr_atm_no_barrier_load(&md->refcnt),
404 gpr_atm_no_barrier_load(&md->refcnt) - 1,
405 grpc_mdstr_as_c_string((grpc_slice)md->key),
406 grpc_mdstr_as_c_string((grpc_slice)md->value));
Craig Tiller1a65a232015-07-06 10:22:32 -0700407#endif
Craig Tiller9ecadce2016-11-18 14:52:25 -0800408 uint32_t hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash(md->key),
409 grpc_slice_hash(md->value));
410 const gpr_atm prev_refcount = gpr_atm_full_fetch_add(&md->refcnt, -1);
411 GPR_ASSERT(prev_refcount >= 1);
412 if (1 == prev_refcount) {
413 /* once the refcount hits zero, some other thread can come along and
414 free md at any time: it's unsafe from this point on to access it */
415 mdtab_shard *shard = &g_shards[SHARD_IDX(hash)];
416 gpr_atm_no_barrier_fetch_add(&shard->free_estimate, 1);
417 }
418 break;
419 }
420 case GRPC_MDELEM_STORAGE_ALLOCATED: {
421 allocated_metadata *md = (allocated_metadata *)GRPC_MDELEM_DATA(gmd);
422#ifdef GRPC_METADATA_REFCOUNT_DEBUG
423 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
424 "ELM UNREF:%p:%zu->%zu: '%s' = '%s'", (void *)md,
425 gpr_atm_no_barrier_load(&md->refcnt),
426 gpr_atm_no_barrier_load(&md->refcnt) - 1,
427 grpc_mdstr_as_c_string((grpc_slice)md->key),
428 grpc_mdstr_as_c_string((grpc_slice)md->value));
429#endif
430 const gpr_atm prev_refcount = gpr_atm_full_fetch_add(&md->refcnt, -1);
431 GPR_ASSERT(prev_refcount >= 1);
432 if (1 == prev_refcount) {
433 grpc_slice_unref_internal(exec_ctx, md->key);
434 grpc_slice_unref_internal(exec_ctx, md->value);
435 gpr_free(md);
436 }
437 break;
438 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800439 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800440}
441
Craig Tiller0160de92016-11-18 08:46:46 -0800442void *grpc_mdelem_get_user_data(grpc_mdelem md, void (*destroy_func)(void *)) {
Craig Tiller9ecadce2016-11-18 14:52:25 -0800443 switch (GRPC_MDELEM_STORAGE(md)) {
444 case GRPC_MDELEM_STORAGE_EXTERNAL:
445 case GRPC_MDELEM_STORAGE_ALLOCATED:
446 return NULL;
447 case GRPC_MDELEM_STORAGE_STATIC:
448 return (void *)grpc_static_mdelem_user_data[GRPC_MDELEM_DATA(md) -
449 grpc_static_mdelem_table];
450 case GRPC_MDELEM_STORAGE_INTERNED: {
451 interned_metadata *im = (interned_metadata *)GRPC_MDELEM_DATA(md);
452 void *result;
453 if (gpr_atm_acq_load(&im->destroy_user_data) == (gpr_atm)destroy_func) {
454 return (void *)gpr_atm_no_barrier_load(&im->user_data);
455 } else {
456 return NULL;
457 }
458 return result;
459 }
Craig Tillerb2b42612015-11-20 12:02:17 -0800460 }
Craig Tiller9ecadce2016-11-18 14:52:25 -0800461 GPR_UNREACHABLE_CODE(return NULL);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800462}
463
Craig Tiller0160de92016-11-18 08:46:46 -0800464void *grpc_mdelem_set_user_data(grpc_mdelem md, void (*destroy_func)(void *),
Craig Tiller738e6db2016-11-15 09:29:44 -0800465 void *user_data) {
Craig Tiller9ecadce2016-11-18 14:52:25 -0800466 switch (GRPC_MDELEM_STORAGE(md)) {
467 case GRPC_MDELEM_STORAGE_EXTERNAL:
468 case GRPC_MDELEM_STORAGE_ALLOCATED:
Craig Tiller83901532015-07-10 14:02:45 -0700469 destroy_func(user_data);
Craig Tiller9ecadce2016-11-18 14:52:25 -0800470 return NULL;
471 case GRPC_MDELEM_STORAGE_STATIC:
472 destroy_func(user_data);
473 return (void *)grpc_static_mdelem_user_data[GRPC_MDELEM_DATA(md) -
474 grpc_static_mdelem_table];
475 case GRPC_MDELEM_STORAGE_INTERNED: {
476 interned_metadata *im = (interned_metadata *)GRPC_MDELEM_DATA(md);
477 GPR_ASSERT(!is_mdelem_static(md));
478 GPR_ASSERT((user_data == NULL) == (destroy_func == NULL));
479 gpr_mu_lock(&im->mu_user_data);
480 if (gpr_atm_no_barrier_load(&im->destroy_user_data)) {
481 /* user data can only be set once */
482 gpr_mu_unlock(&im->mu_user_data);
483 if (destroy_func != NULL) {
484 destroy_func(user_data);
485 }
486 return (void *)gpr_atm_no_barrier_load(&im->user_data);
487 }
488 gpr_atm_no_barrier_store(&im->user_data, (gpr_atm)user_data);
489 gpr_atm_rel_store(&im->destroy_user_data, (gpr_atm)destroy_func);
490 gpr_mu_unlock(&im->mu_user_data);
491 return user_data;
Craig Tiller83901532015-07-10 14:02:45 -0700492 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800493 }
Craig Tiller9ecadce2016-11-18 14:52:25 -0800494 GPR_UNREACHABLE_CODE(return NULL);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800495}
Craig Tiller0160de92016-11-18 08:46:46 -0800496
497bool grpc_mdelem_eq(grpc_mdelem a, grpc_mdelem b) {
498 if (a.payload == b.payload) return true;
499 return 0 == grpc_slice_cmp(GRPC_MDKEY(a), GRPC_MDKEY(b)) &&
500 0 == grpc_slice_cmp(GRPC_MDVALUE(a), GRPC_MDVALUE(b));
501}