Browse Source

H2O: Use adaptive mutexes for the cache again (#4373)

Anton Kirilov 6 years ago
parent
commit
29702196ef
2 changed files with 69 additions and 26 deletions
  1. 68 26
      frameworks/C/h2o/src/cache.c
  2. 1 0
      frameworks/C/h2o/src/cache.h

+ 68 - 26
frameworks/C/h2o/src/cache.c

@@ -22,15 +22,16 @@
 #include <h2o/cache.h>
 
 #include "cache.h"
+#include "error.h"
 #include "utility.h"
 
 // Increasing the number of caches by the following factor reduces contention; must be a power of 2.
 #define CONCURRENCY_FACTOR 4
 
-static h2o_cache_t *get_cache(cache_t *cache, h2o_cache_hashcode_t keyhash)
+static size_t get_index(size_t n, h2o_cache_hashcode_t keyhash)
 {
-	assert(is_power_of_2(cache->cache_num));
-	return cache->cache[keyhash & (cache->cache_num - 1)];
+	assert(is_power_of_2(n));
+	return keyhash & (n - 1);
 }
 
 int cache_create(size_t concurrency,
@@ -39,8 +40,6 @@ int cache_create(size_t concurrency,
                  void (*destroy_cb)(h2o_iovec_t value),
                  cache_t *cache)
 {
-	int ret = EXIT_SUCCESS;
-
 	memset(cache, 0, sizeof(*cache));
 	assert(is_power_of_2(CONCURRENCY_FACTOR));
 	// Rounding up to a power of 2 simplifies the calculations a little bit, and, as any increase in
@@ -48,54 +47,97 @@ int cache_create(size_t concurrency,
 	cache->cache_num = CONCURRENCY_FACTOR * round_up_to_power_of_2(concurrency);
 	cache->cache_num = MAX(cache->cache_num, 1);
 	capacity = (capacity + cache->cache_num - 1) / cache->cache_num;
+
+	pthread_mutexattr_t attr;
+
+	if (pthread_mutexattr_init(&attr))
+		return EXIT_FAILURE;
+
+	if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP))
+		goto error;
+
 	cache->cache = malloc(cache->cache_num * sizeof(*cache->cache));
 
-	if (cache->cache)
-		for (size_t i = 0; i < cache->cache_num; i++) {
-			cache->cache[i] = h2o_cache_create(H2O_CACHE_FLAG_MULTITHREADED,
-			                                   capacity,
-			                                   duration,
-			                                   destroy_cb);
-
-			if (!cache->cache[i]) {
-				cache->cache_num = i;
-				cache_destroy(cache);
-				cache->cache = NULL;
-				ret = EXIT_FAILURE;
-				break;
-			}
+	if (!cache->cache)
+		goto error;
+
+	cache->cache_lock = malloc(cache->cache_num * sizeof(*cache->cache_lock));
+
+	if (!cache->cache_lock)
+		goto error_malloc;
+
+	for (size_t i = 0; i < cache->cache_num; i++) {
+		cache->cache[i] = h2o_cache_create(0, capacity, duration, destroy_cb);
+
+		if (!cache->cache[i] || pthread_mutex_init(cache->cache_lock + i, &attr)) {
+			if (cache->cache[i])
+				h2o_cache_destroy(cache->cache[i]);
+
+			cache->cache_num = i;
+			cache_destroy(cache);
+			goto error;
 		}
-	else
-		ret = EXIT_FAILURE;
+	}
 
-	return ret;
+	pthread_mutexattr_destroy(&attr);
+	return EXIT_SUCCESS;
+error_malloc:
+	free(cache->cache);
+error:
+	pthread_mutexattr_destroy(&attr);
+	return EXIT_FAILURE;
 }
 
 void cache_destroy(cache_t *cache)
 {
 	if (cache->cache) {
-		for (size_t i = 0; i < cache->cache_num; i++)
+		assert(cache->cache_lock);
+
+		for (size_t i = 0; i < cache->cache_num; i++) {
 			h2o_cache_destroy(cache->cache[i]);
+			pthread_mutex_destroy(cache->cache_lock + i);
+		}
 
 		free(cache->cache);
+		free(cache->cache_lock);
+		cache->cache = NULL;
+		cache->cache_lock = NULL;
 	}
+	else
+		assert(!cache->cache_lock);
 }
 
 h2o_cache_ref_t *cache_fetch(cache_t *cache, uint64_t now, h2o_iovec_t key)
 {
 	const h2o_cache_hashcode_t keyhash = h2o_cache_calchash(key.base, key.len);
+	const size_t idx = get_index(cache->cache_num, keyhash);
+	pthread_mutex_t * const mutex = cache->cache_lock + idx;
+
+	CHECK_ERROR(pthread_mutex_lock, mutex);
 
-	return h2o_cache_fetch(get_cache(cache, keyhash), now, key, keyhash);
+	h2o_cache_ref_t * const ret = h2o_cache_fetch(cache->cache[idx], now, key, keyhash);
+
+	CHECK_ERROR(pthread_mutex_unlock, mutex);
+	return ret;
 }
 
 void cache_release(cache_t *cache, h2o_cache_ref_t *ref)
 {
-	h2o_cache_release(get_cache(cache, h2o_cache_calchash(ref->key.base, ref->key.len)), ref);
+	const size_t idx = get_index(cache->cache_num, h2o_cache_calchash(ref->key.base, ref->key.len));
+
+	h2o_cache_release(cache->cache[idx], ref);
 }
 
 int cache_set(uint64_t now, h2o_iovec_t key, h2o_iovec_t value, cache_t *cache)
 {
 	const h2o_cache_hashcode_t keyhash = h2o_cache_calchash(key.base, key.len);
+	const size_t idx = get_index(cache->cache_num, keyhash);
+	pthread_mutex_t * const mutex = cache->cache_lock + idx;
 
-	return h2o_cache_set(get_cache(cache, keyhash), now, key, keyhash, value);
+	CHECK_ERROR(pthread_mutex_lock, mutex);
+
+	const int ret = h2o_cache_set(cache->cache[idx], now, key, keyhash, value);
+
+	CHECK_ERROR(pthread_mutex_unlock, mutex);
+	return ret;
 }

+ 1 - 0
frameworks/C/h2o/src/cache.h

@@ -25,6 +25,7 @@
 
 typedef struct {
 	h2o_cache_t **cache;
+	pthread_mutex_t *cache_lock;
 	size_t cache_num;
 } cache_t;