opr: Allow non-2^x for n_buckets in opr_cache_init 22/14122/3
authorAndrew Deason <adeason@sinenomine.net>
Mon, 30 Mar 2020 19:21:21 +0000 (14:21 -0500)
committerBenjamin Kaduk <kaduk@mit.edu>
Fri, 10 Apr 2020 13:55:35 +0000 (09:55 -0400)
Currently, opr_cache_init requires that opts->n_buckets is a power of
2 (since our underlying opr_dict requires this). However, callers may
want to pick a number of buckets based on some other value. Requiring
each caller to calculate the nearest power-of-2 is annoying, so
instead just have opr_cache_init itself calculate a nearby power of 2.

That is, with this commit, opts->n_buckets is allowed to not be a
power of 2; when it's not a power of 2, opr_cache_init will calculate
the next highest power of 2 and use that as the number of buckets.

Change-Id: Icd3c56c1fe0733e3dac964ea9a98ff7b436254e6
Reviewed-on: https://gerrit.openafs.org/14122
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: Marcio Brito Barbosa <mbarbosa@sinenomine.net>
Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>

src/opr/cache.c
tests/opr/cache-t.c

index 82b759d..54fbf3e 100644 (file)
@@ -356,6 +356,21 @@ isPowerOf2(int value)
     return (value > 0) && (value & (value - 1)) == 0;
 }
 
+static_inline int
+nextPowerOf2(int target)
+{
+    int next = 1;
+    /*
+     * Make sure we have a reasonable target and cannot overflow; callers
+     * should do their own range checks before we get here.
+     */
+    opr_Assert(target > 0 && target <= 0x40000000);
+    while (next < target) {
+       next *= 2;
+    }
+    return next;
+}
+
 /**
  * Create a new opr_cache.
  *
@@ -369,17 +384,20 @@ int
 opr_cache_init(struct opr_cache_opts *opts, struct opr_cache **a_cache)
 {
     struct opr_cache *cache;
+    int n_buckets = opts->n_buckets;
 
-    if (opts->n_buckets < MIN_BUCKETS || opts->n_buckets > MAX_BUCKETS) {
-       return EINVAL;
-    }
-    if (!isPowerOf2(opts->n_buckets)) {
+    if (n_buckets < MIN_BUCKETS || n_buckets > MAX_BUCKETS) {
        return EINVAL;
     }
     if (opts->max_entries < MIN_ENTRIES || opts->max_entries > MAX_ENTRIES) {
        return EINVAL;
     }
 
+    n_buckets = nextPowerOf2(n_buckets);
+    opr_Assert(isPowerOf2(n_buckets));
+    opr_Assert(n_buckets >= MIN_BUCKETS);
+    opr_Assert(n_buckets <= MAX_BUCKETS);
+
     cache = calloc(1, sizeof(*cache));
     if (cache == NULL) {
        return ENOMEM;
@@ -388,7 +406,7 @@ opr_cache_init(struct opr_cache_opts *opts, struct opr_cache **a_cache)
     opr_mutex_init(&cache->lock);
     cache->max_entries = opts->max_entries;
 
-    cache->dict = opr_dict_Init(opts->n_buckets);
+    cache->dict = opr_dict_Init(n_buckets);
     if (cache->dict == NULL) {
        opr_cache_free(&cache);
        return ENOMEM;
index 522bfaf..2d97b2f 100644 (file)
@@ -102,9 +102,27 @@ run_seed(int seed)
     ok(opr_cache_init(&opts, &cache) != 0,
        "Initializing a cache with a huge n_buckets fails");
 
-    opts.n_buckets = 23;
+    opts.n_buckets = 1024*1024 + 1;
     ok(opr_cache_init(&opts, &cache) != 0,
-       "Initializing a cache with non-power-of-2 n_buckets fails");
+       "Initializing a cache with 1024*1024+1 n_buckets fails");
+
+    opts.n_buckets = 1024*1024;
+    code = opr_cache_init(&opts, &cache);
+    is_int(0, code,
+       "Initializing a cache with 1024*1024 n_buckets succeeds");
+    opr_cache_free(&cache);
+
+    opts.n_buckets = 1024*1024 - 1;
+    code = opr_cache_init(&opts, &cache);
+    is_int(0, code,
+       "Initializing a cache with 1024*1024-1 n_buckets succeeds");
+    opr_cache_free(&cache);
+
+    opts.n_buckets = 23;
+    code = opr_cache_init(&opts, &cache);
+    is_int(0, code,
+       "Initializing a cache with non-power-of-2 n_buckets succeeds");
+    opr_cache_free(&cache);
 
     opts.n_buckets = 64;
     opts.max_entries = 1;
@@ -237,7 +255,7 @@ main(void)
 {
     int seed;
 
-    plan(113 * 32);
+    plan(116 * 32);
 
     for (seed = 0; seed < 32; seed++) {
        run_seed(seed);