blob: 188b485625a6d49b8956aad8e77ace60328d47be [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>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080022#include <stddef.h>
23#include <string.h>
24
Craig Tillerebdef9d2015-11-19 17:09:49 -080025#include <grpc/compression.h>
yang-gcb7a8022016-03-30 14:58:53 -070026#include <grpc/grpc.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080027#include <grpc/support/alloc.h>
Craig Tiller9fa41b92015-04-10 15:08:03 -070028#include <grpc/support/atm.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080029#include <grpc/support/log.h>
Craig Tillerebdef9d2015-11-19 17:09:49 -080030#include <grpc/support/string_util.h>
Craig Tiller9d35a1f2015-11-02 14:16:12 -080031#include <grpc/support/time.h>
Craig Tiller0cb803d2016-03-02 22:17:24 -080032
Craig Tiller9533d042016-03-25 17:11:06 -070033#include "src/core/lib/iomgr/iomgr_internal.h"
34#include "src/core/lib/profiling/timers.h"
Craig Tillera59c16c2016-10-31 07:25:01 -070035#include "src/core/lib/slice/slice_internal.h"
Craig Tiller7c70b6c2017-01-23 07:48:42 -080036#include "src/core/lib/slice/slice_string_helpers.h"
Craig Tiller9533d042016-03-25 17:11:06 -070037#include "src/core/lib/support/murmur_hash.h"
38#include "src/core/lib/support/string.h"
Craig Tiller9533d042016-03-25 17:11:06 -070039#include "src/core/lib/transport/static_metadata.h"
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080040
Craig Tiller70b080d2015-11-19 08:04:48 -080041/* There are two kinds of mdelem and mdstr instances.
42 * Static instances are declared in static_metadata.{h,c} and
43 * are initialized by grpc_mdctx_global_init().
44 * Dynamic instances are stored in hash tables on grpc_mdctx, and are backed
45 * by internal_string and internal_element structures.
46 * Internal helper functions here-in (is_mdstr_static, is_mdelem_static) are
47 * used to determine which kind of element a pointer refers to.
48 */
49
ncteisen4b584052017-06-08 16:44:38 -070050#ifndef NDEBUG
ncteisen7712c7c2017-07-12 23:11:27 -070051grpc_tracer_flag grpc_trace_metadata =
52 GRPC_TRACER_INITIALIZER(false, "metadata");
Craig Tiller1a65a232015-07-06 10:22:32 -070053#define DEBUG_ARGS , const char *file, int line
54#define FWD_DEBUG_ARGS , file, line
Craig Tillerb2b42612015-11-20 12:02:17 -080055#define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s), __FILE__, __LINE__)
Craig Tiller1a65a232015-07-06 10:22:32 -070056#else
57#define DEBUG_ARGS
58#define FWD_DEBUG_ARGS
Craig Tillerb2b42612015-11-20 12:02:17 -080059#define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s))
Craig Tiller1a65a232015-07-06 10:22:32 -070060#endif
61
Craig Tiller7c70b6c2017-01-23 07:48:42 -080062#define INITIAL_SHARD_CAPACITY 8
63#define LOG2_SHARD_COUNT 4
64#define SHARD_COUNT ((size_t)(1 << LOG2_SHARD_COUNT))
65
66#define TABLE_IDX(hash, capacity) (((hash) >> (LOG2_SHARD_COUNT)) % (capacity))
67#define SHARD_IDX(hash) ((hash) & ((1 << (LOG2_SHARD_COUNT)) - 1))
Craig Tillerb2b42612015-11-20 12:02:17 -080068
Craig Tiller8344daa2015-10-09 18:10:57 -070069typedef void (*destroy_user_data_func)(void *user_data);
70
Craig Tiller7c70b6c2017-01-23 07:48:42 -080071/* Shadow structure for grpc_mdelem_data for interned elements */
72typedef struct interned_metadata {
73 /* must be byte compatible with grpc_mdelem_data */
74 grpc_slice key;
75 grpc_slice value;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080076
Craig Tillerb2b42612015-11-20 12:02:17 -080077 /* private only data */
Craig Tiller9fa41b92015-04-10 15:08:03 -070078 gpr_atm refcnt;
79
Craig Tiller83901532015-07-10 14:02:45 -070080 gpr_mu mu_user_data;
Craig Tiller8344daa2015-10-09 18:10:57 -070081 gpr_atm destroy_user_data;
82 gpr_atm user_data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080083
Craig Tiller7c70b6c2017-01-23 07:48:42 -080084 struct interned_metadata *bucket_next;
85} interned_metadata;
Craig Tiller9ecadce2016-11-18 14:52:25 -080086
Craig Tiller7c70b6c2017-01-23 07:48:42 -080087/* Shadow structure for grpc_mdelem_data for allocated elements */
88typedef struct allocated_metadata {
89 /* must be byte compatible with grpc_mdelem_data */
90 grpc_slice key;
91 grpc_slice value;
92
93 /* private only data */
94 gpr_atm refcnt;
95} allocated_metadata;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080096
Craig Tillerb2b42612015-11-20 12:02:17 -080097typedef struct mdtab_shard {
98 gpr_mu mu;
Craig Tiller7c70b6c2017-01-23 07:48:42 -080099 interned_metadata **elems;
Craig Tillerb2b42612015-11-20 12:02:17 -0800100 size_t count;
101 size_t capacity;
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700102 /** Estimate of the number of unreferenced mdelems in the hash table.
103 This will eventually converge to the exact number, but it's instantaneous
104 accuracy is not guaranteed */
105 gpr_atm free_estimate;
Craig Tillerb2b42612015-11-20 12:02:17 -0800106} mdtab_shard;
Craig Tiller0e72ede2015-11-19 07:48:53 -0800107
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800108static mdtab_shard g_shards[SHARD_COUNT];
Craig Tillerb2b42612015-11-20 12:02:17 -0800109
Craig Tillera59c16c2016-10-31 07:25:01 -0700110static void gc_mdtab(grpc_exec_ctx *exec_ctx, mdtab_shard *shard);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800111
Craig Tiller0e72ede2015-11-19 07:48:53 -0800112void grpc_mdctx_global_init(void) {
Craig Tillerb2b42612015-11-20 12:02:17 -0800113 /* initialize shards */
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800114 for (size_t i = 0; i < SHARD_COUNT; i++) {
115 mdtab_shard *shard = &g_shards[i];
Craig Tillerb2b42612015-11-20 12:02:17 -0800116 gpr_mu_init(&shard->mu);
117 shard->count = 0;
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700118 gpr_atm_no_barrier_store(&shard->free_estimate, 0);
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800119 shard->capacity = INITIAL_SHARD_CAPACITY;
Yash Tibrewalca3c1c02017-09-07 22:47:16 -0700120 shard->elems = (interned_metadata **)gpr_zalloc(sizeof(*shard->elems) *
121 shard->capacity);
Craig Tiller0e72ede2015-11-19 07:48:53 -0800122 }
123}
124
Craig Tillera59c16c2016-10-31 07:25:01 -0700125void grpc_mdctx_global_shutdown(grpc_exec_ctx *exec_ctx) {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800126 for (size_t i = 0; i < SHARD_COUNT; i++) {
127 mdtab_shard *shard = &g_shards[i];
Craig Tillerb2b42612015-11-20 12:02:17 -0800128 gpr_mu_destroy(&shard->mu);
Craig Tillera59c16c2016-10-31 07:25:01 -0700129 gc_mdtab(exec_ctx, shard);
Craig Tiller34075442015-11-23 16:16:54 -0800130 /* TODO(ctiller): GPR_ASSERT(shard->count == 0); */
131 if (shard->count != 0) {
Yuchen Zeng64c0e8d2016-06-10 11:19:51 -0700132 gpr_log(GPR_DEBUG, "WARNING: %" PRIuPTR " metadata elements were leaked",
yang-gd88e1d82015-12-02 13:23:33 -0800133 shard->count);
Craig Tiller0cb803d2016-03-02 22:17:24 -0800134 if (grpc_iomgr_abort_on_leaks()) {
135 abort();
136 }
Craig Tiller34075442015-11-23 16:16:54 -0800137 }
Craig Tillerb2b42612015-11-20 12:02:17 -0800138 gpr_free(shard->elems);
139 }
Craig Tiller0e72ede2015-11-19 07:48:53 -0800140}
141
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800142static int is_mdelem_static(grpc_mdelem e) {
143 return GRPC_MDELEM_DATA(e) >= &grpc_static_mdelem_table[0] &&
144 GRPC_MDELEM_DATA(e) <
145 &grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
Craig Tiller0e72ede2015-11-19 07:48:53 -0800146}
147
Craig Tillerb2b42612015-11-20 12:02:17 -0800148static void ref_md_locked(mdtab_shard *shard,
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800149 interned_metadata *md DEBUG_ARGS) {
ncteisen4b584052017-06-08 16:44:38 -0700150#ifndef NDEBUG
151 if (GRPC_TRACER_ON(grpc_trace_metadata)) {
152 char *key_str = grpc_slice_to_c_string(md->key);
153 char *value_str = grpc_slice_to_c_string(md->value);
154 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
ncteisen3ac64f82017-06-19 17:35:44 -0700155 "ELM REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", (void *)md,
ncteisen4b584052017-06-08 16:44:38 -0700156 gpr_atm_no_barrier_load(&md->refcnt),
157 gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str);
158 gpr_free(key_str);
159 gpr_free(value_str);
160 }
Craig Tiller1a65a232015-07-06 10:22:32 -0700161#endif
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700162 if (0 == gpr_atm_no_barrier_fetch_add(&md->refcnt, 1)) {
163 gpr_atm_no_barrier_fetch_add(&shard->free_estimate, -1);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800164 }
165}
166
Craig Tillera59c16c2016-10-31 07:25:01 -0700167static void gc_mdtab(grpc_exec_ctx *exec_ctx, mdtab_shard *shard) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800168 size_t i;
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800169 interned_metadata **prev_next;
170 interned_metadata *md, *next;
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700171 gpr_atm num_freed = 0;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800172
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800173 GPR_TIMER_BEGIN("gc_mdtab", 0);
Craig Tillerb2b42612015-11-20 12:02:17 -0800174 for (i = 0; i < shard->capacity; i++) {
175 prev_next = &shard->elems[i];
176 for (md = shard->elems[i]; md; md = next) {
Craig Tiller8344daa2015-10-09 18:10:57 -0700177 void *user_data = (void *)gpr_atm_no_barrier_load(&md->user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800178 next = md->bucket_next;
Craig Tiller9fa41b92015-04-10 15:08:03 -0700179 if (gpr_atm_acq_load(&md->refcnt) == 0) {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800180 grpc_slice_unref_internal(exec_ctx, md->key);
181 grpc_slice_unref_internal(exec_ctx, md->value);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800182 if (md->user_data) {
Craig Tiller8344daa2015-10-09 18:10:57 -0700183 ((destroy_user_data_func)gpr_atm_no_barrier_load(
184 &md->destroy_user_data))(user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800185 }
186 gpr_free(md);
187 *prev_next = next;
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700188 num_freed++;
Craig Tillerb2b42612015-11-20 12:02:17 -0800189 shard->count--;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800190 } else {
191 prev_next = &md->bucket_next;
192 }
193 }
194 }
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700195 gpr_atm_no_barrier_fetch_add(&shard->free_estimate, -num_freed);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800196 GPR_TIMER_END("gc_mdtab", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800197}
198
Craig Tillerb2b42612015-11-20 12:02:17 -0800199static void grow_mdtab(mdtab_shard *shard) {
200 size_t capacity = shard->capacity * 2;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800201 size_t i;
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800202 interned_metadata **mdtab;
203 interned_metadata *md, *next;
Craig Tiller7536af02015-12-22 13:49:30 -0800204 uint32_t hash;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800205
206 GPR_TIMER_BEGIN("grow_mdtab", 0);
207
Yash Tibrewalca3c1c02017-09-07 22:47:16 -0700208 mdtab =
209 (interned_metadata **)gpr_zalloc(sizeof(interned_metadata *) * capacity);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800210
Craig Tillerb2b42612015-11-20 12:02:17 -0800211 for (i = 0; i < shard->capacity; i++) {
212 for (md = shard->elems[i]; md; md = next) {
213 size_t idx;
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800214 hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash(md->key),
215 grpc_slice_hash(md->value));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800216 next = md->bucket_next;
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800217 idx = TABLE_IDX(hash, capacity);
Craig Tillerb2b42612015-11-20 12:02:17 -0800218 md->bucket_next = mdtab[idx];
219 mdtab[idx] = md;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800220 }
221 }
222
Craig Tillerb2b42612015-11-20 12:02:17 -0800223 gpr_free(shard->elems);
224 shard->elems = mdtab;
225 shard->capacity = capacity;
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800226
227 GPR_TIMER_END("grow_mdtab", 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800228}
229
Craig Tillera59c16c2016-10-31 07:25:01 -0700230static void rehash_mdtab(grpc_exec_ctx *exec_ctx, mdtab_shard *shard) {
Craig Tiller139098c2016-06-06 08:10:08 -0700231 if (gpr_atm_no_barrier_load(&shard->free_estimate) >
232 (gpr_atm)(shard->capacity / 4)) {
Craig Tillera59c16c2016-10-31 07:25:01 -0700233 gc_mdtab(exec_ctx, shard);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800234 } else {
Craig Tillerb2b42612015-11-20 12:02:17 -0800235 grow_mdtab(shard);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800236 }
237}
238
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800239grpc_mdelem grpc_mdelem_create(
240 grpc_exec_ctx *exec_ctx, grpc_slice key, grpc_slice value,
241 grpc_mdelem_data *compatible_external_backing_store) {
242 if (!grpc_slice_is_interned(key) || !grpc_slice_is_interned(value)) {
243 if (compatible_external_backing_store != NULL) {
244 return GRPC_MAKE_MDELEM(compatible_external_backing_store,
245 GRPC_MDELEM_STORAGE_EXTERNAL);
246 }
247
Yash Tibrewalca3c1c02017-09-07 22:47:16 -0700248 allocated_metadata *allocated =
249 (allocated_metadata *)gpr_malloc(sizeof(*allocated));
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800250 allocated->key = grpc_slice_ref_internal(key);
251 allocated->value = grpc_slice_ref_internal(value);
252 gpr_atm_rel_store(&allocated->refcnt, 1);
ncteisen4b584052017-06-08 16:44:38 -0700253#ifndef NDEBUG
254 if (GRPC_TRACER_ON(grpc_trace_metadata)) {
255 char *key_str = grpc_slice_to_c_string(allocated->key);
256 char *value_str = grpc_slice_to_c_string(allocated->value);
ncteisen3ac64f82017-06-19 17:35:44 -0700257 gpr_log(GPR_DEBUG, "ELM ALLOC:%p:%" PRIdPTR ": '%s' = '%s'",
258 (void *)allocated, gpr_atm_no_barrier_load(&allocated->refcnt),
259 key_str, value_str);
ncteisen4b584052017-06-08 16:44:38 -0700260 gpr_free(key_str);
261 gpr_free(value_str);
262 }
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800263#endif
264 return GRPC_MAKE_MDELEM(allocated, GRPC_MDELEM_STORAGE_ALLOCATED);
265 }
266
267 if (GRPC_IS_STATIC_METADATA_STRING(key) &&
268 GRPC_IS_STATIC_METADATA_STRING(value)) {
269 grpc_mdelem static_elem = grpc_static_mdelem_for_static_strings(
270 GRPC_STATIC_METADATA_INDEX(key), GRPC_STATIC_METADATA_INDEX(value));
271 if (!GRPC_MDISNULL(static_elem)) {
272 return static_elem;
273 }
274 }
275
276 uint32_t hash =
277 GRPC_MDSTR_KV_HASH(grpc_slice_hash(key), grpc_slice_hash(value));
278 interned_metadata *md;
279 mdtab_shard *shard = &g_shards[SHARD_IDX(hash)];
Craig Tillerb2b42612015-11-20 12:02:17 -0800280 size_t idx;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800281
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800282 GPR_TIMER_BEGIN("grpc_mdelem_from_metadata_strings", 0);
283
Craig Tillerb2b42612015-11-20 12:02:17 -0800284 gpr_mu_lock(&shard->mu);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800285
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800286 idx = TABLE_IDX(hash, shard->capacity);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800287 /* search for an existing pair */
Craig Tillerb2b42612015-11-20 12:02:17 -0800288 for (md = shard->elems[idx]; md; md = md->bucket_next) {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800289 if (grpc_slice_eq(key, md->key) && grpc_slice_eq(value, md->value)) {
Craig Tillerb2b42612015-11-20 12:02:17 -0800290 REF_MD_LOCKED(shard, md);
Craig Tillerb2b42612015-11-20 12:02:17 -0800291 gpr_mu_unlock(&shard->mu);
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800292 GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800293 return GRPC_MAKE_MDELEM(md, GRPC_MDELEM_STORAGE_INTERNED);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800294 }
295 }
296
297 /* not found: create a new pair */
Yash Tibrewalca3c1c02017-09-07 22:47:16 -0700298 md = (interned_metadata *)gpr_malloc(sizeof(interned_metadata));
Craig Tiller8d8f9a82016-06-03 08:49:11 -0700299 gpr_atm_rel_store(&md->refcnt, 1);
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800300 md->key = grpc_slice_ref_internal(key);
301 md->value = grpc_slice_ref_internal(value);
Craig Tiller8344daa2015-10-09 18:10:57 -0700302 md->user_data = 0;
303 md->destroy_user_data = 0;
Craig Tillerb2b42612015-11-20 12:02:17 -0800304 md->bucket_next = shard->elems[idx];
305 shard->elems[idx] = md;
Craig Tiller83901532015-07-10 14:02:45 -0700306 gpr_mu_init(&md->mu_user_data);
ncteisen4b584052017-06-08 16:44:38 -0700307#ifndef NDEBUG
308 if (GRPC_TRACER_ON(grpc_trace_metadata)) {
309 char *key_str = grpc_slice_to_c_string(md->key);
310 char *value_str = grpc_slice_to_c_string(md->value);
ncteisen3ac64f82017-06-19 17:35:44 -0700311 gpr_log(GPR_DEBUG, "ELM NEW:%p:%" PRIdPTR ": '%s' = '%s'", (void *)md,
ncteisen4b584052017-06-08 16:44:38 -0700312 gpr_atm_no_barrier_load(&md->refcnt), key_str, value_str);
313 gpr_free(key_str);
314 gpr_free(value_str);
315 }
Craig Tiller1a65a232015-07-06 10:22:32 -0700316#endif
Craig Tillerb2b42612015-11-20 12:02:17 -0800317 shard->count++;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800318
Craig Tillerb2b42612015-11-20 12:02:17 -0800319 if (shard->count > shard->capacity * 2) {
Craig Tillera59c16c2016-10-31 07:25:01 -0700320 rehash_mdtab(exec_ctx, shard);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800321 }
322
Craig Tillerb2b42612015-11-20 12:02:17 -0800323 gpr_mu_unlock(&shard->mu);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800324
Craig Tiller9d35a1f2015-11-02 14:16:12 -0800325 GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
326
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800327 return GRPC_MAKE_MDELEM(md, GRPC_MDELEM_STORAGE_INTERNED);
Craig Tiller9ecadce2016-11-18 14:52:25 -0800328}
329
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800330grpc_mdelem grpc_mdelem_from_slices(grpc_exec_ctx *exec_ctx, grpc_slice key,
331 grpc_slice value) {
332 grpc_mdelem out = grpc_mdelem_create(exec_ctx, key, value, NULL);
333 grpc_slice_unref_internal(exec_ctx, key);
334 grpc_slice_unref_internal(exec_ctx, value);
335 return out;
Craig Tiller9ecadce2016-11-18 14:52:25 -0800336}
337
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800338grpc_mdelem grpc_mdelem_from_grpc_metadata(grpc_exec_ctx *exec_ctx,
339 grpc_metadata *metadata) {
340 bool changed = false;
341 grpc_slice key_slice =
342 grpc_slice_maybe_static_intern(metadata->key, &changed);
343 grpc_slice value_slice =
344 grpc_slice_maybe_static_intern(metadata->value, &changed);
345 return grpc_mdelem_create(exec_ctx, key_slice, value_slice,
346 changed ? NULL : (grpc_mdelem_data *)metadata);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800347}
348
yang-g24ba7c12016-03-30 16:19:43 -0700349static size_t get_base64_encoded_size(size_t raw_length) {
350 static const uint8_t tail_xtra[3] = {0, 2, 3};
351 return raw_length / 3 * 4 + tail_xtra[raw_length % 3];
352}
353
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800354size_t grpc_mdelem_get_size_in_hpack_table(grpc_mdelem elem) {
355 size_t overhead_and_key = 32 + GRPC_SLICE_LENGTH(GRPC_MDKEY(elem));
356 size_t value_len = GRPC_SLICE_LENGTH(GRPC_MDVALUE(elem));
357 if (grpc_is_binary_header(GRPC_MDKEY(elem))) {
358 return overhead_and_key + get_base64_encoded_size(value_len);
yang-gcb7a8022016-03-30 14:58:53 -0700359 } else {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800360 return overhead_and_key + value_len;
yang-gcb7a8022016-03-30 14:58:53 -0700361 }
362}
363
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800364grpc_mdelem grpc_mdelem_ref(grpc_mdelem gmd DEBUG_ARGS) {
365 switch (GRPC_MDELEM_STORAGE(gmd)) {
366 case GRPC_MDELEM_STORAGE_EXTERNAL:
367 case GRPC_MDELEM_STORAGE_STATIC:
368 break;
369 case GRPC_MDELEM_STORAGE_INTERNED: {
370 interned_metadata *md = (interned_metadata *)GRPC_MDELEM_DATA(gmd);
ncteisen4b584052017-06-08 16:44:38 -0700371#ifndef NDEBUG
372 if (GRPC_TRACER_ON(grpc_trace_metadata)) {
373 char *key_str = grpc_slice_to_c_string(md->key);
374 char *value_str = grpc_slice_to_c_string(md->value);
375 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
ncteisen3ac64f82017-06-19 17:35:44 -0700376 "ELM REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'",
377 (void *)md, gpr_atm_no_barrier_load(&md->refcnt),
ncteisen4b584052017-06-08 16:44:38 -0700378 gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str);
379 gpr_free(key_str);
380 gpr_free(value_str);
381 }
Craig Tiller1a65a232015-07-06 10:22:32 -0700382#endif
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800383 /* we can assume the ref count is >= 1 as the application is calling
384 this function - meaning that no adjustment to mdtab_free is necessary,
385 simplifying the logic here to be just an atomic increment */
386 /* use C assert to have this removed in opt builds */
387 GPR_ASSERT(gpr_atm_no_barrier_load(&md->refcnt) >= 1);
388 gpr_atm_no_barrier_fetch_add(&md->refcnt, 1);
389 break;
390 }
391 case GRPC_MDELEM_STORAGE_ALLOCATED: {
392 allocated_metadata *md = (allocated_metadata *)GRPC_MDELEM_DATA(gmd);
ncteisen4b584052017-06-08 16:44:38 -0700393#ifndef NDEBUG
394 if (GRPC_TRACER_ON(grpc_trace_metadata)) {
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,
ncteisen3ac64f82017-06-19 17:35:44 -0700398 "ELM REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'",
399 (void *)md, gpr_atm_no_barrier_load(&md->refcnt),
ncteisen4b584052017-06-08 16:44:38 -0700400 gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str);
401 gpr_free(key_str);
402 gpr_free(value_str);
ncteisend39010e2017-06-08 17:08:07 -0700403 }
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800404#endif
405 /* we can assume the ref count is >= 1 as the application is calling
406 this function - meaning that no adjustment to mdtab_free is necessary,
407 simplifying the logic here to be just an atomic increment */
408 /* use C assert to have this removed in opt builds */
409 gpr_atm_no_barrier_fetch_add(&md->refcnt, 1);
410 break;
411 }
412 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800413 return gmd;
414}
415
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800416void grpc_mdelem_unref(grpc_exec_ctx *exec_ctx, grpc_mdelem gmd DEBUG_ARGS) {
417 switch (GRPC_MDELEM_STORAGE(gmd)) {
418 case GRPC_MDELEM_STORAGE_EXTERNAL:
419 case GRPC_MDELEM_STORAGE_STATIC:
420 break;
421 case GRPC_MDELEM_STORAGE_INTERNED: {
422 interned_metadata *md = (interned_metadata *)GRPC_MDELEM_DATA(gmd);
ncteisen4b584052017-06-08 16:44:38 -0700423#ifndef NDEBUG
424 if (GRPC_TRACER_ON(grpc_trace_metadata)) {
425 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,
ncteisen3ac64f82017-06-19 17:35:44 -0700428 "ELM UNREF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'",
429 (void *)md, gpr_atm_no_barrier_load(&md->refcnt),
ncteisen4b584052017-06-08 16:44:38 -0700430 gpr_atm_no_barrier_load(&md->refcnt) - 1, key_str, value_str);
431 gpr_free(key_str);
432 gpr_free(value_str);
433 }
Craig Tiller1a65a232015-07-06 10:22:32 -0700434#endif
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800435 uint32_t hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash(md->key),
436 grpc_slice_hash(md->value));
437 const gpr_atm prev_refcount = gpr_atm_full_fetch_add(&md->refcnt, -1);
438 GPR_ASSERT(prev_refcount >= 1);
439 if (1 == prev_refcount) {
440 /* once the refcount hits zero, some other thread can come along and
441 free md at any time: it's unsafe from this point on to access it */
442 mdtab_shard *shard = &g_shards[SHARD_IDX(hash)];
443 gpr_atm_no_barrier_fetch_add(&shard->free_estimate, 1);
444 }
445 break;
Craig Tiller83901532015-07-10 14:02:45 -0700446 }
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800447 case GRPC_MDELEM_STORAGE_ALLOCATED: {
448 allocated_metadata *md = (allocated_metadata *)GRPC_MDELEM_DATA(gmd);
ncteisen4b584052017-06-08 16:44:38 -0700449#ifndef NDEBUG
450 if (GRPC_TRACER_ON(grpc_trace_metadata)) {
451 char *key_str = grpc_slice_to_c_string(md->key);
452 char *value_str = grpc_slice_to_c_string(md->value);
453 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
ncteisen3ac64f82017-06-19 17:35:44 -0700454 "ELM UNREF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'",
455 (void *)md, gpr_atm_no_barrier_load(&md->refcnt),
ncteisen4b584052017-06-08 16:44:38 -0700456 gpr_atm_no_barrier_load(&md->refcnt) - 1, key_str, value_str);
457 gpr_free(key_str);
458 gpr_free(value_str);
459 }
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800460#endif
461 const gpr_atm prev_refcount = gpr_atm_full_fetch_add(&md->refcnt, -1);
462 GPR_ASSERT(prev_refcount >= 1);
463 if (1 == prev_refcount) {
464 grpc_slice_unref_internal(exec_ctx, md->key);
465 grpc_slice_unref_internal(exec_ctx, md->value);
466 gpr_free(md);
467 }
468 break;
469 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800470 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800471}
Craig Tiller0160de92016-11-18 08:46:46 -0800472
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800473void *grpc_mdelem_get_user_data(grpc_mdelem md, void (*destroy_func)(void *)) {
474 switch (GRPC_MDELEM_STORAGE(md)) {
475 case GRPC_MDELEM_STORAGE_EXTERNAL:
476 case GRPC_MDELEM_STORAGE_ALLOCATED:
477 return NULL;
478 case GRPC_MDELEM_STORAGE_STATIC:
479 return (void *)grpc_static_mdelem_user_data[GRPC_MDELEM_DATA(md) -
480 grpc_static_mdelem_table];
481 case GRPC_MDELEM_STORAGE_INTERNED: {
482 interned_metadata *im = (interned_metadata *)GRPC_MDELEM_DATA(md);
483 void *result;
484 if (gpr_atm_acq_load(&im->destroy_user_data) == (gpr_atm)destroy_func) {
485 return (void *)gpr_atm_no_barrier_load(&im->user_data);
486 } else {
487 return NULL;
488 }
489 return result;
490 }
Craig Tiller5e01e2a2017-01-20 18:11:52 -0800491 }
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800492 GPR_UNREACHABLE_CODE(return NULL);
493}
494
495void *grpc_mdelem_set_user_data(grpc_mdelem md, void (*destroy_func)(void *),
496 void *user_data) {
497 switch (GRPC_MDELEM_STORAGE(md)) {
498 case GRPC_MDELEM_STORAGE_EXTERNAL:
499 case GRPC_MDELEM_STORAGE_ALLOCATED:
500 destroy_func(user_data);
501 return NULL;
502 case GRPC_MDELEM_STORAGE_STATIC:
503 destroy_func(user_data);
504 return (void *)grpc_static_mdelem_user_data[GRPC_MDELEM_DATA(md) -
505 grpc_static_mdelem_table];
506 case GRPC_MDELEM_STORAGE_INTERNED: {
507 interned_metadata *im = (interned_metadata *)GRPC_MDELEM_DATA(md);
508 GPR_ASSERT(!is_mdelem_static(md));
509 GPR_ASSERT((user_data == NULL) == (destroy_func == NULL));
510 gpr_mu_lock(&im->mu_user_data);
511 if (gpr_atm_no_barrier_load(&im->destroy_user_data)) {
512 /* user data can only be set once */
513 gpr_mu_unlock(&im->mu_user_data);
514 if (destroy_func != NULL) {
515 destroy_func(user_data);
516 }
517 return (void *)gpr_atm_no_barrier_load(&im->user_data);
518 }
519 gpr_atm_no_barrier_store(&im->user_data, (gpr_atm)user_data);
520 gpr_atm_rel_store(&im->destroy_user_data, (gpr_atm)destroy_func);
521 gpr_mu_unlock(&im->mu_user_data);
522 return user_data;
523 }
524 }
525 GPR_UNREACHABLE_CODE(return NULL);
526}
527
528bool grpc_mdelem_eq(grpc_mdelem a, grpc_mdelem b) {
529 if (a.payload == b.payload) return true;
530 if (GRPC_MDELEM_IS_INTERNED(a) && GRPC_MDELEM_IS_INTERNED(b)) return false;
531 if (GRPC_MDISNULL(a) || GRPC_MDISNULL(b)) return false;
532 return grpc_slice_eq(GRPC_MDKEY(a), GRPC_MDKEY(b)) &&
533 grpc_slice_eq(GRPC_MDVALUE(a), GRPC_MDVALUE(b));
Craig Tiller0160de92016-11-18 08:46:46 -0800534}