blob: a16f0508f05296de23f79b1268799d74fa1df9cb [file] [log] [blame]
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001/*
2 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02003 * Copyright 2015 gRPC authors.
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08004 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02005 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08008 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02009 * http://www.apache.org/licenses/LICENSE-2.0
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080010 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +020011 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080016 *
17 */
18
Craig Tiller9533d042016-03-25 17:11:06 -070019#include "src/core/lib/transport/metadata.h"
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080020
Craig Tiller9fa41b92015-04-10 15:08:03 -070021#include <assert.h>
Yash Tibrewalfcd26bc2017-09-25 15:08:28 -070022#include <inttypes.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080023#include <stddef.h>
24#include <string.h>
25
Craig Tillerebdef9d2015-11-19 17:09:49 -080026#include <grpc/compression.h>
yang-gcb7a8022016-03-30 14:58:53 -070027#include <grpc/grpc.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080028#include <grpc/support/alloc.h>
Craig Tiller9fa41b92015-04-10 15:08:03 -070029#include <grpc/support/atm.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080030#include <grpc/support/log.h>
Craig Tillerebdef9d2015-11-19 17:09:49 -080031#include <grpc/support/string_util.h>
Craig Tiller9d35a1f2015-11-02 14:16:12 -080032#include <grpc/support/time.h>
Craig Tiller0cb803d2016-03-02 22:17:24 -080033
Craig Tiller9533d042016-03-25 17:11:06 -070034#include "src/core/lib/iomgr/iomgr_internal.h"
35#include "src/core/lib/profiling/timers.h"
Craig Tillera59c16c2016-10-31 07:25:01 -070036#include "src/core/lib/slice/slice_internal.h"
Craig Tiller7c70b6c2017-01-23 07:48:42 -080037#include "src/core/lib/slice/slice_string_helpers.h"
Craig Tiller9533d042016-03-25 17:11:06 -070038#include "src/core/lib/support/murmur_hash.h"
39#include "src/core/lib/support/string.h"
Craig Tiller9533d042016-03-25 17:11:06 -070040#include "src/core/lib/transport/static_metadata.h"
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080041
Craig Tiller70b080d2015-11-19 08:04:48 -080042/* There are two kinds of mdelem and mdstr instances.
43 * Static instances are declared in static_metadata.{h,c} and
44 * are initialized by grpc_mdctx_global_init().
45 * Dynamic instances are stored in hash tables on grpc_mdctx, and are backed
46 * by internal_string and internal_element structures.
47 * Internal helper functions here-in (is_mdstr_static, is_mdelem_static) are
48 * used to determine which kind of element a pointer refers to.
49 */
50
ncteisen4b584052017-06-08 16:44:38 -070051#ifndef NDEBUG
ncteisen7712c7c2017-07-12 23:11:27 -070052grpc_tracer_flag grpc_trace_metadata =
53 GRPC_TRACER_INITIALIZER(false, "metadata");
Craig Tiller1a65a232015-07-06 10:22:32 -070054#define DEBUG_ARGS , const char *file, int line
55#define FWD_DEBUG_ARGS , file, line
Craig Tillerb2b42612015-11-20 12:02:17 -080056#define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s), __FILE__, __LINE__)
Craig Tiller1a65a232015-07-06 10:22:32 -070057#else
58#define DEBUG_ARGS
59#define FWD_DEBUG_ARGS
Craig Tillerb2b42612015-11-20 12:02:17 -080060#define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s))
Craig Tiller1a65a232015-07-06 10:22:32 -070061#endif
62
Craig Tiller7c70b6c2017-01-23 07:48:42 -080063#define INITIAL_SHARD_CAPACITY 8
64#define LOG2_SHARD_COUNT 4
65#define SHARD_COUNT ((size_t)(1 << LOG2_SHARD_COUNT))
66
67#define TABLE_IDX(hash, capacity) (((hash) >> (LOG2_SHARD_COUNT)) % (capacity))
68#define SHARD_IDX(hash) ((hash) & ((1 << (LOG2_SHARD_COUNT)) - 1))
Craig Tillerb2b42612015-11-20 12:02:17 -080069
Craig Tillerbaa14a92017-11-03 09:09:36 -070070typedef void (*destroy_user_data_func)(void* user_data);
Craig Tiller8344daa2015-10-09 18:10:57 -070071
Craig Tiller7c70b6c2017-01-23 07:48:42 -080072/* Shadow structure for grpc_mdelem_data for interned elements */
73typedef struct interned_metadata {
74 /* must be byte compatible with grpc_mdelem_data */
75 grpc_slice key;
76 grpc_slice value;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080077
Craig Tillerb2b42612015-11-20 12:02:17 -080078 /* private only data */
Craig Tiller9fa41b92015-04-10 15:08:03 -070079 gpr_atm refcnt;
80
Craig Tiller83901532015-07-10 14:02:45 -070081 gpr_mu mu_user_data;
Craig Tiller8344daa2015-10-09 18:10:57 -070082 gpr_atm destroy_user_data;
83 gpr_atm user_data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080084
Craig Tillerbaa14a92017-11-03 09:09:36 -070085 struct interned_metadata* bucket_next;
Craig Tiller7c70b6c2017-01-23 07:48:42 -080086} interned_metadata;
Craig Tiller9ecadce2016-11-18 14:52:25 -080087
Craig Tiller7c70b6c2017-01-23 07:48:42 -080088/* Shadow structure for grpc_mdelem_data for allocated elements */
89typedef struct allocated_metadata {
90 /* must be byte compatible with grpc_mdelem_data */
91 grpc_slice key;
92 grpc_slice value;
93
94 /* private only data */
95 gpr_atm refcnt;
96} allocated_metadata;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080097
Craig Tillerb2b42612015-11-20 12:02:17 -080098typedef struct mdtab_shard {
99 gpr_mu mu;
Craig Tillerbaa14a92017-11-03 09:09:36 -0700100 interned_metadata** elems;
Craig Tillerb2b42612015-11-20 12:02:17 -0800101 size_t count;
102 size_t capacity;
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700103 /** Estimate of the number of unreferenced mdelems in the hash table.
104 This will eventually converge to the exact number, but it's instantaneous
105 accuracy is not guaranteed */
106 gpr_atm free_estimate;
Craig Tillerb2b42612015-11-20 12:02:17 -0800107} mdtab_shard;
Craig Tiller0e72ede2015-11-19 07:48:53 -0800108
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800109static mdtab_shard g_shards[SHARD_COUNT];
Craig Tillerb2b42612015-11-20 12:02:17 -0800110
Craig Tillerbaa14a92017-11-03 09:09:36 -0700111static void gc_mdtab(grpc_exec_ctx* exec_ctx, mdtab_shard* shard);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800112
Craig Tiller0e72ede2015-11-19 07:48:53 -0800113void grpc_mdctx_global_init(void) {
Craig Tillerb2b42612015-11-20 12:02:17 -0800114 /* initialize shards */
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800115 for (size_t i = 0; i < SHARD_COUNT; i++) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700116 mdtab_shard* shard = &g_shards[i];
Craig Tillerb2b42612015-11-20 12:02:17 -0800117 gpr_mu_init(&shard->mu);
118 shard->count = 0;
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700119 gpr_atm_no_barrier_store(&shard->free_estimate, 0);
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800120 shard->capacity = INITIAL_SHARD_CAPACITY;
Craig Tillerbaa14a92017-11-03 09:09:36 -0700121 shard->elems = (interned_metadata**)gpr_zalloc(sizeof(*shard->elems) *
122 shard->capacity);
Craig Tiller0e72ede2015-11-19 07:48:53 -0800123 }
124}
125
Craig Tillerbaa14a92017-11-03 09:09:36 -0700126void grpc_mdctx_global_shutdown(grpc_exec_ctx* exec_ctx) {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800127 for (size_t i = 0; i < SHARD_COUNT; i++) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700128 mdtab_shard* shard = &g_shards[i];
Craig Tillerb2b42612015-11-20 12:02:17 -0800129 gpr_mu_destroy(&shard->mu);
Craig Tillera59c16c2016-10-31 07:25:01 -0700130 gc_mdtab(exec_ctx, shard);
Craig Tiller34075442015-11-23 16:16:54 -0800131 /* TODO(ctiller): GPR_ASSERT(shard->count == 0); */
132 if (shard->count != 0) {
Yuchen Zeng64c0e8d2016-06-10 11:19:51 -0700133 gpr_log(GPR_DEBUG, "WARNING: %" PRIuPTR " metadata elements were leaked",
yang-gd88e1d82015-12-02 13:23:33 -0800134 shard->count);
Craig Tiller0cb803d2016-03-02 22:17:24 -0800135 if (grpc_iomgr_abort_on_leaks()) {
136 abort();
137 }
Craig Tiller34075442015-11-23 16:16:54 -0800138 }
Craig Tillerb2b42612015-11-20 12:02:17 -0800139 gpr_free(shard->elems);
140 }
Craig Tiller0e72ede2015-11-19 07:48:53 -0800141}
142
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800143static int is_mdelem_static(grpc_mdelem e) {
144 return GRPC_MDELEM_DATA(e) >= &grpc_static_mdelem_table[0] &&
145 GRPC_MDELEM_DATA(e) <
146 &grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
Craig Tiller0e72ede2015-11-19 07:48:53 -0800147}
148
Craig Tillerbaa14a92017-11-03 09:09:36 -0700149static void ref_md_locked(mdtab_shard* shard,
150 interned_metadata* md DEBUG_ARGS) {
ncteisen4b584052017-06-08 16:44:38 -0700151#ifndef NDEBUG
152 if (GRPC_TRACER_ON(grpc_trace_metadata)) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700153 char* key_str = grpc_slice_to_c_string(md->key);
154 char* value_str = grpc_slice_to_c_string(md->value);
ncteisen4b584052017-06-08 16:44:38 -0700155 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
Craig Tillerbaa14a92017-11-03 09:09:36 -0700156 "ELM REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", (void*)md,
ncteisen4b584052017-06-08 16:44:38 -0700157 gpr_atm_no_barrier_load(&md->refcnt),
158 gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str);
159 gpr_free(key_str);
160 gpr_free(value_str);
161 }
Craig Tiller1a65a232015-07-06 10:22:32 -0700162#endif
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700163 if (0 == gpr_atm_no_barrier_fetch_add(&md->refcnt, 1)) {
164 gpr_atm_no_barrier_fetch_add(&shard->free_estimate, -1);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800165 }
166}
167
Craig Tillerbaa14a92017-11-03 09:09:36 -0700168static void gc_mdtab(grpc_exec_ctx* exec_ctx, mdtab_shard* shard) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800169 size_t i;
Craig Tillerbaa14a92017-11-03 09:09:36 -0700170 interned_metadata** prev_next;
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800171 interned_metadata *md, *next;
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700172 gpr_atm num_freed = 0;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800173
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800174 GPR_TIMER_BEGIN("gc_mdtab", 0);
Craig Tillerb2b42612015-11-20 12:02:17 -0800175 for (i = 0; i < shard->capacity; i++) {
176 prev_next = &shard->elems[i];
177 for (md = shard->elems[i]; md; md = next) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700178 void* user_data = (void*)gpr_atm_no_barrier_load(&md->user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800179 next = md->bucket_next;
Craig Tiller9fa41b92015-04-10 15:08:03 -0700180 if (gpr_atm_acq_load(&md->refcnt) == 0) {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800181 grpc_slice_unref_internal(exec_ctx, md->key);
182 grpc_slice_unref_internal(exec_ctx, md->value);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800183 if (md->user_data) {
Craig Tiller8344daa2015-10-09 18:10:57 -0700184 ((destroy_user_data_func)gpr_atm_no_barrier_load(
185 &md->destroy_user_data))(user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800186 }
187 gpr_free(md);
188 *prev_next = next;
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700189 num_freed++;
Craig Tillerb2b42612015-11-20 12:02:17 -0800190 shard->count--;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800191 } else {
192 prev_next = &md->bucket_next;
193 }
194 }
195 }
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700196 gpr_atm_no_barrier_fetch_add(&shard->free_estimate, -num_freed);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800197 GPR_TIMER_END("gc_mdtab", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800198}
199
Craig Tillerbaa14a92017-11-03 09:09:36 -0700200static void grow_mdtab(mdtab_shard* shard) {
Craig Tillerb2b42612015-11-20 12:02:17 -0800201 size_t capacity = shard->capacity * 2;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800202 size_t i;
Craig Tillerbaa14a92017-11-03 09:09:36 -0700203 interned_metadata** mdtab;
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800204 interned_metadata *md, *next;
Craig Tiller7536af02015-12-22 13:49:30 -0800205 uint32_t hash;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800206
207 GPR_TIMER_BEGIN("grow_mdtab", 0);
208
Yash Tibrewalca3c1c02017-09-07 22:47:16 -0700209 mdtab =
Craig Tillerbaa14a92017-11-03 09:09:36 -0700210 (interned_metadata**)gpr_zalloc(sizeof(interned_metadata*) * capacity);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800211
Craig Tillerb2b42612015-11-20 12:02:17 -0800212 for (i = 0; i < shard->capacity; i++) {
213 for (md = shard->elems[i]; md; md = next) {
214 size_t idx;
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800215 hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash(md->key),
216 grpc_slice_hash(md->value));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800217 next = md->bucket_next;
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800218 idx = TABLE_IDX(hash, capacity);
Craig Tillerb2b42612015-11-20 12:02:17 -0800219 md->bucket_next = mdtab[idx];
220 mdtab[idx] = md;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800221 }
222 }
223
Craig Tillerb2b42612015-11-20 12:02:17 -0800224 gpr_free(shard->elems);
225 shard->elems = mdtab;
226 shard->capacity = capacity;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800227
228 GPR_TIMER_END("grow_mdtab", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800229}
230
Craig Tillerbaa14a92017-11-03 09:09:36 -0700231static void rehash_mdtab(grpc_exec_ctx* exec_ctx, mdtab_shard* shard) {
Craig Tiller139098c2016-06-06 08:10:08 -0700232 if (gpr_atm_no_barrier_load(&shard->free_estimate) >
233 (gpr_atm)(shard->capacity / 4)) {
Craig Tillera59c16c2016-10-31 07:25:01 -0700234 gc_mdtab(exec_ctx, shard);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800235 } else {
Craig Tillerb2b42612015-11-20 12:02:17 -0800236 grow_mdtab(shard);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800237 }
238}
239
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800240grpc_mdelem grpc_mdelem_create(
Craig Tillerbaa14a92017-11-03 09:09:36 -0700241 grpc_exec_ctx* exec_ctx, grpc_slice key, grpc_slice value,
242 grpc_mdelem_data* compatible_external_backing_store) {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800243 if (!grpc_slice_is_interned(key) || !grpc_slice_is_interned(value)) {
Craig Tiller4782d922017-11-10 09:53:21 -0800244 if (compatible_external_backing_store != nullptr) {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800245 return GRPC_MAKE_MDELEM(compatible_external_backing_store,
246 GRPC_MDELEM_STORAGE_EXTERNAL);
247 }
248
Craig Tillerbaa14a92017-11-03 09:09:36 -0700249 allocated_metadata* allocated =
250 (allocated_metadata*)gpr_malloc(sizeof(*allocated));
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800251 allocated->key = grpc_slice_ref_internal(key);
252 allocated->value = grpc_slice_ref_internal(value);
253 gpr_atm_rel_store(&allocated->refcnt, 1);
ncteisen4b584052017-06-08 16:44:38 -0700254#ifndef NDEBUG
255 if (GRPC_TRACER_ON(grpc_trace_metadata)) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700256 char* key_str = grpc_slice_to_c_string(allocated->key);
257 char* value_str = grpc_slice_to_c_string(allocated->value);
ncteisen3ac64f82017-06-19 17:35:44 -0700258 gpr_log(GPR_DEBUG, "ELM ALLOC:%p:%" PRIdPTR ": '%s' = '%s'",
Craig Tillerbaa14a92017-11-03 09:09:36 -0700259 (void*)allocated, gpr_atm_no_barrier_load(&allocated->refcnt),
ncteisen3ac64f82017-06-19 17:35:44 -0700260 key_str, value_str);
ncteisen4b584052017-06-08 16:44:38 -0700261 gpr_free(key_str);
262 gpr_free(value_str);
263 }
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800264#endif
265 return GRPC_MAKE_MDELEM(allocated, GRPC_MDELEM_STORAGE_ALLOCATED);
266 }
267
268 if (GRPC_IS_STATIC_METADATA_STRING(key) &&
269 GRPC_IS_STATIC_METADATA_STRING(value)) {
270 grpc_mdelem static_elem = grpc_static_mdelem_for_static_strings(
271 GRPC_STATIC_METADATA_INDEX(key), GRPC_STATIC_METADATA_INDEX(value));
272 if (!GRPC_MDISNULL(static_elem)) {
273 return static_elem;
274 }
275 }
276
277 uint32_t hash =
278 GRPC_MDSTR_KV_HASH(grpc_slice_hash(key), grpc_slice_hash(value));
Craig Tillerbaa14a92017-11-03 09:09:36 -0700279 interned_metadata* md;
280 mdtab_shard* shard = &g_shards[SHARD_IDX(hash)];
Craig Tillerb2b42612015-11-20 12:02:17 -0800281 size_t idx;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800282
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800283 GPR_TIMER_BEGIN("grpc_mdelem_from_metadata_strings", 0);
284
Craig Tillerb2b42612015-11-20 12:02:17 -0800285 gpr_mu_lock(&shard->mu);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800286
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800287 idx = TABLE_IDX(hash, shard->capacity);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800288 /* search for an existing pair */
Craig Tillerb2b42612015-11-20 12:02:17 -0800289 for (md = shard->elems[idx]; md; md = md->bucket_next) {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800290 if (grpc_slice_eq(key, md->key) && grpc_slice_eq(value, md->value)) {
Craig Tillerb2b42612015-11-20 12:02:17 -0800291 REF_MD_LOCKED(shard, md);
Craig Tillerb2b42612015-11-20 12:02:17 -0800292 gpr_mu_unlock(&shard->mu);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800293 GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800294 return GRPC_MAKE_MDELEM(md, GRPC_MDELEM_STORAGE_INTERNED);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800295 }
296 }
297
298 /* not found: create a new pair */
Craig Tillerbaa14a92017-11-03 09:09:36 -0700299 md = (interned_metadata*)gpr_malloc(sizeof(interned_metadata));
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700300 gpr_atm_rel_store(&md->refcnt, 1);
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800301 md->key = grpc_slice_ref_internal(key);
302 md->value = grpc_slice_ref_internal(value);
Craig Tiller8344daa2015-10-09 18:10:57 -0700303 md->user_data = 0;
304 md->destroy_user_data = 0;
Craig Tillerb2b42612015-11-20 12:02:17 -0800305 md->bucket_next = shard->elems[idx];
306 shard->elems[idx] = md;
Craig Tiller83901532015-07-10 14:02:45 -0700307 gpr_mu_init(&md->mu_user_data);
ncteisen4b584052017-06-08 16:44:38 -0700308#ifndef NDEBUG
309 if (GRPC_TRACER_ON(grpc_trace_metadata)) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700310 char* key_str = grpc_slice_to_c_string(md->key);
311 char* value_str = grpc_slice_to_c_string(md->value);
312 gpr_log(GPR_DEBUG, "ELM NEW:%p:%" PRIdPTR ": '%s' = '%s'", (void*)md,
ncteisen4b584052017-06-08 16:44:38 -0700313 gpr_atm_no_barrier_load(&md->refcnt), key_str, value_str);
314 gpr_free(key_str);
315 gpr_free(value_str);
316 }
Craig Tiller1a65a232015-07-06 10:22:32 -0700317#endif
Craig Tillerb2b42612015-11-20 12:02:17 -0800318 shard->count++;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800319
Craig Tillerb2b42612015-11-20 12:02:17 -0800320 if (shard->count > shard->capacity * 2) {
Craig Tillera59c16c2016-10-31 07:25:01 -0700321 rehash_mdtab(exec_ctx, shard);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800322 }
323
Craig Tillerb2b42612015-11-20 12:02:17 -0800324 gpr_mu_unlock(&shard->mu);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800325
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800326 GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
327
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800328 return GRPC_MAKE_MDELEM(md, GRPC_MDELEM_STORAGE_INTERNED);
Craig Tiller9ecadce2016-11-18 14:52:25 -0800329}
330
Craig Tillerbaa14a92017-11-03 09:09:36 -0700331grpc_mdelem grpc_mdelem_from_slices(grpc_exec_ctx* exec_ctx, grpc_slice key,
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800332 grpc_slice value) {
Craig Tiller4782d922017-11-10 09:53:21 -0800333 grpc_mdelem out = grpc_mdelem_create(exec_ctx, key, value, nullptr);
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800334 grpc_slice_unref_internal(exec_ctx, key);
335 grpc_slice_unref_internal(exec_ctx, value);
336 return out;
Craig Tiller9ecadce2016-11-18 14:52:25 -0800337}
338
Craig Tillerbaa14a92017-11-03 09:09:36 -0700339grpc_mdelem grpc_mdelem_from_grpc_metadata(grpc_exec_ctx* exec_ctx,
340 grpc_metadata* metadata) {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800341 bool changed = false;
342 grpc_slice key_slice =
343 grpc_slice_maybe_static_intern(metadata->key, &changed);
344 grpc_slice value_slice =
345 grpc_slice_maybe_static_intern(metadata->value, &changed);
346 return grpc_mdelem_create(exec_ctx, key_slice, value_slice,
Craig Tiller4782d922017-11-10 09:53:21 -0800347 changed ? nullptr : (grpc_mdelem_data*)metadata);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800348}
349
yang-g24ba7c12016-03-30 16:19:43 -0700350static size_t get_base64_encoded_size(size_t raw_length) {
351 static const uint8_t tail_xtra[3] = {0, 2, 3};
352 return raw_length / 3 * 4 + tail_xtra[raw_length % 3];
353}
354
yang-gfd42ceb2017-10-10 16:55:13 -0700355size_t grpc_mdelem_get_size_in_hpack_table(grpc_mdelem elem,
356 bool use_true_binary_metadata) {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800357 size_t overhead_and_key = 32 + GRPC_SLICE_LENGTH(GRPC_MDKEY(elem));
358 size_t value_len = GRPC_SLICE_LENGTH(GRPC_MDVALUE(elem));
359 if (grpc_is_binary_header(GRPC_MDKEY(elem))) {
yang-gfd42ceb2017-10-10 16:55:13 -0700360 return overhead_and_key + (use_true_binary_metadata
361 ? value_len + 1
362 : get_base64_encoded_size(value_len));
yang-gcb7a8022016-03-30 14:58:53 -0700363 } else {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800364 return overhead_and_key + value_len;
yang-gcb7a8022016-03-30 14:58:53 -0700365 }
366}
367
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800368grpc_mdelem grpc_mdelem_ref(grpc_mdelem gmd DEBUG_ARGS) {
369 switch (GRPC_MDELEM_STORAGE(gmd)) {
370 case GRPC_MDELEM_STORAGE_EXTERNAL:
371 case GRPC_MDELEM_STORAGE_STATIC:
372 break;
373 case GRPC_MDELEM_STORAGE_INTERNED: {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700374 interned_metadata* md = (interned_metadata*)GRPC_MDELEM_DATA(gmd);
ncteisen4b584052017-06-08 16:44:38 -0700375#ifndef NDEBUG
376 if (GRPC_TRACER_ON(grpc_trace_metadata)) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700377 char* key_str = grpc_slice_to_c_string(md->key);
378 char* value_str = grpc_slice_to_c_string(md->value);
ncteisen4b584052017-06-08 16:44:38 -0700379 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
ncteisen3ac64f82017-06-19 17:35:44 -0700380 "ELM REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'",
Craig Tillerbaa14a92017-11-03 09:09:36 -0700381 (void*)md, gpr_atm_no_barrier_load(&md->refcnt),
ncteisen4b584052017-06-08 16:44:38 -0700382 gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str);
383 gpr_free(key_str);
384 gpr_free(value_str);
385 }
Craig Tiller1a65a232015-07-06 10:22:32 -0700386#endif
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800387 /* we can assume the ref count is >= 1 as the application is calling
388 this function - meaning that no adjustment to mdtab_free is necessary,
389 simplifying the logic here to be just an atomic increment */
390 /* use C assert to have this removed in opt builds */
391 GPR_ASSERT(gpr_atm_no_barrier_load(&md->refcnt) >= 1);
392 gpr_atm_no_barrier_fetch_add(&md->refcnt, 1);
393 break;
394 }
395 case GRPC_MDELEM_STORAGE_ALLOCATED: {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700396 allocated_metadata* md = (allocated_metadata*)GRPC_MDELEM_DATA(gmd);
ncteisen4b584052017-06-08 16:44:38 -0700397#ifndef NDEBUG
398 if (GRPC_TRACER_ON(grpc_trace_metadata)) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700399 char* key_str = grpc_slice_to_c_string(md->key);
400 char* value_str = grpc_slice_to_c_string(md->value);
ncteisen4b584052017-06-08 16:44:38 -0700401 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
ncteisen3ac64f82017-06-19 17:35:44 -0700402 "ELM REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'",
Craig Tillerbaa14a92017-11-03 09:09:36 -0700403 (void*)md, gpr_atm_no_barrier_load(&md->refcnt),
ncteisen4b584052017-06-08 16:44:38 -0700404 gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str);
405 gpr_free(key_str);
406 gpr_free(value_str);
ncteisend39010e2017-06-08 17:08:07 -0700407 }
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800408#endif
409 /* we can assume the ref count is >= 1 as the application is calling
410 this function - meaning that no adjustment to mdtab_free is necessary,
411 simplifying the logic here to be just an atomic increment */
412 /* use C assert to have this removed in opt builds */
413 gpr_atm_no_barrier_fetch_add(&md->refcnt, 1);
414 break;
415 }
416 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800417 return gmd;
418}
419
Craig Tillerbaa14a92017-11-03 09:09:36 -0700420void grpc_mdelem_unref(grpc_exec_ctx* exec_ctx, grpc_mdelem gmd DEBUG_ARGS) {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800421 switch (GRPC_MDELEM_STORAGE(gmd)) {
422 case GRPC_MDELEM_STORAGE_EXTERNAL:
423 case GRPC_MDELEM_STORAGE_STATIC:
424 break;
425 case GRPC_MDELEM_STORAGE_INTERNED: {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700426 interned_metadata* md = (interned_metadata*)GRPC_MDELEM_DATA(gmd);
ncteisen4b584052017-06-08 16:44:38 -0700427#ifndef NDEBUG
428 if (GRPC_TRACER_ON(grpc_trace_metadata)) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700429 char* key_str = grpc_slice_to_c_string(md->key);
430 char* value_str = grpc_slice_to_c_string(md->value);
ncteisen4b584052017-06-08 16:44:38 -0700431 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
ncteisen3ac64f82017-06-19 17:35:44 -0700432 "ELM UNREF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'",
Craig Tillerbaa14a92017-11-03 09:09:36 -0700433 (void*)md, gpr_atm_no_barrier_load(&md->refcnt),
ncteisen4b584052017-06-08 16:44:38 -0700434 gpr_atm_no_barrier_load(&md->refcnt) - 1, key_str, value_str);
435 gpr_free(key_str);
436 gpr_free(value_str);
437 }
Craig Tiller1a65a232015-07-06 10:22:32 -0700438#endif
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800439 uint32_t hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash(md->key),
440 grpc_slice_hash(md->value));
441 const gpr_atm prev_refcount = gpr_atm_full_fetch_add(&md->refcnt, -1);
442 GPR_ASSERT(prev_refcount >= 1);
443 if (1 == prev_refcount) {
444 /* once the refcount hits zero, some other thread can come along and
445 free md at any time: it's unsafe from this point on to access it */
Craig Tillerbaa14a92017-11-03 09:09:36 -0700446 mdtab_shard* shard = &g_shards[SHARD_IDX(hash)];
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800447 gpr_atm_no_barrier_fetch_add(&shard->free_estimate, 1);
448 }
449 break;
Craig Tiller83901532015-07-10 14:02:45 -0700450 }
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800451 case GRPC_MDELEM_STORAGE_ALLOCATED: {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700452 allocated_metadata* md = (allocated_metadata*)GRPC_MDELEM_DATA(gmd);
ncteisen4b584052017-06-08 16:44:38 -0700453#ifndef NDEBUG
454 if (GRPC_TRACER_ON(grpc_trace_metadata)) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700455 char* key_str = grpc_slice_to_c_string(md->key);
456 char* value_str = grpc_slice_to_c_string(md->value);
ncteisen4b584052017-06-08 16:44:38 -0700457 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
ncteisen3ac64f82017-06-19 17:35:44 -0700458 "ELM UNREF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'",
Craig Tillerbaa14a92017-11-03 09:09:36 -0700459 (void*)md, gpr_atm_no_barrier_load(&md->refcnt),
ncteisen4b584052017-06-08 16:44:38 -0700460 gpr_atm_no_barrier_load(&md->refcnt) - 1, key_str, value_str);
461 gpr_free(key_str);
462 gpr_free(value_str);
463 }
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800464#endif
465 const gpr_atm prev_refcount = gpr_atm_full_fetch_add(&md->refcnt, -1);
466 GPR_ASSERT(prev_refcount >= 1);
467 if (1 == prev_refcount) {
468 grpc_slice_unref_internal(exec_ctx, md->key);
469 grpc_slice_unref_internal(exec_ctx, md->value);
470 gpr_free(md);
471 }
472 break;
473 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800474 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800475}
Craig Tiller0160de92016-11-18 08:46:46 -0800476
Craig Tillerbaa14a92017-11-03 09:09:36 -0700477void* grpc_mdelem_get_user_data(grpc_mdelem md, void (*destroy_func)(void*)) {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800478 switch (GRPC_MDELEM_STORAGE(md)) {
479 case GRPC_MDELEM_STORAGE_EXTERNAL:
480 case GRPC_MDELEM_STORAGE_ALLOCATED:
Craig Tiller4782d922017-11-10 09:53:21 -0800481 return nullptr;
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800482 case GRPC_MDELEM_STORAGE_STATIC:
Craig Tillerbaa14a92017-11-03 09:09:36 -0700483 return (void*)grpc_static_mdelem_user_data[GRPC_MDELEM_DATA(md) -
484 grpc_static_mdelem_table];
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800485 case GRPC_MDELEM_STORAGE_INTERNED: {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700486 interned_metadata* im = (interned_metadata*)GRPC_MDELEM_DATA(md);
487 void* result;
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800488 if (gpr_atm_acq_load(&im->destroy_user_data) == (gpr_atm)destroy_func) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700489 return (void*)gpr_atm_no_barrier_load(&im->user_data);
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800490 } else {
Craig Tiller4782d922017-11-10 09:53:21 -0800491 return nullptr;
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800492 }
493 return result;
494 }
Craig Tiller5e01e2a2017-01-20 18:11:52 -0800495 }
Craig Tiller4782d922017-11-10 09:53:21 -0800496 GPR_UNREACHABLE_CODE(return nullptr);
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800497}
498
Craig Tillerbaa14a92017-11-03 09:09:36 -0700499void* grpc_mdelem_set_user_data(grpc_mdelem md, void (*destroy_func)(void*),
500 void* user_data) {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800501 switch (GRPC_MDELEM_STORAGE(md)) {
502 case GRPC_MDELEM_STORAGE_EXTERNAL:
503 case GRPC_MDELEM_STORAGE_ALLOCATED:
504 destroy_func(user_data);
Craig Tiller4782d922017-11-10 09:53:21 -0800505 return nullptr;
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800506 case GRPC_MDELEM_STORAGE_STATIC:
507 destroy_func(user_data);
Craig Tillerbaa14a92017-11-03 09:09:36 -0700508 return (void*)grpc_static_mdelem_user_data[GRPC_MDELEM_DATA(md) -
509 grpc_static_mdelem_table];
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800510 case GRPC_MDELEM_STORAGE_INTERNED: {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700511 interned_metadata* im = (interned_metadata*)GRPC_MDELEM_DATA(md);
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800512 GPR_ASSERT(!is_mdelem_static(md));
Craig Tiller4782d922017-11-10 09:53:21 -0800513 GPR_ASSERT((user_data == nullptr) == (destroy_func == nullptr));
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800514 gpr_mu_lock(&im->mu_user_data);
515 if (gpr_atm_no_barrier_load(&im->destroy_user_data)) {
516 /* user data can only be set once */
517 gpr_mu_unlock(&im->mu_user_data);
Craig Tiller4782d922017-11-10 09:53:21 -0800518 if (destroy_func != nullptr) {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800519 destroy_func(user_data);
520 }
Craig Tillerbaa14a92017-11-03 09:09:36 -0700521 return (void*)gpr_atm_no_barrier_load(&im->user_data);
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800522 }
523 gpr_atm_no_barrier_store(&im->user_data, (gpr_atm)user_data);
524 gpr_atm_rel_store(&im->destroy_user_data, (gpr_atm)destroy_func);
525 gpr_mu_unlock(&im->mu_user_data);
526 return user_data;
527 }
528 }
Craig Tiller4782d922017-11-10 09:53:21 -0800529 GPR_UNREACHABLE_CODE(return nullptr);
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800530}
531
532bool grpc_mdelem_eq(grpc_mdelem a, grpc_mdelem b) {
533 if (a.payload == b.payload) return true;
534 if (GRPC_MDELEM_IS_INTERNED(a) && GRPC_MDELEM_IS_INTERNED(b)) return false;
535 if (GRPC_MDISNULL(a) || GRPC_MDISNULL(b)) return false;
536 return grpc_slice_eq(GRPC_MDKEY(a), GRPC_MDKEY(b)) &&
537 grpc_slice_eq(GRPC_MDVALUE(a), GRPC_MDVALUE(b));
Craig Tiller0160de92016-11-18 08:46:46 -0800538}