@@ -72,6 +72,84 @@ test_write_iterate(nvobj::pool<root> &pop,
7272 UT_ASSERTeq (num_allocs (pop), 0 );
7373}
7474
75+ /* Insert INITIAL_ELEMENTS elements to the radix. After that, concurrently
76+ * erase elements with even keys and iterate through the entire container to
77+ * count elements with odd keys. */
78+ void
79+ test_erase_iterate (nvobj::pool<root> &pop,
80+ nvobj::persistent_ptr<container_int_int> &ptr)
81+ {
82+ const size_t value_repeats = 1000 ;
83+ size_t threads = 4 ;
84+ if (On_drd)
85+ threads = 2 ;
86+
87+ /* check how many allocs will remain after removing elements with even
88+ * keys */
89+ init_container (pop, ptr, INITIAL_ELEMENTS, value_repeats);
90+ for (size_t i = 0 ; i < INITIAL_ELEMENTS; i += 2 ) {
91+ ptr->erase (key<container_int_int>(i));
92+ }
93+ auto expected_allocs = num_allocs (pop);
94+ nvobj::transaction::run (
95+ pop, [&] { nvobj::delete_persistent<container_int_int>(ptr); });
96+
97+ init_container (pop, ptr, INITIAL_ELEMENTS, value_repeats);
98+ ptr->runtime_initialize_mt ();
99+
100+ /* force 3 gc cycles to ensure that all garbage vectors will be
101+ * allocated */
102+ for (size_t i = 0 ; i < 3 ; ++i) {
103+ ptr->erase (key<container_int_int>(i));
104+ ptr->garbage_collect ();
105+ ptr->emplace (key<container_int_int>(i),
106+ value<container_int_int>(i));
107+ }
108+
109+ auto writer_f = [&] {
110+ for (size_t i = 0 ; i < INITIAL_ELEMENTS; i += 2 ) {
111+ ptr->erase (key<container_int_int>(i));
112+ ptr->garbage_collect ();
113+ }
114+ };
115+
116+ auto readers_f = std::vector<std::function<void ()>>{
117+ [&] {
118+ auto w = ptr->register_worker ();
119+
120+ size_t cnt = 0 ;
121+ w.critical ([&] {
122+ for (auto it = ptr->begin (); it != ptr->end ();
123+ ++it) {
124+ if (it->key () % 2 ) {
125+ ++cnt;
126+ }
127+ }
128+ });
129+ UT_ASSERTeq (cnt, INITIAL_ELEMENTS / 2 );
130+ },
131+ };
132+
133+ parallel_modify_read (writer_f, readers_f, threads);
134+
135+ UT_ASSERTeq (ptr->size (), INITIAL_ELEMENTS / 2 );
136+
137+ ptr->garbage_collect_force ();
138+
139+ /* num allocs == expected_allocs + 3 garbage vectors */
140+ UT_ASSERTeq (num_allocs (pop), expected_allocs + 3 );
141+
142+ ptr->runtime_finalize_mt ();
143+
144+ nvobj::transaction::run (
145+ pop, [&] { nvobj::delete_persistent<container_int_int>(ptr); });
146+
147+ UT_ASSERTeq (num_allocs (pop), 0 );
148+ }
149+
150+ /* Insert INITIAL_ELEMENTS/2 elements to the radix. After that concurrently
151+ * write new elements from the writer thread and do lower_bound/upper_bound
152+ * from the other threads. */
75153void
76154test_write_upper_lower_bounds (nvobj::pool<root> &pop,
77155 nvobj::persistent_ptr<container_int_int> &ptr)
@@ -379,6 +457,77 @@ test_write_erase_upper_lower_bounds_split(
379457 UT_ASSERTeq (num_allocs (pop), 0 );
380458}
381459
460+ /* Insert INITIAL_ELEMENTS elements to the radix. After that concurrently
461+ * erase elements in one thread and do lower_bound/upper_bound
462+ * in the other threads. */
463+ void
464+ test_erase_upper_lower_bounds (nvobj::pool<root> &pop,
465+ nvobj::persistent_ptr<container_int_int> &ptr)
466+ {
467+ const size_t value_repeats = 10 ;
468+ size_t threads = 4 ;
469+ if (On_drd)
470+ threads = 2 ;
471+ const size_t batch_size = INITIAL_ELEMENTS / threads;
472+
473+ init_container (pop, ptr, INITIAL_ELEMENTS, value_repeats);
474+ ptr->runtime_initialize_mt ();
475+
476+ auto writer = [&]() {
477+ for (size_t i = 0 ; i < INITIAL_ELEMENTS; i += 2 ) {
478+ ptr->erase (key<container_int_int>(i));
479+ }
480+ };
481+
482+ std::atomic<size_t > reader_id;
483+ reader_id.store (0 );
484+ auto readers = std::vector<std::function<void ()>>{
485+ [&]() {
486+ auto id = reader_id++;
487+ for (size_t i = id * batch_size;
488+ i < (id + 1 ) * batch_size; ++i) {
489+ std::vector<unsigned int > keys;
490+ auto it = ptr->lower_bound (i);
491+ while (it != ptr->end ()) {
492+ keys.push_back (it->key ());
493+ ++it;
494+ }
495+
496+ for (auto &k : keys) {
497+ UT_ASSERT (k >=
498+ key<container_int_int>(i));
499+ }
500+ }
501+ },
502+ [&]() {
503+ auto id = reader_id++;
504+ for (size_t i = id * batch_size;
505+ i < (id + 1 ) * batch_size; ++i) {
506+ std::vector<unsigned int > keys;
507+ auto it = ptr->upper_bound (i);
508+ while (it != ptr->end ()) {
509+ keys.push_back (it->key ());
510+ ++it;
511+ }
512+
513+ for (auto &k : keys) {
514+ UT_ASSERT (k >
515+ key<container_int_int>(i));
516+ }
517+ }
518+ },
519+ };
520+
521+ parallel_modify_read (writer, readers, threads);
522+
523+ ptr->runtime_finalize_mt ();
524+
525+ nvobj::transaction::run (
526+ pop, [&] { nvobj::delete_persistent<container_int_int>(ptr); });
527+
528+ UT_ASSERTeq (num_allocs (pop), 0 );
529+ }
530+
382531static void
383532test (int argc, char *argv[])
384533{
@@ -407,6 +556,7 @@ test(int argc, char *argv[])
407556 generator = std::mt19937_64 (seed);
408557
409558 test_write_iterate (pop, pop.root ()->radix_int_int );
559+ test_erase_iterate (pop, pop.root ()->radix_int_int );
410560 test_write_upper_lower_bounds (pop, pop.root ()->radix_int_int );
411561 test_erase_upper_lower_bounds_neighbours (pop, pop.root ()->radix_str );
412562 test_write_erase_upper_lower_bounds_split (pop, pop.root ()->radix_str );
0 commit comments