hash.hpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. #ifndef __XRCU_TESTS_HASH__
  2. #define __XRCU_TESTS_HASH__ 1
  3. #include "../hash_table.hpp"
  4. #include "utils.hpp"
  5. #include <thread>
  6. #include <algorithm>
  7. #include <cstdio>
  8. typedef xrcu::hash_table<int, std::string> table_t;
  9. namespace ht_test
  10. {
  11. static std::string&
  12. mutate (std::string& in, const char *s)
  13. {
  14. in += s;
  15. return (in);
  16. }
  17. static std::string
  18. mknew (std::string& in, const char *s)
  19. {
  20. return (in + s);
  21. }
  22. void test_single_threaded ()
  23. {
  24. table_t tx { { -1, "abc" }, { -2, "def" }, { -3, "ghi" } };
  25. ASSERT (tx.size () == 3);
  26. ASSERT (!tx.empty ());
  27. ASSERT (tx.find (-2, std::string ("")) == "def");
  28. for (int i = 0; i < 4000; ++i)
  29. tx.insert (i, mkstr (i));
  30. for (int i = -3; i < 4000; ++i)
  31. ASSERT (tx.contains (i));
  32. tx.update (101, mutate, "!!!");
  33. ASSERT (tx.find(101, std::string ("")).find ("!!!") != std::string::npos);
  34. tx.update (2002, mknew, "!!!");
  35. ASSERT (tx.find(2002, std::string ("")).find ("!!!") != std::string::npos);
  36. auto old_size = tx.size ();
  37. int i;
  38. for (i = 0; i < 1000; i += 2)
  39. ASSERT (tx.erase (i));
  40. ASSERT (tx.size () == old_size - i / 2);
  41. auto prev = tx.remove (101);
  42. ASSERT (prev.has_value ());
  43. i = 0;
  44. for (const auto& q : tx)
  45. {
  46. ASSERT (q.second.size () > 0);
  47. ++i;
  48. }
  49. ASSERT ((size_t)i == tx.size ());
  50. auto old = tx;
  51. tx.clear ();
  52. ASSERT (tx.size () == 0);
  53. ASSERT (old.size () != 0);
  54. tx.swap (old);
  55. ASSERT (old.size () == 0);
  56. ASSERT (tx.size () != 0);
  57. ASSERT (!xrcu::in_cs ());
  58. }
  59. static void
  60. mt_inserter (table_t *tx, int index)
  61. {
  62. for (int i = 0; i < INSERTER_LOOPS; ++i)
  63. {
  64. int key = index * INSERTER_LOOPS + i;
  65. ASSERT (tx->insert (key, mkstr (key)));
  66. }
  67. }
  68. static bool
  69. ht_consistent (table_t& tx)
  70. {
  71. if (tx.empty ())
  72. return (true);
  73. for (auto p : tx)
  74. if (mkstr (p.first) != p.second)
  75. return (false);
  76. return (true);
  77. }
  78. void test_insert_mt ()
  79. {
  80. table_t tx;
  81. std::vector<std::thread> thrs;
  82. for (int i = 0; i < INSERTER_THREADS; ++i)
  83. thrs.push_back (std::thread (mt_inserter, &tx, i));
  84. for (auto& thr: thrs)
  85. thr.join ();
  86. ASSERT (tx.size () == INSERTER_THREADS * INSERTER_LOOPS);
  87. ASSERT (ht_consistent (tx));
  88. }
  89. static void
  90. mt_inserter_ov (table_t *tx, int index)
  91. {
  92. for (int i = 0; i < INSERTER_LOOPS; ++i)
  93. {
  94. int key = index * (INSERTER_LOOPS / 2) + i;
  95. tx->insert (key, mkstr (key));
  96. }
  97. }
  98. void test_insert_mt_ov ()
  99. {
  100. table_t tx;
  101. std::vector<std::thread> thrs;
  102. for (int i = 0; i < INSERTER_THREADS; ++i)
  103. thrs.push_back (std::thread (mt_inserter_ov, &tx, i));
  104. for (auto& thr : thrs)
  105. thr.join ();
  106. ASSERT (tx.size () == (INSERTER_THREADS + 1) * INSERTER_LOOPS / 2);
  107. ASSERT (ht_consistent (tx));
  108. }
  109. static void
  110. mt_eraser (table_t *tx, int index)
  111. {
  112. for (int i = 0; i < ERASER_LOOPS; ++i)
  113. {
  114. int key = index * ERASER_LOOPS + i;
  115. auto prev = tx->remove (key);
  116. ASSERT (prev.has_value ());
  117. ASSERT ((*prev)[0] == '-');
  118. }
  119. }
  120. static void
  121. fill_table_for_erase (table_t& tx)
  122. {
  123. for (int i = 0; i < ERASER_THREADS * ERASER_LOOPS; ++i)
  124. tx.insert (i, mkstr (-i - 1));
  125. }
  126. void test_erase_mt ()
  127. {
  128. table_t tx;
  129. fill_table_for_erase (tx);
  130. std::vector<std::thread> thrs;
  131. for (int i = 0; i < ERASER_THREADS; ++i)
  132. thrs.push_back (std::thread (mt_eraser, &tx, i));
  133. for (auto& thr : thrs)
  134. thr.join ();
  135. ASSERT (tx.empty ());
  136. }
  137. static void
  138. mt_eraser_ov (table_t *tx, int index)
  139. {
  140. for (int i = 0; i < ERASER_LOOPS; ++i)
  141. {
  142. int key = index * (ERASER_LOOPS / 2) + i;
  143. tx->erase (key);
  144. }
  145. }
  146. void test_erase_mt_ov ()
  147. {
  148. table_t tx;
  149. fill_table_for_erase (tx);
  150. std::vector<std::thread> thrs;
  151. for (int i = 0; i < ERASER_THREADS; ++i)
  152. thrs.push_back (std::thread (mt_eraser_ov, &tx, i));
  153. for (auto& thr : thrs)
  154. thr.join ();
  155. ASSERT (tx.size () == (ERASER_THREADS - 1) * ERASER_LOOPS / 2);
  156. }
  157. static void
  158. mt_mutator (table_t *tx)
  159. {
  160. int keys[MUTATOR_KEY_SIZE];
  161. for (int i = 0; i < MUTATOR_KEY_SIZE; ++i)
  162. keys[i] = i;
  163. std::random_shuffle (keys, keys + MUTATOR_KEY_SIZE);
  164. for (int i = 0; i < MUTATOR_KEY_SIZE; ++i)
  165. if (!tx->insert (keys[i], std::string ("")))
  166. tx->erase (keys[i]);
  167. }
  168. void test_mutate_mt ()
  169. {
  170. table_t tx;
  171. std::vector<std::thread> thrs;
  172. for (int i = 0; i < MUTATOR_THREADS; ++i)
  173. thrs.push_back (std::thread (mt_mutator, &tx));
  174. for (auto& thr : thrs)
  175. thr.join ();
  176. for (auto q : tx)
  177. ASSERT (q.first <= MUTATOR_KEY_SIZE && q.second.empty ());
  178. }
  179. void test_iter ()
  180. {
  181. table_t tx;
  182. for (int i = 0; i < 5; ++i)
  183. tx.insert (i, mkstr (i));
  184. auto it = tx.begin ();
  185. for (int i = 10; i < 1000; ++i)
  186. tx.insert (i, mkstr (i));
  187. int c = 0;
  188. for (; it != tx.end (); ++it, ++c)
  189. ;
  190. ASSERT (c >= 5);
  191. }
  192. test_module hash_table_tests
  193. {
  194. "hash table",
  195. {
  196. { "API in a single thread", test_single_threaded },
  197. { "multi threaded insertions", test_insert_mt },
  198. { "multi threaded overlapped insertions", test_insert_mt_ov },
  199. { "multi threaded erasures", test_erase_mt },
  200. { "multi threaded overlapped erasures", test_erase_mt_ov },
  201. { "multi threaded mutations", test_mutate_mt },
  202. { "iteration during modifications", test_iter }
  203. }
  204. };
  205. } // namespace ht_test
  206. #endif