Research portable Memory game | Исследовать портируемую игру Память
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

246 lines
7.2KB

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