diff --git a/src/hashmap.c b/src/hashmap.c index 8f47871..908df86 100644 --- a/src/hashmap.c +++ b/src/hashmap.c @@ -362,10 +362,15 @@ int hashmap_clear(hashmap *map) { } /** - * Iterates through the key/value pairs in the map, - * invoking a callback for each. The call back gets a - * key, value for each and returns an integer stop value. - * If the callback returns 1, then the iteration stops. + * Iterates through the key/value pairs in the map, invoking a + * callback for each. The call back gets a key, value for each and + * returns an integer stop value. If the callback returns 1, then the + * iteration stops. + * + * Calling hashmap_delete is safe (if not inefficient) from this + * function. Calling any insertion function will lead to undefined + * iteration order. + * * @arg map The hashmap to iterate over * @arg cb The callback function to invoke * @arg data Opaque handle passed to the callback @@ -377,9 +382,16 @@ int hashmap_iter(hashmap *map, hashmap_callback cb, void *data) { for (int i = 0; i < map->table_size && !should_break; i++) { entry = map->table + i; while (entry && entry->key && !should_break) { - // Invoke the callback + /* Grab the next entry to avoid issues where the entry may + * be freed by the callback. This is pretty inefficient + * way to handle deletes but prevents a class of bugs from + * code that decides to do that anyway + */ + hashmap_entry *next_entry = entry->next; + /* Invoke the callback */ should_break = cb(data, entry->key, entry->value, entry->metadata); - entry = entry->next; + + entry = next_entry; } } return should_break; diff --git a/src/sampling.c b/src/sampling.c index 6e0220d..02b2cf8 100644 --- a/src/sampling.c +++ b/src/sampling.c @@ -173,6 +173,8 @@ static int expiry_callback(void* _s, const char* key, void* _value, void *metada time_t now = timestamp(); if ((now - bucket->last_modified_at) > (*ttl)) { + if (_value) + free(_value); hashmap_delete(sampler->map, key); }