X-Git-Url: http://git.ozlabs.org/?a=blobdiff_plain;ds=sidebyside;f=ccan%2Ftdb2%2Fhash.c;h=619d56f8116c49cd954ad0f8864412a5b93ab16e;hb=20f3b260313fb4d5566aeb0d0c5439574e914e2d;hp=e1b1dfe34bdbfa071e8e4b8a43595c56af247b9d;hpb=5eaf46e9a0d38c371b50d5dd3a433fc721c1c4dc;p=ccan
diff --git a/ccan/tdb2/hash.c b/ccan/tdb2/hash.c
index e1b1dfe3..619d56f8 100644
--- a/ccan/tdb2/hash.c
+++ b/ccan/tdb2/hash.c
@@ -1,7 +1,7 @@
- /*
+ /*
Trivial Database 2: hash handling
Copyright (C) Rusty Russell 2010
-
+
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
@@ -16,11 +16,12 @@
License along with this library; if not, see .
*/
#include "private.h"
-#include
#include
+#include
-static uint64_t jenkins_hash(const void *key, size_t length, uint64_t seed,
- void *arg)
+/* Default hash function. */
+uint64_t tdb_jenkins_hash(const void *key, size_t length, uint64_t seed,
+ void *unused)
{
uint64_t ret;
/* hash64_stable assumes lower bits are more important; they are a
@@ -29,15 +30,9 @@ static uint64_t jenkins_hash(const void *key, size_t length, uint64_t seed,
return (ret >> 32) | (ret << 32);
}
-void tdb_hash_init(struct tdb_context *tdb)
-{
- tdb->khash = jenkins_hash;
- tdb->hash_priv = NULL;
-}
-
uint64_t tdb_hash(struct tdb_context *tdb, const void *ptr, size_t len)
{
- return tdb->khash(ptr, len, tdb->hash_seed, tdb->hash_priv);
+ return tdb->hash_fn(ptr, len, tdb->hash_seed, tdb->hash_data);
}
uint64_t hash_record(struct tdb_context *tdb, tdb_off_t off)
@@ -47,16 +42,18 @@ uint64_t hash_record(struct tdb_context *tdb, tdb_off_t off)
uint64_t klen, hash;
r = tdb_access_read(tdb, off, sizeof(*r), true);
- if (!r)
+ if (TDB_PTR_IS_ERR(r)) {
/* FIXME */
return 0;
+ }
klen = rec_key_length(r);
tdb_access_release(tdb, r);
key = tdb_access_read(tdb, off + sizeof(*r), klen, false);
- if (!key)
+ if (TDB_PTR_IS_ERR(key)) {
return 0;
+ }
hash = tdb_hash(tdb, key, klen);
tdb_access_release(tdb, key);
@@ -78,43 +75,45 @@ static uint32_t use_bits(struct hash_info *h, unsigned num)
return bits_from(h->h, 64 - h->hash_used, num);
}
-static bool key_matches(struct tdb_context *tdb,
- const struct tdb_used_record *rec,
- tdb_off_t off,
- const struct tdb_data *key)
+static tdb_bool_err key_matches(struct tdb_context *tdb,
+ const struct tdb_used_record *rec,
+ tdb_off_t off,
+ const struct tdb_data *key)
{
- bool ret = false;
+ tdb_bool_err ret = false;
const char *rkey;
if (rec_key_length(rec) != key->dsize) {
- add_stat(tdb, compare_wrong_keylen, 1);
+ tdb->stats.compare_wrong_keylen++;
return ret;
}
rkey = tdb_access_read(tdb, off + sizeof(*rec), key->dsize, false);
- if (!rkey)
- return ret;
+ if (TDB_PTR_IS_ERR(rkey)) {
+ return (tdb_bool_err)TDB_PTR_ERR(rkey);
+ }
if (memcmp(rkey, key->dptr, key->dsize) == 0)
ret = true;
else
- add_stat(tdb, compare_wrong_keycmp, 1);
+ tdb->stats.compare_wrong_keycmp++;
tdb_access_release(tdb, rkey);
return ret;
}
/* Does entry match? */
-static bool match(struct tdb_context *tdb,
- struct hash_info *h,
- const struct tdb_data *key,
- tdb_off_t val,
- struct tdb_used_record *rec)
+static tdb_bool_err match(struct tdb_context *tdb,
+ struct hash_info *h,
+ const struct tdb_data *key,
+ tdb_off_t val,
+ struct tdb_used_record *rec)
{
tdb_off_t off;
+ enum TDB_ERROR ecode;
- add_stat(tdb, compares, 1);
+ tdb->stats.compares++;
/* Desired bucket must match. */
if (h->home_bucket != (val & TDB_OFF_HASH_GROUP_MASK)) {
- add_stat(tdb, compare_wrong_bucket, 1);
+ tdb->stats.compare_wrong_bucket++;
return false;
}
@@ -122,16 +121,18 @@ static bool match(struct tdb_context *tdb,
if (bits_from(val, TDB_OFF_HASH_EXTRA_BIT, TDB_OFF_UPPER_STEAL_EXTRA)
!= bits_from(h->h, 64 - h->hash_used - TDB_OFF_UPPER_STEAL_EXTRA,
TDB_OFF_UPPER_STEAL_EXTRA)) {
- add_stat(tdb, compare_wrong_offsetbits, 1);
+ tdb->stats.compare_wrong_offsetbits++;
return false;
}
off = val & TDB_OFF_MASK;
- if (tdb_read_convert(tdb, off, rec, sizeof(*rec)) == -1)
- return false;
+ ecode = tdb_read_convert(tdb, off, rec, sizeof(*rec));
+ if (ecode != TDB_SUCCESS) {
+ return (tdb_bool_err)ecode;
+ }
if ((h->h & ((1 << 11)-1)) != rec_hash(rec)) {
- add_stat(tdb, compare_wrong_rechash, 1);
+ tdb->stats.compare_wrong_rechash++;
return false;
}
@@ -164,6 +165,7 @@ static tdb_off_t COLD find_in_chain(struct tdb_context *tdb,
struct traverse_info *tinfo)
{
tdb_off_t off, next;
+ enum TDB_ERROR ecode;
/* In case nothing is free, we set these to zero. */
h->home_bucket = h->found_bucket = 0;
@@ -172,8 +174,10 @@ static tdb_off_t COLD find_in_chain(struct tdb_context *tdb,
unsigned int i;
h->group_start = off;
- if (tdb_read_convert(tdb, off, h->group, sizeof(h->group)))
- return TDB_OFF_ERR;
+ ecode = tdb_read_convert(tdb, off, h->group, sizeof(h->group));
+ if (ecode != TDB_SUCCESS) {
+ return TDB_ERR_TO_OFF(ecode);
+ }
for (i = 0; i < (1 << TDB_HASH_GROUP_BITS); i++) {
tdb_off_t recoff;
@@ -186,10 +190,18 @@ static tdb_off_t COLD find_in_chain(struct tdb_context *tdb,
/* We can insert extra bits via add_to_hash
* empty bucket logic. */
recoff = h->group[i] & TDB_OFF_MASK;
- if (tdb_read_convert(tdb, recoff, rec, sizeof(*rec)))
- return TDB_OFF_ERR;
+ ecode = tdb_read_convert(tdb, recoff, rec,
+ sizeof(*rec));
+ if (ecode != TDB_SUCCESS) {
+ return TDB_ERR_TO_OFF(ecode);
+ }
- if (key_matches(tdb, rec, recoff, &key)) {
+ ecode = TDB_OFF_TO_ERR(key_matches(tdb, rec, recoff,
+ &key));
+ if (ecode < 0) {
+ return TDB_ERR_TO_OFF(ecode);
+ }
+ if (ecode == (enum TDB_ERROR)1) {
h->home_bucket = h->found_bucket = i;
if (tinfo) {
@@ -207,8 +219,9 @@ static tdb_off_t COLD find_in_chain(struct tdb_context *tdb,
}
next = tdb_read_off(tdb, off
+ offsetof(struct tdb_chain, next));
- if (next == TDB_OFF_ERR)
- return TDB_OFF_ERR;
+ if (TDB_OFF_IS_ERR(next)) {
+ return next;
+ }
if (next)
next += sizeof(struct tdb_used_record);
}
@@ -216,7 +229,7 @@ static tdb_off_t COLD find_in_chain(struct tdb_context *tdb,
}
/* This is the core routine which searches the hashtable for an entry.
- * On error, no locks are held and TDB_OFF_ERR is returned.
+ * On error, no locks are held and -ve is returned.
* Otherwise, hinfo is filled in (and the optional tinfo).
* If not found, the return value is 0.
* If found, the return value is the offset, and *rec is the record. */
@@ -229,6 +242,7 @@ tdb_off_t find_and_lock(struct tdb_context *tdb,
{
uint32_t i, group;
tdb_off_t hashtable;
+ enum TDB_ERROR ecode;
h->h = tdb_hash(tdb, key.dptr, key.dsize);
h->hash_used = 0;
@@ -236,16 +250,18 @@ tdb_off_t find_and_lock(struct tdb_context *tdb,
h->home_bucket = use_bits(h, TDB_HASH_GROUP_BITS);
h->hlock_start = hlock_range(group, &h->hlock_range);
- if (tdb_lock_hashes(tdb, h->hlock_start, h->hlock_range, ltype,
- TDB_LOCK_WAIT))
- return TDB_OFF_ERR;
+ ecode = tdb_lock_hashes(tdb, h->hlock_start, h->hlock_range, ltype,
+ TDB_LOCK_WAIT);
+ if (ecode != TDB_SUCCESS) {
+ return TDB_ERR_TO_OFF(ecode);
+ }
hashtable = offsetof(struct tdb_header, hashtable);
if (tinfo) {
tinfo->toplevel_group = group;
tinfo->num_levels = 1;
tinfo->levels[0].entry = 0;
- tinfo->levels[0].hashtable = hashtable
+ tinfo->levels[0].hashtable = hashtable
+ (group << TDB_HASH_GROUP_BITS) * sizeof(tdb_off_t);
tinfo->levels[0].total_buckets = 1 << TDB_HASH_GROUP_BITS;
}
@@ -255,9 +271,11 @@ tdb_off_t find_and_lock(struct tdb_context *tdb,
h->group_start = hashtable
+ group * (sizeof(tdb_off_t) << TDB_HASH_GROUP_BITS);
- if (tdb_read_convert(tdb, h->group_start, &h->group,
- sizeof(h->group)) == -1)
+ ecode = tdb_read_convert(tdb, h->group_start, &h->group,
+ sizeof(h->group));
+ if (ecode != TDB_SUCCESS) {
goto fail;
+ }
/* Pointer to another hash table? Go down... */
if (is_subhash(h->group[h->home_bucket])) {
@@ -288,14 +306,20 @@ tdb_off_t find_and_lock(struct tdb_context *tdb,
i < (1 << TDB_HASH_GROUP_BITS);
i++, h->found_bucket = ((h->found_bucket+1)
% (1 << TDB_HASH_GROUP_BITS))) {
+ tdb_bool_err berr;
if (is_subhash(h->group[h->found_bucket]))
continue;
if (!h->group[h->found_bucket])
break;
- if (match(tdb, h, &key, h->group[h->found_bucket],
- rec)) {
+ berr = match(tdb, h, &key, h->group[h->found_bucket],
+ rec);
+ if (berr < 0) {
+ ecode = TDB_OFF_TO_ERR(berr);
+ goto fail;
+ }
+ if (berr) {
if (tinfo) {
tinfo->levels[tinfo->num_levels-1].entry
+= h->found_bucket;
@@ -311,7 +335,7 @@ tdb_off_t find_and_lock(struct tdb_context *tdb,
fail:
tdb_unlock_hashes(tdb, h->hlock_start, h->hlock_range, ltype);
- return TDB_OFF_ERR;
+ return TDB_ERR_TO_OFF(ecode);
}
/* I wrote a simple test, expanding a hash to 2GB, for the following
@@ -384,42 +408,55 @@ static tdb_off_t encode_offset(tdb_off_t new_off, struct hash_info *h)
<< TDB_OFF_HASH_EXTRA_BIT);
}
-/* Simply overwrite the hash entry we found before. */
-int replace_in_hash(struct tdb_context *tdb,
- struct hash_info *h,
- tdb_off_t new_off)
+/* Simply overwrite the hash entry we found before. */
+enum TDB_ERROR replace_in_hash(struct tdb_context *tdb,
+ struct hash_info *h,
+ tdb_off_t new_off)
{
return tdb_write_off(tdb, hbucket_off(h->group_start, h->found_bucket),
encode_offset(new_off, h));
}
/* We slot in anywhere that's empty in the chain. */
-static int COLD add_to_chain(struct tdb_context *tdb,
- tdb_off_t subhash,
- tdb_off_t new_off)
+static enum TDB_ERROR COLD add_to_chain(struct tdb_context *tdb,
+ tdb_off_t subhash,
+ tdb_off_t new_off)
{
- size_t entry = tdb_find_zero_off(tdb, subhash, 1<group, h->home_bucket);
if (h->hash_used == 64) {
- add_stat(tdb, alloc_chain, 1);
+ tdb->stats.alloc_chain++;
subsize = sizeof(struct tdb_chain);
magic = TDB_CHAIN_MAGIC;
} else {
- add_stat(tdb, alloc_subhash, 1);
+ tdb->stats.alloc_subhash++;
subsize = (sizeof(tdb_off_t) << TDB_SUBLEVEL_HASH_BITS);
magic = TDB_HTABLE_MAGIC;
}
subhash = alloc(tdb, 0, subsize, 0, magic, false);
- if (subhash == TDB_OFF_ERR)
- return -1;
+ if (TDB_OFF_IS_ERR(subhash)) {
+ return TDB_OFF_TO_ERR(subhash);
+ }
- if (zero_out(tdb, subhash + sizeof(struct tdb_used_record), subsize))
- return -1;
+ ecode = zero_out(tdb, subhash + sizeof(struct tdb_used_record),
+ subsize);
+ if (ecode != TDB_SUCCESS) {
+ return ecode;
+ }
/* Remove any which are destined for bucket or are in wrong place. */
num_vals = 0;
@@ -507,17 +550,19 @@ static int expand_group(struct tdb_context *tdb, struct hash_info *h)
unsigned this_bucket = vals[i] & TDB_OFF_HASH_GROUP_MASK;
if (this_bucket == bucket) {
- if (add_to_subhash(tdb, subhash, h->hash_used, vals[i]))
- return -1;
+ ecode = add_to_subhash(tdb, subhash, h->hash_used,
+ vals[i]);
+ if (ecode != TDB_SUCCESS)
+ return ecode;
} else {
/* There should be room to put this back. */
force_into_group(h->group, this_bucket, vals[i]);
}
}
- return 0;
+ return TDB_SUCCESS;
}
-int delete_from_hash(struct tdb_context *tdb, struct hash_info *h)
+enum TDB_ERROR delete_from_hash(struct tdb_context *tdb, struct hash_info *h)
{
unsigned int i, num_movers = 0;
tdb_off_t movers[1 << TDB_HASH_GROUP_BITS];
@@ -554,8 +599,11 @@ int delete_from_hash(struct tdb_context *tdb, struct hash_info *h)
h->group, sizeof(h->group));
}
-int add_to_hash(struct tdb_context *tdb, struct hash_info *h, tdb_off_t new_off)
+enum TDB_ERROR add_to_hash(struct tdb_context *tdb, struct hash_info *h,
+ tdb_off_t new_off)
{
+ enum TDB_ERROR ecode;
+
/* We hit an empty bucket during search? That's where it goes. */
if (!h->group[h->found_bucket]) {
h->group[h->found_bucket] = encode_offset(new_off, h);
@@ -568,8 +616,10 @@ int add_to_hash(struct tdb_context *tdb, struct hash_info *h, tdb_off_t new_off)
return add_to_chain(tdb, h->group_start, new_off);
/* We're full. Expand. */
- if (expand_group(tdb, h) == -1)
- return -1;
+ ecode = expand_group(tdb, h);
+ if (ecode != TDB_SUCCESS) {
+ return ecode;
+ }
if (is_subhash(h->group[h->home_bucket])) {
/* We were expanded! */
@@ -577,9 +627,11 @@ int add_to_hash(struct tdb_context *tdb, struct hash_info *h, tdb_off_t new_off)
unsigned int gnum;
/* Write back the modified group. */
- if (tdb_write_convert(tdb, h->group_start, h->group,
- sizeof(h->group)))
- return -1;
+ ecode = tdb_write_convert(tdb, h->group_start, h->group,
+ sizeof(h->group));
+ if (ecode != TDB_SUCCESS) {
+ return ecode;
+ }
/* Move hashinfo down a level. */
hashtable = (h->group[h->home_bucket] & TDB_OFF_MASK)
@@ -588,28 +640,30 @@ int add_to_hash(struct tdb_context *tdb, struct hash_info *h, tdb_off_t new_off)
h->home_bucket = use_bits(h, TDB_HASH_GROUP_BITS);
h->group_start = hashtable
+ gnum * (sizeof(tdb_off_t) << TDB_HASH_GROUP_BITS);
- if (tdb_read_convert(tdb, h->group_start, &h->group,
- sizeof(h->group)) == -1)
- return -1;
+ ecode = tdb_read_convert(tdb, h->group_start, &h->group,
+ sizeof(h->group));
+ if (ecode != TDB_SUCCESS) {
+ return ecode;
+ }
}
/* Expanding the group must have made room if it didn't choose this
* bucket. */
- if (put_into_group(h->group, h->home_bucket, encode_offset(new_off, h)))
+ if (put_into_group(h->group, h->home_bucket, encode_offset(new_off,h))){
return tdb_write_convert(tdb, h->group_start,
h->group, sizeof(h->group));
+ }
/* This can happen if all hashes in group (and us) dropped into same
* group in subhash. */
return add_to_hash(tdb, h, new_off);
}
-/* Traverse support: returns offset of record, or 0 or TDB_OFF_ERR. */
+/* Traverse support: returns offset of record, or 0 or -ve error. */
static tdb_off_t iterate_hash(struct tdb_context *tdb,
struct traverse_info *tinfo)
{
- tdb_off_t off, val;
- unsigned int i;
+ tdb_off_t off, val, i;
struct traverse_level *tlevel;
tlevel = &tinfo->levels[tinfo->num_levels-1];
@@ -620,9 +674,14 @@ again:
i != tlevel->total_buckets;
i = tdb_find_nonzero_off(tdb, tlevel->hashtable,
i+1, tlevel->total_buckets)) {
+ if (TDB_OFF_IS_ERR(i)) {
+ return i;
+ }
+
val = tdb_read_off(tdb, tlevel->hashtable+sizeof(tdb_off_t)*i);
- if (unlikely(val == TDB_OFF_ERR))
- return TDB_OFF_ERR;
+ if (TDB_OFF_IS_ERR(val)) {
+ return val;
+ }
off = val & TDB_OFF_MASK;
@@ -662,8 +721,9 @@ again:
tlevel->hashtable = tdb_read_off(tdb, tlevel->hashtable
+ offsetof(struct tdb_chain,
next));
- if (tlevel->hashtable == TDB_OFF_ERR)
- return TDB_OFF_ERR;
+ if (TDB_OFF_IS_ERR(tlevel->hashtable)) {
+ return tlevel->hashtable;
+ }
if (tlevel->hashtable) {
tlevel->hashtable += sizeof(struct tdb_used_record);
tlevel->entry = 0;
@@ -677,38 +737,45 @@ again:
goto again;
}
-/* Return 1 if we find something, 0 if not, -1 on error. */
-int next_in_hash(struct tdb_context *tdb, int ltype,
- struct traverse_info *tinfo,
- TDB_DATA *kbuf, size_t *dlen)
+/* Return success if we find something, TDB_ERR_NOEXIST if none. */
+enum TDB_ERROR next_in_hash(struct tdb_context *tdb,
+ struct traverse_info *tinfo,
+ TDB_DATA *kbuf, size_t *dlen)
{
const unsigned group_bits = TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS;
tdb_off_t hl_start, hl_range, off;
+ enum TDB_ERROR ecode;
while (tinfo->toplevel_group < (1 << group_bits)) {
hl_start = (tdb_off_t)tinfo->toplevel_group
<< (64 - group_bits);
hl_range = 1ULL << group_bits;
- if (tdb_lock_hashes(tdb, hl_start, hl_range, ltype,
- TDB_LOCK_WAIT) != 0)
- return -1;
+ ecode = tdb_lock_hashes(tdb, hl_start, hl_range, F_RDLCK,
+ TDB_LOCK_WAIT);
+ if (ecode != TDB_SUCCESS) {
+ return ecode;
+ }
off = iterate_hash(tdb, tinfo);
if (off) {
struct tdb_used_record rec;
- if (tdb_read_convert(tdb, off, &rec, sizeof(rec))) {
- tdb_unlock_hashes(tdb,
- hl_start, hl_range, ltype);
- return -1;
+ if (TDB_OFF_IS_ERR(off)) {
+ ecode = TDB_OFF_TO_ERR(off);
+ goto fail;
+ }
+
+ ecode = tdb_read_convert(tdb, off, &rec, sizeof(rec));
+ if (ecode != TDB_SUCCESS) {
+ goto fail;
}
if (rec_magic(&rec) != TDB_USED_MAGIC) {
- tdb_logerr(tdb, TDB_ERR_CORRUPT,
- TDB_DEBUG_FATAL,
- "next_in_hash:"
- " corrupt record at %llu",
- (long long)off);
- return -1;
+ ecode = tdb_logerr(tdb, TDB_ERR_CORRUPT,
+ TDB_LOG_ERROR,
+ "next_in_hash:"
+ " corrupt record at %llu",
+ (long long)off);
+ goto fail;
}
kbuf->dsize = rec_key_length(&rec);
@@ -716,33 +783,40 @@ int next_in_hash(struct tdb_context *tdb, int ltype,
/* They want data as well? */
if (dlen) {
*dlen = rec_data_length(&rec);
- kbuf->dptr = tdb_alloc_read(tdb,
+ kbuf->dptr = tdb_alloc_read(tdb,
off + sizeof(rec),
kbuf->dsize
+ *dlen);
} else {
- kbuf->dptr = tdb_alloc_read(tdb,
+ kbuf->dptr = tdb_alloc_read(tdb,
off + sizeof(rec),
kbuf->dsize);
}
- tdb_unlock_hashes(tdb, hl_start, hl_range, ltype);
- return kbuf->dptr ? 1 : -1;
+ tdb_unlock_hashes(tdb, hl_start, hl_range, F_RDLCK);
+ if (TDB_PTR_IS_ERR(kbuf->dptr)) {
+ return TDB_PTR_ERR(kbuf->dptr);
+ }
+ return TDB_SUCCESS;
}
- tdb_unlock_hashes(tdb, hl_start, hl_range, ltype);
+ tdb_unlock_hashes(tdb, hl_start, hl_range, F_RDLCK);
tinfo->toplevel_group++;
tinfo->levels[0].hashtable
+= (sizeof(tdb_off_t) << TDB_HASH_GROUP_BITS);
tinfo->levels[0].entry = 0;
}
- return 0;
+ return TDB_ERR_NOEXIST;
+
+fail:
+ tdb_unlock_hashes(tdb, hl_start, hl_range, F_RDLCK);
+ return ecode;
+
}
-/* Return 1 if we find something, 0 if not, -1 on error. */
-int first_in_hash(struct tdb_context *tdb, int ltype,
- struct traverse_info *tinfo,
- TDB_DATA *kbuf, size_t *dlen)
+enum TDB_ERROR first_in_hash(struct tdb_context *tdb,
+ struct traverse_info *tinfo,
+ TDB_DATA *kbuf, size_t *dlen)
{
tinfo->prev = 0;
tinfo->toplevel_group = 0;
@@ -751,16 +825,16 @@ int first_in_hash(struct tdb_context *tdb, int ltype,
tinfo->levels[0].entry = 0;
tinfo->levels[0].total_buckets = (1 << TDB_HASH_GROUP_BITS);
- return next_in_hash(tdb, ltype, tinfo, kbuf, dlen);
+ return next_in_hash(tdb, tinfo, kbuf, dlen);
}
/* Even if the entry isn't in this hash bucket, you'd have to lock this
* bucket to find it. */
-static int chainlock(struct tdb_context *tdb, const TDB_DATA *key,
- int ltype, enum tdb_lock_flags waitflag,
- const char *func)
+static enum TDB_ERROR chainlock(struct tdb_context *tdb, const TDB_DATA *key,
+ int ltype, enum tdb_lock_flags waitflag,
+ const char *func)
{
- int ret;
+ enum TDB_ERROR ecode;
uint64_t h = tdb_hash(tdb, key->dptr, key->dsize);
tdb_off_t lockstart, locksize;
unsigned int group, gbits;
@@ -770,29 +844,70 @@ static int chainlock(struct tdb_context *tdb, const TDB_DATA *key,
lockstart = hlock_range(group, &locksize);
- ret = tdb_lock_hashes(tdb, lockstart, locksize, ltype, waitflag);
+ ecode = tdb_lock_hashes(tdb, lockstart, locksize, ltype, waitflag);
tdb_trace_1rec(tdb, func, *key);
- return ret;
+ return ecode;
}
/* lock/unlock one hash chain. This is meant to be used to reduce
contention - it cannot guarantee how many records will be locked */
-int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key)
+enum TDB_ERROR tdb_chainlock(struct tdb_context *tdb, TDB_DATA key)
{
- return chainlock(tdb, &key, F_WRLCK, TDB_LOCK_WAIT, "tdb_chainlock");
+ if (tdb->flags & TDB_VERSION1) {
+ if (tdb1_chainlock(tdb, key) == -1)
+ return tdb->last_error;
+ return TDB_SUCCESS;
+ }
+ return tdb->last_error = chainlock(tdb, &key, F_WRLCK, TDB_LOCK_WAIT,
+ "tdb_chainlock");
}
-int tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key)
+void tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key)
{
uint64_t h = tdb_hash(tdb, key.dptr, key.dsize);
tdb_off_t lockstart, locksize;
unsigned int group, gbits;
+ if (tdb->flags & TDB_VERSION1) {
+ tdb1_chainunlock(tdb, key);
+ return;
+ }
+
gbits = TDB_TOPLEVEL_HASH_BITS - TDB_HASH_GROUP_BITS;
group = bits_from(h, 64 - gbits, gbits);
lockstart = hlock_range(group, &locksize);
tdb_trace_1rec(tdb, "tdb_chainunlock", key);
- return tdb_unlock_hashes(tdb, lockstart, locksize, F_WRLCK);
+ tdb_unlock_hashes(tdb, lockstart, locksize, F_WRLCK);
+}
+
+enum TDB_ERROR tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key)
+{
+ if (tdb->flags & TDB_VERSION1) {
+ if (tdb1_chainlock_read(tdb, key) == -1)
+ return tdb->last_error;
+ return TDB_SUCCESS;
+ }
+ return tdb->last_error = chainlock(tdb, &key, F_RDLCK, TDB_LOCK_WAIT,
+ "tdb_chainlock_read");
+}
+
+void tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key)
+{
+ uint64_t h = tdb_hash(tdb, key.dptr, key.dsize);
+ tdb_off_t lockstart, locksize;
+ unsigned int group, gbits;
+
+ if (tdb->flags & TDB_VERSION1) {
+ tdb1_chainunlock_read(tdb, key);
+ return;
+ }
+ gbits = TDB_TOPLEVEL_HASH_BITS - TDB_HASH_GROUP_BITS;
+ group = bits_from(h, 64 - gbits, gbits);
+
+ lockstart = hlock_range(group, &locksize);
+
+ tdb_trace_1rec(tdb, "tdb_chainunlock_read", key);
+ tdb_unlock_hashes(tdb, lockstart, locksize, F_RDLCK);
}