hashmap: add API to disable item counting when threaded
This is to address concerns raised by ThreadSanitizer on the mailing list
about threaded unprotected R/W access to map.size with my previous "disallow
rehash" change (0607e10009).
See:
https://public-inbox.org/git/adb37b70139fd1e2bac18bfd22c8b96683ae18eb.1502780344.git.martin.agren@gmail.com/
Add API to hashmap to disable item counting and thus automatic rehashing.
Also include API to later re-enable them.
When item counting is disabled, the map.size field is invalid. So to
prevent accidents, the field has been renamed and an accessor function
hashmap_get_size() has been added. All direct references to this
field have been been updated. And the name of the field changed
to map.private_size to communicate this.
Here is the relevant output from ThreadSanitizer showing the problem:
WARNING: ThreadSanitizer: data race (pid=10554)
Read of size 4 at 0x00000082d488 by thread T2 (mutexes: write M16):
#0 hashmap_add hashmap.c:209
#1 hash_dir_entry_with_parent_and_prefix name-hash.c:302
#2 handle_range_dir name-hash.c:347
#3 handle_range_1 name-hash.c:415
#4 lazy_dir_thread_proc name-hash.c:471
#5 <null> <null>
Previous write of size 4 at 0x00000082d488 by thread T1 (mutexes: write M31):
#0 hashmap_add hashmap.c:209
#1 hash_dir_entry_with_parent_and_prefix name-hash.c:302
#2 handle_range_dir name-hash.c:347
#3 handle_range_1 name-hash.c:415
#4 handle_range_dir name-hash.c:380
#5 handle_range_1 name-hash.c:415
#6 lazy_dir_thread_proc name-hash.c:471
#7 <null> <null>
Martin gives instructions for running TSan on test t3008 in this post:
https://public-inbox.org/git/CAN0heSoJDL9pWELD6ciLTmWf-a=oyxe4EXXOmCKvsG5MSuzxsA@mail.gmail.com/
Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
committed by
Junio C Hamano
parent
238e487ea9
commit
8b604d1951
26
hashmap.c
26
hashmap.c
@@ -116,9 +116,6 @@ static void rehash(struct hashmap *map, unsigned int newsize)
|
||||
unsigned int i, oldsize = map->tablesize;
|
||||
struct hashmap_entry **oldtable = map->table;
|
||||
|
||||
if (map->disallow_rehash)
|
||||
return;
|
||||
|
||||
alloc_table(map, newsize);
|
||||
for (i = 0; i < oldsize; i++) {
|
||||
struct hashmap_entry *e = oldtable[i];
|
||||
@@ -166,6 +163,12 @@ void hashmap_init(struct hashmap *map, hashmap_cmp_fn equals_function,
|
||||
while (initial_size > size)
|
||||
size <<= HASHMAP_RESIZE_BITS;
|
||||
alloc_table(map, size);
|
||||
|
||||
/*
|
||||
* Keep track of the number of items in the map and
|
||||
* allow the map to automatically grow as necessary.
|
||||
*/
|
||||
map->do_count_items = 1;
|
||||
}
|
||||
|
||||
void hashmap_free(struct hashmap *map, int free_entries)
|
||||
@@ -206,9 +209,11 @@ void hashmap_add(struct hashmap *map, void *entry)
|
||||
map->table[b] = entry;
|
||||
|
||||
/* fix size and rehash if appropriate */
|
||||
map->size++;
|
||||
if (map->size > map->grow_at)
|
||||
rehash(map, map->tablesize << HASHMAP_RESIZE_BITS);
|
||||
if (map->do_count_items) {
|
||||
map->private_size++;
|
||||
if (map->private_size > map->grow_at)
|
||||
rehash(map, map->tablesize << HASHMAP_RESIZE_BITS);
|
||||
}
|
||||
}
|
||||
|
||||
void *hashmap_remove(struct hashmap *map, const void *key, const void *keydata)
|
||||
@@ -224,9 +229,12 @@ void *hashmap_remove(struct hashmap *map, const void *key, const void *keydata)
|
||||
old->next = NULL;
|
||||
|
||||
/* fix size and rehash if appropriate */
|
||||
map->size--;
|
||||
if (map->size < map->shrink_at)
|
||||
rehash(map, map->tablesize >> HASHMAP_RESIZE_BITS);
|
||||
if (map->do_count_items) {
|
||||
map->private_size--;
|
||||
if (map->private_size < map->shrink_at)
|
||||
rehash(map, map->tablesize >> HASHMAP_RESIZE_BITS);
|
||||
}
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user