1// Copyright (C) 2023 Christian Mazakas
2// Copyright (C) 2023 Joaquin M Lopez Munoz
3// Distributed under the Boost Software License, Version 1.0. (See accompanying
4// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5
6#include "exception_helpers.hpp"
7
8#include <boost/unordered/concurrent_flat_map.hpp>
9#include <boost/unordered/concurrent_flat_set.hpp>
10
11#include <boost/core/ignore_unused.hpp>
12
13namespace {
14 test::seed_t initialize_seed(73987);
15
16 struct lvalue_inserter_type
17 {
18 template <class T, class X> void operator()(std::vector<T>& values, X& x)
19 {
20 enable_exceptions();
21
22 std::atomic<std::uint64_t> num_inserts{0};
23 thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
24 for (auto const& r : s) {
25 try {
26 bool b = x.insert(r);
27 if (b) {
28 ++num_inserts;
29 }
30 } catch (...) {
31 }
32 }
33 });
34
35 disable_exceptions();
36
37 BOOST_TEST_EQ(raii::copy_assignment, 0u);
38 BOOST_TEST_EQ(raii::move_assignment, 0u);
39 }
40 } lvalue_inserter;
41
42 struct norehash_lvalue_inserter_type : public lvalue_inserter_type
43 {
44 template <class T, class X> void operator()(std::vector<T>& values, X& x)
45 {
46 x.reserve(values.size());
47 lvalue_inserter_type::operator()(values, x);
48 BOOST_TEST_GT(raii::copy_constructor, 0u);
49 BOOST_TEST_EQ(raii::move_constructor, 0u);
50 }
51 } norehash_lvalue_inserter;
52
53 struct rvalue_inserter_type
54 {
55 template <class T, class X> void operator()(std::vector<T>& values, X& x)
56 {
57 BOOST_TEST_EQ(raii::copy_constructor, 0u);
58
59 enable_exceptions();
60
61 std::atomic<std::uint64_t> num_inserts{0};
62 thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
63 for (auto& r : s) {
64 try {
65 bool b = x.insert(std::move(r));
66 if (b) {
67 ++num_inserts;
68 }
69 } catch (...) {
70 }
71 }
72 });
73
74 disable_exceptions();
75
76 if (!std::is_same<T, typename X::value_type>::value) {
77 BOOST_TEST_EQ(raii::copy_constructor, 0u);
78 }
79
80 BOOST_TEST_EQ(raii::copy_assignment, 0u);
81 BOOST_TEST_EQ(raii::move_assignment, 0u);
82 }
83 } rvalue_inserter;
84
85 struct norehash_rvalue_inserter_type : public rvalue_inserter_type
86 {
87 template <class T, class X> void operator()(std::vector<T>& values, X& x)
88 {
89 static constexpr auto value_type_cardinality =
90 value_cardinality<typename X::value_type>::value;
91
92 x.reserve(values.size());
93
94 BOOST_TEST_EQ(raii::copy_constructor, 0u);
95 BOOST_TEST_EQ(raii::move_constructor, 0u);
96
97 rvalue_inserter_type::operator()(values, x);
98
99 if (std::is_same<T, typename X::value_type>::value) {
100 if (std::is_same<typename X::key_type,
101 typename X::value_type>::value) {
102 BOOST_TEST_EQ(raii::copy_constructor, 0u);
103 BOOST_TEST_EQ(raii::move_constructor, x.size());
104 }
105 else {
106 BOOST_TEST_EQ(raii::copy_constructor, x.size());
107 BOOST_TEST_EQ(raii::move_constructor, x.size());
108 }
109 } else {
110 BOOST_TEST_EQ(raii::copy_constructor, 0u);
111 BOOST_TEST_EQ(
112 raii::move_constructor, value_type_cardinality * x.size());
113 }
114 }
115 } norehash_rvalue_inserter;
116
117 struct iterator_range_inserter_type
118 {
119 template <class T, class X> void operator()(std::vector<T>& values, X& x)
120 {
121 for (std::size_t i = 0; i < 10; ++i) {
122 x.insert(values[i]);
123 }
124
125 enable_exceptions();
126 thread_runner(values, [&x](boost::span<T> s) {
127 try {
128 x.insert(s.begin(), s.end());
129 } catch (...) {
130 }
131 });
132 disable_exceptions();
133
134 BOOST_TEST_EQ(raii::copy_assignment, 0u);
135 BOOST_TEST_EQ(raii::move_assignment, 0u);
136 }
137 } iterator_range_inserter;
138
139 struct lvalue_insert_or_assign_copy_assign_type
140 {
141 template <class T, class X> void operator()(std::vector<T>& values, X& x)
142 {
143 enable_exceptions();
144 thread_runner(values, [&x](boost::span<T> s) {
145 for (auto& r : s) {
146 try {
147 x.insert_or_assign(r.first, r.second);
148 } catch (...) {
149 }
150 }
151 });
152 disable_exceptions();
153
154 BOOST_TEST_EQ(raii::default_constructor, 0u);
155 BOOST_TEST_GT(raii::copy_constructor, 0u);
156 BOOST_TEST_GT(raii::move_constructor, 0u);
157 BOOST_TEST_EQ(raii::move_assignment, 0u);
158 }
159 } lvalue_insert_or_assign_copy_assign;
160
161 struct lvalue_insert_or_assign_move_assign_type
162 {
163 template <class T, class X> void operator()(std::vector<T>& values, X& x)
164 {
165 enable_exceptions();
166 thread_runner(values, [&x](boost::span<T> s) {
167 for (auto& r : s) {
168 try {
169
170 x.insert_or_assign(r.first, std::move(r.second));
171 } catch (...) {
172 }
173 }
174 });
175 disable_exceptions();
176
177 BOOST_TEST_EQ(raii::default_constructor, 0u);
178 BOOST_TEST_GT(raii::copy_constructor, 0u);
179 BOOST_TEST_GT(raii::move_constructor, 0u);
180 BOOST_TEST_EQ(raii::copy_assignment, 0u);
181 }
182 } lvalue_insert_or_assign_move_assign;
183
184 struct rvalue_insert_or_assign_copy_assign_type
185 {
186 template <class T, class X> void operator()(std::vector<T>& values, X& x)
187 {
188 enable_exceptions();
189 thread_runner(values, [&x](boost::span<T> s) {
190 for (auto& r : s) {
191 try {
192 x.insert_or_assign(std::move(r.first), r.second);
193 } catch (...) {
194 }
195 }
196 });
197 disable_exceptions();
198
199 BOOST_TEST_EQ(raii::default_constructor, 0u);
200 BOOST_TEST_GT(raii::copy_constructor, 0u);
201 BOOST_TEST_GT(raii::move_constructor, x.size()); // rehashing
202 BOOST_TEST_EQ(raii::move_assignment, 0u);
203 }
204 } rvalue_insert_or_assign_copy_assign;
205
206 struct rvalue_insert_or_assign_move_assign_type
207 {
208 template <class T, class X> void operator()(std::vector<T>& values, X& x)
209 {
210 enable_exceptions();
211 thread_runner(values, [&x](boost::span<T> s) {
212 for (auto& r : s) {
213 try {
214 x.insert_or_assign(std::move(r.first), std::move(r.second));
215 } catch (...) {
216 }
217 }
218 });
219 disable_exceptions();
220
221 BOOST_TEST_EQ(raii::default_constructor, 0u);
222 BOOST_TEST_EQ(raii::copy_constructor, 0u);
223 BOOST_TEST_GT(raii::move_constructor, 0u);
224 BOOST_TEST_EQ(raii::copy_assignment, 0u);
225 }
226 } rvalue_insert_or_assign_move_assign;
227
228 struct lvalue_insert_or_cvisit_type
229 {
230 template <class T, class X> void operator()(std::vector<T>& values, X& x)
231 {
232 std::atomic<std::uint64_t> num_inserts{0};
233
234 enable_exceptions();
235 thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
236 for (auto& r : s) {
237 try {
238 bool b = x.insert_or_cvisit(
239 r, [](typename X::value_type const& v) { (void)v; });
240
241 if (b) {
242 ++num_inserts;
243 }
244 } catch (...) {
245 }
246 }
247 });
248 disable_exceptions();
249
250 BOOST_TEST_GT(num_inserts, 0u);
251 BOOST_TEST_EQ(raii::default_constructor, 0u);
252 // don't check move construction count here because of rehashing
253 BOOST_TEST_GT(raii::move_constructor, 0u);
254 BOOST_TEST_EQ(raii::move_assignment, 0u);
255 }
256 } lvalue_insert_or_cvisit;
257
258 struct lvalue_insert_or_visit_type
259 {
260 template <class T, class X> void operator()(std::vector<T>& values, X& x)
261 {
262 // concurrent_flat_set visit is always const access
263 using arg_type = typename std::conditional<
264 std::is_same<typename X::key_type, typename X::value_type>::value,
265 typename X::value_type const,
266 typename X::value_type
267 >::type;
268
269 std::atomic<std::uint64_t> num_inserts{0};
270
271 enable_exceptions();
272 thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
273 for (auto& r : s) {
274 try {
275 bool b =
276 x.insert_or_visit(r, [](arg_type& v) { (void)v; });
277
278 if (b) {
279 ++num_inserts;
280 }
281 } catch (...) {
282 }
283 }
284 });
285 disable_exceptions();
286
287 BOOST_TEST_GT(num_inserts, 0u);
288
289 BOOST_TEST_EQ(raii::default_constructor, 0u);
290
291 // don't check move construction count here because of rehashing
292 BOOST_TEST_GT(raii::move_constructor, 0u);
293 BOOST_TEST_EQ(raii::move_assignment, 0u);
294 }
295 } lvalue_insert_or_visit;
296
297 struct rvalue_insert_or_cvisit_type
298 {
299 template <class T, class X> void operator()(std::vector<T>& values, X& x)
300 {
301 std::atomic<std::uint64_t> num_inserts{0};
302
303 enable_exceptions();
304 thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
305 for (auto& r : s) {
306 try {
307 bool b = x.insert_or_cvisit(
308 std::move(r), [](typename X::value_type const& v) { (void)v; });
309
310 if (b) {
311 ++num_inserts;
312 }
313 } catch (...) {
314 }
315 }
316 });
317 disable_exceptions();
318
319 BOOST_TEST_GT(num_inserts, 0u);
320
321 BOOST_TEST_EQ(raii::default_constructor, 0u);
322 }
323 } rvalue_insert_or_cvisit;
324
325 struct rvalue_insert_or_visit_type
326 {
327 template <class T, class X> void operator()(std::vector<T>& values, X& x)
328 {
329 // concurrent_flat_set visit is always const access
330 using arg_type = typename std::conditional<
331 std::is_same<typename X::key_type, typename X::value_type>::value,
332 typename X::value_type const,
333 typename X::value_type
334 >::type;
335
336 std::atomic<std::uint64_t> num_inserts{0};
337
338 enable_exceptions();
339 thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
340 for (auto& r : s) {
341 try {
342 bool b = x.insert_or_visit(
343 std::move(r), [](arg_type& v) { (void)v; });
344
345 if (b) {
346 ++num_inserts;
347 }
348 } catch (...) {
349 }
350 }
351 });
352 disable_exceptions();
353
354 BOOST_TEST_GT(num_inserts, 0u);
355
356 BOOST_TEST_EQ(raii::default_constructor, 0u);
357 if (!std::is_same<T, typename X::value_type>::value) {
358 BOOST_TEST_EQ(raii::copy_constructor, 0u);
359 }
360 }
361 } rvalue_insert_or_visit;
362
363 struct iterator_range_insert_or_cvisit_type
364 {
365 template <class T, class X> void operator()(std::vector<T>& values, X& x)
366 {
367 for (std::size_t i = 0; i < 10; ++i) {
368 x.insert(values[i]);
369 }
370
371 enable_exceptions();
372 thread_runner(values, [&x](boost::span<T> s) {
373 try {
374 x.insert_or_cvisit(s.begin(), s.end(),
375 [](typename X::value_type const& v) { (void)v; });
376 } catch (...) {
377 }
378 });
379 disable_exceptions();
380
381 BOOST_TEST_EQ(raii::default_constructor, 0u);
382 }
383 } iterator_range_insert_or_cvisit;
384
385 struct iterator_range_insert_or_visit_type
386 {
387 template <class T, class X> void operator()(std::vector<T>& values, X& x)
388 {
389 for (std::size_t i = 0; i < 10; ++i) {
390 x.insert(values[i]);
391 }
392
393 enable_exceptions();
394 thread_runner(values, [&x](boost::span<T> s) {
395 try {
396 x.insert_or_visit(s.begin(), s.end(),
397 [](typename X::value_type const& v) { (void)v; });
398 } catch (...) {
399 }
400 });
401 disable_exceptions();
402
403 BOOST_TEST_EQ(raii::default_constructor, 0u);
404 }
405 } iterator_range_insert_or_visit;
406
407 template <class X, class GF, class F>
408 void insert(X*, GF gen_factory, F inserter, test::random_generator rg)
409 {
410 disable_exceptions();
411
412 auto gen = gen_factory.template get<X>();
413 auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
414 auto reference_cont = reference_container<X>(values.begin(), values.end());
415
416 raii::reset_counts();
417 {
418 X x;
419
420 inserter(values, x);
421
422 test_fuzzy_matches_reference(x, reference_cont, rg);
423 }
424 check_raii_counts();
425 }
426
427 boost::unordered::concurrent_flat_map<raii, raii, stateful_hash,
428 stateful_key_equal, stateful_allocator<std::pair<raii const, raii> > >* map;
429 boost::unordered::concurrent_flat_set<raii, stateful_hash,
430 stateful_key_equal, stateful_allocator<raii> >* set;
431
432} // namespace
433
434using test::default_generator;
435using test::limited_range;
436using test::sequential;
437
438// clang-format off
439UNORDERED_TEST(
440 insert,
441 ((map)(set))
442 ((exception_value_type_generator_factory)
443 (exception_init_type_generator_factory))
444 ((lvalue_inserter)(rvalue_inserter)(iterator_range_inserter)
445 (norehash_lvalue_inserter)(norehash_rvalue_inserter)
446 (lvalue_insert_or_cvisit)(lvalue_insert_or_visit)
447 (rvalue_insert_or_cvisit)(rvalue_insert_or_visit)
448 (iterator_range_insert_or_cvisit)(iterator_range_insert_or_visit))
449 ((default_generator)(sequential)(limited_range)))
450
451UNORDERED_TEST(
452 insert,
453 ((map))
454 ((exception_init_type_generator_factory))
455 ((lvalue_insert_or_assign_copy_assign)(lvalue_insert_or_assign_move_assign)
456 (rvalue_insert_or_assign_copy_assign)(rvalue_insert_or_assign_move_assign))
457 ((default_generator)(sequential)(limited_range)))
458
459// clang-format on
460
461RUN_TESTS()
462

source code of boost/libs/unordered/test/cfoa/exception_insert_tests.cpp