1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
|
#include <stdio.h>
#define HASH_NONFATAL_OOM 1
#include "uthash.h"
#undef uthash_malloc
#undef uthash_free
#undef uthash_nonfatal_oom
#define uthash_malloc(sz) alt_malloc(sz)
#define uthash_free(ptr,sz) alt_free(ptr)
#define uthash_nonfatal_oom(e) do{(e)->mem_failed=1;}while(0)
#define all_select(a) 1
typedef struct example_user_t {
int id;
int cookie;
UT_hash_handle hh;
UT_hash_handle hh2;
int mem_failed;
} example_user_t;
static int malloc_cnt = 0;
static int malloc_failed = 0;
static int free_cnt = 0;
static void *alt_malloc(size_t sz)
{
if (--malloc_cnt <= 0) {
malloc_failed = 1;
return 0;
}
malloc_failed = 0;
return malloc(sz);
}
static void alt_free(void *ptr) {
free_cnt++;
free(ptr);
}
static void complain(int index, example_user_t *users, example_user_t *user)
{
int expected_frees = (3 - index);
if (users) {
printf("%d: users hash must be empty\n", index);
}
if (user->hh.tbl) {
printf("%d hash table must be empty\n", index);
}
if (free_cnt != expected_frees) {
printf("%d Expected %d frees, only had %d\n", index, expected_frees, free_cnt);
}
if (user->mem_failed != 1) {
printf("%d Expected user->mem_failed(%d) to be 1\n", index, user->mem_failed);
}
}
int main()
{
example_user_t *users = NULL;
example_user_t *user = (example_user_t*)malloc(sizeof(example_user_t));
example_user_t *test;
example_user_t *users2 = NULL;
int id = 0;
int i;
int saved_cnt;
user->id = id;
#ifdef HASH_BLOOM
malloc_cnt = 3; // bloom filter must fail
user->mem_failed = 0;
user->hh.tbl = (UT_hash_table*)1;
free_cnt = 0;
HASH_ADD_INT(users, id, user);
complain(1, users, user);
#endif /* HASH_BLOOM */
malloc_cnt = 2; // bucket creation must fail
user->mem_failed = 0;
free_cnt = 0;
user->hh.tbl = (UT_hash_table*)1;
HASH_ADD_INT(users, id, user);
complain(2, users, user);
malloc_cnt = 1; // table creation must fail
user->mem_failed = 0;
free_cnt = 0;
user->hh.tbl = (UT_hash_table*)1;
HASH_ADD_INT(users, id, user);
complain(3, users, user);
malloc_cnt = 4; // hash must create OK
user->mem_failed = 0;
HASH_ADD_INT(users, id, user);
if (user->mem_failed) {
printf("mem_failed must be 0, not %d\n", user->mem_failed);
}
HASH_FIND_INT(users,&id,test);
if (!test) {
printf("test user ID %d not found\n", id);
}
if (HASH_COUNT(users) != 1) {
printf("Got HASH_COUNT(users)=%d, should be 1\n", HASH_COUNT(users));
}
// let's add users until expansion fails.
malloc_failed = 0;
free_cnt = 0;
malloc_cnt = 1;
for (id = 1; 1; ++id) {
user = (example_user_t*)malloc(sizeof(example_user_t));
user->id = id;
if (id >= 1000) {
// prevent infinite, or too long of a loop here
puts("too many allocs before memory request");
break;
}
user->hh.tbl = (UT_hash_table*)1;
HASH_ADD_INT(users, id, user);
if (malloc_failed) {
if (id < 10) {
puts("there is no way your bucket size is <= 10");
}
if (user->hh.tbl) {
puts("user->hh.tbl should be NULL after failure");
} else if (user->mem_failed != 1) {
printf("mem_failed should be 1 after failure, not %d\n", user->mem_failed);
}
if (free_cnt != 0) {
printf("Expected 0 frees, had %d\n", free_cnt);
}
// let's make sure all previous IDs are there.
for (i=0; i<id; ++i) {
HASH_FIND_INT(users,&i,test);
if (test == NULL) {
printf("test user ID %d not found\n", i);
}
}
// let's try to add again, but with mem_failed set to 0
user->hh.tbl = NULL;
user->mem_failed = 0;
malloc_failed = 0;
HASH_ADD_INT(users, id, user);
if (!malloc_failed) {
puts("malloc should have been attempted");
}
if (user->hh.tbl) {
puts("user->hh.tbl should be NULL after second failure");
} else if (user->mem_failed != 1) {
printf("mem_failed should be 1 after second failure, not %d\n", user->mem_failed);
}
break;
}
}
// let's test HASH_SELECT.
// let's double the size of the table we've already built.
saved_cnt = id;
if (HASH_COUNT(users) != (unsigned)saved_cnt) {
printf("Got HASH_COUNT(users)=%d, should be %d\n", HASH_COUNT(users), saved_cnt);
}
for (i=0; i < saved_cnt; i++) {
user = (example_user_t*)malloc(sizeof(example_user_t));
user->id = ++id;
malloc_cnt = 20; // don't fail
HASH_ADD_INT(users, id, user);
}
HASH_ITER(hh, users, user, test) {
user->mem_failed = 0;
}
// HASH_SELECT calls uthash_nonfatal_oom() with an argument of type (void*).
#undef uthash_nonfatal_oom
#define uthash_nonfatal_oom(e) do{((example_user_t*)e)->mem_failed=1;}while(0)
malloc_cnt = 0;
free_cnt = 0;
HASH_SELECT(hh2, users2, hh, users, all_select);
if (users2) {
puts("Nothing should have been copied into users2");
}
HASH_ITER(hh, users, user, test) {
if (user->hh2.tbl) {
printf("User ID %d has tbl at %p\n", user->id, (void*)user->hh2.tbl);
}
if (user->mem_failed != 1) {
printf("User ID %d has mem_failed(%d), should be 1\n", user->id, user->mem_failed);
}
user->mem_failed = 0;
}
malloc_cnt = 4;
HASH_SELECT(hh2, users2, hh, users, all_select);
// note about the above.
// we tried to stick up to 1,000 entries into users,
// and the malloc failed after saved_cnt. The bucket threshold must have
// been triggered. We then doubled the amount of entries in user,
// and just ran HASH_SELECT, trying to copy them into users2.
// because the order is different, and because we continue after
// failures, the bucket threshold may get triggered on arbitrary
// elements, depending on the hash function.
saved_cnt = 0;
HASH_ITER(hh, users, user, test) {
example_user_t * user2;
HASH_FIND(hh2, users2, &user->id, sizeof(int), user2);
if (user2) {
if (!user->hh2.tbl) {
printf("User ID %d has tbl==NULL\n", user->id);
}
if (user->mem_failed != 0) {
printf("User ID %d has mem_failed(%d), expected 0\n", user->id, user->mem_failed);
}
} else {
saved_cnt++;
if (user->hh2.tbl) {
printf("User ID %d has tbl at %p, expected 0\n", user->id, (void*)user->hh2.tbl);
}
if (user->mem_failed != 1) {
printf("User ID %d has mem_failed(%d), expected is 1\n", user->id, user->mem_failed);
}
}
}
if (saved_cnt + HASH_CNT(hh2, users2) != HASH_COUNT(users)) {
printf("Selected elements : %d + %d != %d\n",
saved_cnt, HASH_CNT(hh2, users2), HASH_COUNT(users));
}
puts("End");
return 0;
}
|