OSDN Git Service

Add internal constructors to hash map and list
authorZach Johnson <zachoverflow@google.com>
Wed, 27 Aug 2014 00:21:44 +0000 (17:21 -0700)
committerAndre Eisenbach <eisenbach@google.com>
Mon, 16 Mar 2015 23:51:30 +0000 (16:51 -0700)
This makes allocation tracker safe and ensures the hash map
and associated lists for the allocation tracker don't show
up in the allocation tracker's stats.

osi/include/hash_map.h
osi/src/allocation_tracker.c
osi/src/hash_map.c
osi/src/list.c

index a751ce4..bb0cb5e 100644 (file)
@@ -40,8 +40,11 @@ typedef void (*key_free_fn)(void *data);
 typedef void (*data_free_fn)(void *data);
 
 // Lifecycle.
-hash_map_t *hash_map_new(size_t size, hash_index_fn hash_fn,
-    key_free_fn key_fn, data_free_fn);
+hash_map_t *hash_map_new(
+    size_t size,
+    hash_index_fn hash_fn,
+    key_free_fn key_fn,
+    data_free_fn);
 void hash_map_free(hash_map_t *hash_map);
 
 // Accessors.
index 6e127e2..8b314a1 100644 (file)
@@ -21,6 +21,7 @@
 #include <utils/Log.h>
 
 #include "allocation_tracker.h"
+#include "allocator.h"
 #include "hash_functions.h"
 #include "hash_map.h"
 #include "osi.h"
@@ -33,7 +34,23 @@ typedef struct {
   bool freed;
 } allocation_t;
 
+
+// Hidden constructor for hash map for our use only. Everything else should use the
+// normal interface.
+hash_map_t *hash_map_new_internal(
+    size_t size,
+    hash_index_fn hash_fn,
+    key_free_fn key_fn,
+    data_free_fn,
+    const allocator_t *zeroed_allocator);
+
 static bool allocation_entry_freed_checker(hash_map_entry_t *entry, void *context);
+static void *untracked_calloc(size_t size);
+
+static const allocator_t untracked_calloc_allocator = {
+  untracked_calloc,
+  free
+};
 
 static hash_map_t *allocations;
 static pthread_mutex_t lock;
@@ -42,8 +59,13 @@ void allocation_tracker_init(void) {
   if (allocations)
     return;
 
-  allocations = hash_map_new(ALLOCATION_HASH_MAP_SIZE, hash_function_knuth, NULL, free);
   pthread_mutex_init(&lock, NULL);
+  allocations = hash_map_new_internal(
+    ALLOCATION_HASH_MAP_SIZE,
+    hash_function_knuth,
+    NULL,
+    free,
+    &untracked_calloc_allocator);
 }
 
 void allocation_tracker_reset(void) {
@@ -111,3 +133,7 @@ static bool allocation_entry_freed_checker(hash_map_entry_t *entry, void *contex
 
   return true;
 }
+
+static void *untracked_calloc(size_t size) {
+  return calloc(size, 1);
+}
index c3d5a51..83a567d 100644 (file)
@@ -20,6 +20,8 @@
 #include <list.h>
 #include <hash_map.h>
 
+#include "allocator.h"
+
 struct hash_map_t;
 
 typedef struct hash_map_bucket_t {
@@ -33,50 +35,70 @@ typedef struct hash_map_t {
   hash_index_fn hash_fn;
   key_free_fn key_fn;
   data_free_fn data_fn;
+  const allocator_t *allocator;
 } hash_map_t;
 
+// Hidden constructor for list, only to be used by us.
+list_t *list_new_internal(list_free_cb callback, const allocator_t *zeroed_allocator);
+
 static void bucket_free_(void *data);
 static hash_map_entry_t *find_bucket_entry_(list_t *hash_bucket_list,
     const void *key);
 
-// Returns a new, empty hash_map. Returns NULL if not enough memory could be allocated
-// for the hash_map structure. The returned hash_map must be freed with |hash_map_free|.
-// The |num_bucket| specifies the number of hashable buckets for the map and must not
-// be zero.  The |hash_fn| specifies a hash function to be used and must not be NULL.
-// The |key_fn| and |data_fn| are called whenever a hash_map element is removed from
-// the hash_map. They can be used to release resources held by the hash_map element,
-// e.g.  memory or file descriptor.  |key_fn| and |data_fn| may be NULL if no cleanup
-// is necessary on element removal.
-hash_map_t * hash_map_new(size_t num_bucket, hash_index_fn hash_fn,
-    key_free_fn key_fn, data_free_fn data_fn) {
+// Hidden constructor, only to be used by the allocation tracker. Behaves the same as
+// |hash_map_new|, except you get to specify the allocator.
+hash_map_t * hash_map_new_internal(
+    size_t num_bucket,
+    hash_index_fn hash_fn,
+    key_free_fn key_fn,
+    data_free_fn data_fn,
+    const allocator_t *zeroed_allocator) {
   assert(hash_fn != NULL);
   assert(num_bucket > 0);
+  assert(zeroed_allocator != NULL);
 
-  hash_map_t *hash_map = calloc(sizeof(hash_map_t), 1);
+  hash_map_t *hash_map = zeroed_allocator->alloc(sizeof(hash_map_t));
   if (hash_map == NULL)
     return NULL;
 
   hash_map->hash_fn = hash_fn;
   hash_map->key_fn = key_fn;
   hash_map->data_fn = data_fn;
+  hash_map->allocator = zeroed_allocator;
 
   hash_map->num_bucket = num_bucket;
-  hash_map->bucket = calloc(sizeof(hash_map_bucket_t), num_bucket);
+  hash_map->bucket = zeroed_allocator->alloc(sizeof(hash_map_bucket_t) * num_bucket);
   if (hash_map->bucket == NULL) {
-    free(hash_map);
+    zeroed_allocator->free(hash_map);
     return NULL;
   }
   return hash_map;
 }
 
+// Returns a new, empty hash_map. Returns NULL if not enough memory could be allocated
+// for the hash_map structure. The returned hash_map must be freed with |hash_map_free|.
+// The |num_bucket| specifies the number of hashable buckets for the map and must not
+// be zero.  The |hash_fn| specifies a hash function to be used and must not be NULL.
+// The |key_fn| and |data_fn| are called whenever a hash_map element is removed from
+// the hash_map. They can be used to release resources held by the hash_map element,
+// e.g.  memory or file descriptor.  |key_fn| and |data_fn| may be NULL if no cleanup
+// is necessary on element removal.
+hash_map_t * hash_map_new(
+    size_t num_bucket,
+    hash_index_fn hash_fn,
+    key_free_fn key_fn,
+    data_free_fn data_fn) {
+  return hash_map_new_internal(num_bucket, hash_fn, key_fn, data_fn, &allocator_calloc);
+}
+
 // Frees the hash_map. This function accepts NULL as an argument, in which case it
 // behaves like a no-op.
 void hash_map_free(hash_map_t *hash_map) {
   if (hash_map == NULL)
     return;
   hash_map_clear(hash_map);
-  free(hash_map->bucket);
-  free(hash_map);
+  hash_map->allocator->free(hash_map->bucket);
+  hash_map->allocator->free(hash_map);
 }
 
 // Returns true if the hash_map is empty (has no elements), false otherwise.
@@ -125,7 +147,7 @@ bool hash_map_set(hash_map_t *hash_map, const void *key, void *data) {
   hash_index_t hash_key = hash_map->hash_fn(key) % hash_map->num_bucket;
 
   if (hash_map->bucket[hash_key].list == NULL) {
-    hash_map->bucket[hash_key].list = list_new(bucket_free_);
+    hash_map->bucket[hash_key].list = list_new_internal(bucket_free_, hash_map->allocator);
     if (hash_map->bucket[hash_key].list == NULL)
         return false;
   }
@@ -140,7 +162,7 @@ bool hash_map_set(hash_map_t *hash_map, const void *key, void *data) {
   } else {
     hash_map->hash_size++;
   }
-  hash_map_entry = calloc(sizeof(hash_map_entry_t), 1);
+  hash_map_entry = hash_map->allocator->alloc(sizeof(hash_map_entry_t));
   if (hash_map_entry == NULL)
     return false;
 
@@ -224,12 +246,13 @@ void hash_map_foreach(hash_map_t *hash_map, hash_map_iter_cb callback, void *con
 static void bucket_free_(void *data) {
   assert(data != NULL);
   hash_map_entry_t *hash_map_entry = (hash_map_entry_t *)data;
+  const hash_map_t *hash_map = hash_map_entry->hash_map;
 
-  if (hash_map_entry->hash_map->key_fn)
-    hash_map_entry->hash_map->key_fn((void *)hash_map_entry->key);
-  if (hash_map_entry->hash_map->data_fn)
-    hash_map_entry->hash_map->data_fn(hash_map_entry->data);
-  free(hash_map_entry);
+  if (hash_map->key_fn)
+    hash_map->key_fn((void *)hash_map_entry->key);
+  if (hash_map->data_fn)
+    hash_map->data_fn(hash_map_entry->data);
+  hash_map->allocator->free(hash_map_entry);
 }
 
 static hash_map_entry_t * find_bucket_entry_(list_t *hash_bucket_list,
index c65720d..3f1b379 100644 (file)
@@ -1,5 +1,6 @@
 #include <assert.h>
 
+#include "allocator.h"
 #include "list.h"
 #include "osi.h"
 
@@ -13,10 +14,23 @@ typedef struct list_t {
   list_node_t *tail;
   size_t length;
   list_free_cb free_cb;
+  const allocator_t *allocator;
 } list_t;
 
 static list_node_t *list_free_node_(list_t *list, list_node_t *node);
 
+// Hidden constructor, only to be used by the hash map for the allocation tracker.
+// Behaves the same as |list_new|, except you get to specify the allocator.
+list_t *list_new_internal(list_free_cb callback, const allocator_t *zeroed_allocator) {
+  list_t *list = (list_t *)zeroed_allocator->alloc(sizeof(list_t));
+  if (!list)
+    return NULL;
+
+  list->free_cb = callback;
+  list->allocator = zeroed_allocator;
+  return list;
+}
+
 // Returns a new, empty list. Returns NULL if not enough memory could be allocated
 // for the list structure. The returned list must be freed with |list_free|. The
 // |callback| specifies a function to be called whenever a list element is removed
@@ -24,19 +38,17 @@ static list_node_t *list_free_node_(list_t *list, list_node_t *node);
 // memory or file descriptor. |callback| may be NULL if no cleanup is necessary on
 // element removal.
 list_t *list_new(list_free_cb callback) {
-  list_t *list = (list_t *)calloc(sizeof(list_t), 1);
-  if (list)
-    list->free_cb = callback;
-  return list;
+  return list_new_internal(callback, &allocator_calloc);
 }
 
 // Frees the list. This function accepts NULL as an argument, in which case it
 // behaves like a no-op.
 void list_free(list_t *list) {
-  if (list != NULL)
-    list_clear(list);
+  if (!list)
+    return;
 
-  free(list);
+  list_clear(list);
+  list->allocator->free(list);
 }
 
 // Returns true if the list is empty (has no elements), false otherwise.
@@ -88,7 +100,7 @@ bool list_insert_after(list_t *list, list_node_t *prev_node, void *data) {
   assert(node != NULL);
   assert(data != NULL);
 
-  list_node_t *node = (list_node_t *)malloc(sizeof(list_node_t));
+  list_node_t *node = (list_node_t *)list->allocator->alloc(sizeof(list_node_t));
   if (!node)
     return false;
 
@@ -109,7 +121,7 @@ bool list_prepend(list_t *list, void *data) {
   assert(list != NULL);
   assert(data != NULL);
 
-  list_node_t *node = (list_node_t *)malloc(sizeof(list_node_t));
+  list_node_t *node = (list_node_t *)list->allocator->alloc(sizeof(list_node_t));
   if (!node)
     return false;
   node->next = list->head;
@@ -129,7 +141,7 @@ bool list_append(list_t *list, void *data) {
   assert(list != NULL);
   assert(data != NULL);
 
-  list_node_t *node = (list_node_t *)malloc(sizeof(list_node_t));
+  list_node_t *node = (list_node_t *)list->allocator->alloc(sizeof(list_node_t));
   if (!node)
     return false;
   node->next = NULL;
@@ -244,7 +256,7 @@ static list_node_t *list_free_node_(list_t *list, list_node_t *node) {
 
   if (list->free_cb)
     list->free_cb(node->data);
-  free(node);
+  list->allocator->free(node);
   --list->length;
 
   return next;