@@ -49,6 +49,12 @@ class string_hasher {
4949 return hash (str.c_str (), str.size ());
5050 }
5151
52+ size_t
53+ operator ()(const std::string &str) const
54+ {
55+ return hash (str.c_str (), str.size ());
56+ }
57+
5258private:
5359 size_t
5460 hash (const char *str, size_t size) const
@@ -70,7 +76,7 @@ struct root {
7076};
7177
7278/*
73- * insert_and_erase_test_str -- (internal) test insert and erase operations
79+ * insert_defrag_lookup -- test insert, erase and defrag operations
7480 * pmem::obj::concurrent_hash_map<pmem::obj::string, pmem::obj::string>
7581 */
7682void
@@ -159,6 +165,88 @@ insert_defrag_lookup_test(nvobj::pool<root> &pop)
159165 persistent_map_type::value_type>(ptr[i]);
160166 }
161167 });
168+
169+ map->clear ();
170+ }
171+
172+ /*
173+ * insert_defrag_concurrent -- test concurrently erase and defrag operations
174+ * pmem::obj::concurrent_hash_map<pmem::obj::string, pmem::obj::string>
175+ */
176+ void
177+ erase_defrag_concurrent_test (nvobj::pool<root> &pop, bool reversed_order,
178+ size_t erase_threads_n)
179+ {
180+ const ptrdiff_t BATCH_SIZE = 1000 ;
181+ const size_t NUMBER_ITEMS_ERASE = BATCH_SIZE * erase_threads_n;
182+ const size_t NUMBER_ITEMS_SAVE = 100 ;
183+
184+ auto map = pop.root ()->cons ;
185+
186+ UT_ASSERT (map != nullptr );
187+
188+ map->runtime_initialize ();
189+
190+ std::string str = " " ;
191+ for (size_t i = 0 ; i < NUMBER_ITEMS_ERASE + NUMBER_ITEMS_SAVE; i++) {
192+ map->insert_or_assign (str, str);
193+ str.append (std::to_string (i));
194+ }
195+
196+ std::vector<std::string> elements_to_erase;
197+ std::vector<std::string> elements_to_save;
198+
199+ size_t cnt = 0 ;
200+ for (auto &v : *map) {
201+ /* first NUMBER_ITEMS_SAVE elements wont be erased */
202+ if (cnt++ < NUMBER_ITEMS_SAVE)
203+ elements_to_save.push_back (
204+ std::string (v.first .c_str ()));
205+ else
206+ elements_to_erase.push_back (
207+ std::string (v.first .c_str ()));
208+ }
209+
210+ /* reverse order of elements_to_erase to test case when we are erasing
211+ * in order from last element to first */
212+ if (reversed_order)
213+ std::reverse (elements_to_erase.begin (),
214+ elements_to_erase.end ());
215+
216+ std::vector<std::thread> threads;
217+ for (ptrdiff_t i = 0 ; i < static_cast <ptrdiff_t >(erase_threads_n);
218+ i++) {
219+ threads.emplace_back ([&, i]() {
220+ auto start = std::next (elements_to_erase.begin (),
221+ i * BATCH_SIZE);
222+ auto end = std::next (elements_to_erase.begin (),
223+ (i + 1 ) * BATCH_SIZE);
224+ for (auto it = start; it != end; ++it) {
225+ UT_ASSERT (map->erase (*it));
226+ }
227+ });
228+ }
229+
230+ threads.emplace_back ([&]() { map->defragment (); });
231+
232+ for (auto &thread : threads)
233+ thread.join ();
234+
235+ UT_ASSERT (map->size () == NUMBER_ITEMS_SAVE);
236+
237+ for (size_t i = 0 ; i < NUMBER_ITEMS_SAVE; ++i) {
238+ persistent_map_type::accessor acc;
239+ bool res = map->find (acc, elements_to_save[i]);
240+
241+ if (res) {
242+ UT_ASSERT (acc->first == (elements_to_save[i]));
243+ UT_ASSERT (acc->second == (elements_to_save[i]));
244+ } else {
245+ UT_ASSERT (false );
246+ }
247+ }
248+
249+ map->clear ();
162250}
163251}
164252
@@ -185,6 +273,10 @@ test(int argc, char *argv[])
185273 }
186274
187275 insert_defrag_lookup_test (pop);
276+ erase_defrag_concurrent_test (pop, false , 1 );
277+ erase_defrag_concurrent_test (pop, true , 1 );
278+ erase_defrag_concurrent_test (pop, false , 10 );
279+ erase_defrag_concurrent_test (pop, true , 10 );
188280
189281 pop.close ();
190282}
0 commit comments