1 // Copyright (c) 2011 Helge Bahmann
2 // Copyright (c) 2017 - 2020 Andrey Semashev
3 //
4 // Distributed under the Boost Software License, Version 1.0.
5 // See accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt)
7
8 #ifndef BOOST_ATOMIC_API_TEST_HELPERS_HPP
9 #define BOOST_ATOMIC_API_TEST_HELPERS_HPP
10
11 #include <boost/atomic.hpp>
12 #include <cstddef>
13 #include <cstring>
14 #include <cstdlib>
15 #include <limits>
16 #include <vector>
17 #include <iostream>
18 #include <boost/config.hpp>
19 #include <boost/cstdint.hpp>
20 #include <boost/type.hpp>
21 #include <boost/core/enable_if.hpp>
22 #include <boost/type_traits/integral_constant.hpp>
23 #include <boost/type_traits/alignment_of.hpp>
24 #include <boost/type_traits/is_pointer.hpp>
25 #include <boost/type_traits/is_signed.hpp>
26 #include <boost/type_traits/is_unsigned.hpp>
27 #include <boost/type_traits/make_signed.hpp>
28 #include <boost/type_traits/make_unsigned.hpp>
29 #include <boost/type_traits/conditional.hpp>
30
31 #include "lightweight_test_stream.hpp"
32 #include "value_with_epsilon.hpp"
33 #include "atomic_wrapper.hpp"
34
35 const unsigned int max_weak_cas_loops = 1000;
36
37 template< typename T >
38 struct is_atomic :
39 public boost::false_type
40 {
41 };
42
43 template< typename T >
44 struct is_atomic< boost::atomic< T > > :
45 public boost::true_type
46 {
47 };
48
49 template< typename T >
50 struct is_atomic< boost::ipc_atomic< T > > :
51 public boost::true_type
52 {
53 };
54
55 template< typename T >
56 struct is_atomic_ref :
57 public boost::false_type
58 {
59 };
60
61 template< typename T >
62 struct is_atomic_ref< boost::atomic_ref< T > > :
63 public boost::true_type
64 {
65 };
66
67 template< typename T >
68 struct is_atomic_ref< boost::ipc_atomic_ref< T > > :
69 public boost::true_type
70 {
71 };
72
73 template< typename Flag >
test_flag_api(void)74 inline void test_flag_api(void)
75 {
76 #ifndef BOOST_ATOMIC_NO_ATOMIC_FLAG_INIT
77 Flag f = BOOST_ATOMIC_FLAG_INIT;
78 #else
79 Flag f;
80 #endif
81
82 BOOST_TEST( !f.test() );
83 BOOST_TEST( !f.test_and_set() );
84 BOOST_TEST( f.test() );
85 BOOST_TEST( f.test_and_set() );
86 BOOST_TEST( f.test() );
87 f.clear();
88 BOOST_TEST( !f.test() );
89 BOOST_TEST( !f.test_and_set() );
90 }
91
92 template< typename T >
test_atomic_type_traits(boost::type<T>)93 inline typename boost::enable_if< is_atomic< T > >::type test_atomic_type_traits(boost::type< T >)
94 {
95 BOOST_TEST_GE(sizeof(T), sizeof(typename T::value_type));
96 }
97
98 template< typename T >
test_atomic_type_traits(boost::type<T>)99 inline typename boost::enable_if< is_atomic_ref< T > >::type test_atomic_type_traits(boost::type< T >)
100 {
101 if (T::is_always_lock_free)
102 {
103 BOOST_TEST_GE(T::required_alignment, boost::alignment_of< typename T::value_type >::value);
104 }
105 else
106 {
107 // Lock-based implementation should not require alignment higher than alignof(T)
108 BOOST_TEST_EQ(T::required_alignment, boost::alignment_of< typename T::value_type >::value);
109 }
110 }
111
112 template< template< typename > class Wrapper, typename T >
test_base_operators(T value1,T value2,T value3)113 void test_base_operators(T value1, T value2, T value3)
114 {
115 test_atomic_type_traits(boost::type< typename Wrapper<T>::atomic_type >());
116
117 // explicit load/store
118 {
119 Wrapper<T> wrapper(value1);
120 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
121 BOOST_TEST_EQ( a.load(), value1 );
122 }
123
124 {
125 Wrapper<T> wrapper(value1);
126 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
127 a.store(value2);
128 BOOST_TEST_EQ( a.load(), value2 );
129 }
130
131 // overloaded assignment/conversion
132 {
133 Wrapper<T> wrapper(value1);
134 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
135 BOOST_TEST( value1 == a );
136 }
137
138 {
139 Wrapper<T> wrapper(value1);
140 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
141 a = value2;
142 BOOST_TEST( value2 == a );
143 }
144
145 // exchange-type operators
146 {
147 Wrapper<T> wrapper(value1);
148 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
149 T n = a.exchange(value2);
150 BOOST_TEST_EQ( a.load(), value2 );
151 BOOST_TEST_EQ( n, value1 );
152 }
153
154 {
155 Wrapper<T> wrapper(value1);
156 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
157 T expected = value1;
158 bool success = a.compare_exchange_strong(expected, value3);
159 BOOST_TEST( success );
160 BOOST_TEST_EQ( a.load(), value3 );
161 BOOST_TEST_EQ( expected, value1 );
162 }
163
164 {
165 Wrapper<T> wrapper(value1);
166 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
167 T expected = value2;
168 bool success = a.compare_exchange_strong(expected, value3);
169 BOOST_TEST( !success );
170 BOOST_TEST_EQ( a.load(), value1 );
171 BOOST_TEST_EQ( expected, value1 );
172 }
173
174 {
175 Wrapper<T> wrapper(value1);
176 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
177 T expected;
178 unsigned int loops = 0;
179 bool success = false;
180 do
181 {
182 expected = value1;
183 success = a.compare_exchange_weak(expected, value3);
184 ++loops;
185 }
186 while (!success && loops < max_weak_cas_loops);
187 BOOST_TEST( success );
188 BOOST_TEST_EQ( a.load(), value3 );
189 BOOST_TEST_EQ( expected, value1 );
190 }
191
192 {
193 Wrapper<T> wrapper(value1);
194 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
195 T expected;
196 unsigned int loops = 0;
197 bool success = false;
198 do
199 {
200 expected = value2;
201 success = a.compare_exchange_weak(expected, value3);
202 if (expected != value2)
203 break;
204 ++loops;
205 }
206 while (!success && loops < max_weak_cas_loops);
207 BOOST_TEST( !success );
208 BOOST_TEST_EQ( a.load(), value1 );
209 BOOST_TEST_EQ( expected, value1 );
210 }
211 }
212
213 //! Tests whether boost::atomic supports constexpr constructor. Note that boost::atomic_ref (as std::atomic_ref) does not support constexpr constructor.
214 template< typename T >
test_constexpr_ctor()215 void test_constexpr_ctor()
216 {
217 #ifndef BOOST_ATOMIC_DETAIL_NO_CXX11_CONSTEXPR_UNION_INIT
218 constexpr T value(0);
219 constexpr boost::atomic<T> tester(value);
220 BOOST_TEST( tester == value );
221 #endif
222 }
223
224 //! The type traits provides max and min values of type D that can be added/subtracted to T(0) without signed overflow
225 template< typename T, typename D, bool IsSigned = boost::is_signed< D >::value >
226 struct distance_limits
227 {
228 //! Difference type D promoted to the width of type T
229 typedef typename boost::conditional<
230 IsSigned,
231 boost::make_signed< T >,
232 boost::make_unsigned< T >
233 >::type::type promoted_difference_type;
234
BOOST_PREVENT_MACRO_SUBSTITUTIONdistance_limits235 static D min BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
236 {
237 return (std::numeric_limits< D >::min)();
238 }
BOOST_PREVENT_MACRO_SUBSTITUTIONdistance_limits239 static D max BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
240 {
241 return (std::numeric_limits< D >::max)();
242 }
243 };
244
245 #if defined(BOOST_MSVC)
246 #pragma warning(push)
247 // 'static_cast': truncation of constant value. There is no actual truncation happening because
248 // the cast is only performed if the value fits in the range of the result.
249 #pragma warning(disable: 4309)
250 #endif
251
252 template< typename T, typename D >
253 struct distance_limits< T*, D, true >
254 {
255 //! Difference type D promoted to the width of type T
256 typedef std::ptrdiff_t promoted_difference_type;
257
BOOST_PREVENT_MACRO_SUBSTITUTIONdistance_limits258 static D min BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
259 {
260 const std::ptrdiff_t ptrdiff = (std::numeric_limits< std::ptrdiff_t >::min)() / static_cast< std::ptrdiff_t >(sizeof(T));
261 const D diff = (std::numeric_limits< D >::min)();
262 // Both values are negative. Return the closest value to zero.
263 return diff < ptrdiff ? static_cast< D >(ptrdiff) : diff;
264 }
BOOST_PREVENT_MACRO_SUBSTITUTIONdistance_limits265 static D max BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
266 {
267 const std::ptrdiff_t ptrdiff = (std::numeric_limits< std::ptrdiff_t >::max)() / static_cast< std::ptrdiff_t >(sizeof(T));
268 const D diff = (std::numeric_limits< D >::max)();
269 // Both values are positive. Return the closest value to zero.
270 return diff > ptrdiff ? static_cast< D >(ptrdiff) : diff;
271 }
272 };
273
274 template< typename T, typename D >
275 struct distance_limits< T*, D, false >
276 {
277 //! Difference type D promoted to the width of type T
278 typedef std::size_t promoted_difference_type;
279
BOOST_PREVENT_MACRO_SUBSTITUTIONdistance_limits280 static D min BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
281 {
282 return (std::numeric_limits< D >::min)();
283 }
BOOST_PREVENT_MACRO_SUBSTITUTIONdistance_limits284 static D max BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
285 {
286 const std::size_t ptrdiff = static_cast< std::size_t >((std::numeric_limits< std::ptrdiff_t >::max)()) / sizeof(T);
287 const D diff = (std::numeric_limits< D >::max)();
288 return diff > ptrdiff ? static_cast< D >(ptrdiff) : diff;
289 }
290 };
291
292 #if defined(BOOST_HAS_INT128)
293
294 // At least libstdc++ does not specialize std::numeric_limits for __int128 in strict mode (i.e. with GNU extensions disabled).
295 // So we have to specialize the limits ourself. We assume two's complement signed representation.
296 template< typename T, bool IsSigned >
297 struct distance_limits< T, boost::int128_type, IsSigned >
298 {
299 //! Difference type D promoted to the width of type T
300 typedef boost::int128_type promoted_difference_type;
301
BOOST_PREVENT_MACRO_SUBSTITUTIONdistance_limits302 static boost::int128_type min BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
303 {
304 return -(max)() - 1;
305 }
BOOST_PREVENT_MACRO_SUBSTITUTIONdistance_limits306 static boost::int128_type max BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
307 {
308 return static_cast< boost::int128_type >((~static_cast< boost::uint128_type >(0u)) >> 1);
309 }
310 };
311
312 template< typename T, bool IsSigned >
313 struct distance_limits< T, boost::uint128_type, IsSigned >
314 {
315 //! Difference type D promoted to the width of type T
316 typedef boost::uint128_type promoted_difference_type;
317
BOOST_PREVENT_MACRO_SUBSTITUTIONdistance_limits318 static boost::uint128_type min BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
319 {
320 return 0u;
321 }
BOOST_PREVENT_MACRO_SUBSTITUTIONdistance_limits322 static boost::uint128_type max BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
323 {
324 return ~static_cast< boost::uint128_type >(0u);
325 }
326 };
327
328 #endif // defined(BOOST_HAS_INT128)
329
330 #if defined(BOOST_MSVC)
331 #pragma warning(pop)
332 #endif
333
334 #if defined(BOOST_MSVC)
335 #pragma warning(push)
336 // unary minus operator applied to unsigned type, result still unsigned
337 #pragma warning(disable: 4146)
338 #endif
339
340 template< template< typename > class Wrapper, typename T, typename D, typename AddType >
test_additive_operators_with_type_and_test()341 void test_additive_operators_with_type_and_test()
342 {
343 #if defined(UBSAN)
344 // clang UBSAN flags this test when AddType is a pointer as it considers subtracting from a null pointer (zero_add) an UB
345 if (boost::is_pointer< AddType >::value)
346 return;
347 #endif
348
349 // Note: This set of tests is extracted to a separate function because otherwise MSVC-10 for x64 generates broken code
350 typedef typename distance_limits< T, D >::promoted_difference_type promoted_difference_type;
351 typedef typename boost::make_unsigned< promoted_difference_type >::type unsigned_promoted_difference_type;
352 const T zero_value = 0;
353 const D zero_diff = 0;
354 const D one_diff = 1;
355 const AddType zero_add = 0;
356 {
357 Wrapper<T> wrapper(zero_value);
358 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
359 bool f = a.add_and_test(zero_diff);
360 BOOST_TEST_EQ( f, false );
361 BOOST_TEST_EQ( a.load(), zero_value );
362
363 f = a.add_and_test(one_diff);
364 BOOST_TEST_EQ( f, true );
365 BOOST_TEST_EQ( a.load(), T(zero_add + one_diff) );
366 }
367 {
368 Wrapper<T> wrapper(zero_value);
369 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
370 bool f = a.add_and_test((distance_limits< T, D >::max)());
371 BOOST_TEST_EQ( f, true );
372 BOOST_TEST_EQ( a.load(), T(zero_add + (distance_limits< T, D >::max)()) );
373 }
374 {
375 Wrapper<T> wrapper(zero_value);
376 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
377 bool f = a.add_and_test((distance_limits< T, D >::min)());
378 BOOST_TEST_EQ( f, ((distance_limits< T, D >::min)() != 0) );
379 BOOST_TEST_EQ( a.load(), T(zero_add + (distance_limits< T, D >::min)()) );
380 }
381
382 {
383 Wrapper<T> wrapper(zero_value);
384 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
385 bool f = a.sub_and_test(zero_diff);
386 BOOST_TEST_EQ( f, false );
387 BOOST_TEST_EQ( a.load(), zero_value );
388
389 f = a.sub_and_test(one_diff);
390 BOOST_TEST_EQ( f, true );
391 BOOST_TEST_EQ( a.load(), T(zero_add - one_diff) );
392 }
393 {
394 Wrapper<T> wrapper(zero_value);
395 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
396 bool f = a.sub_and_test((distance_limits< T, D >::max)());
397 BOOST_TEST_EQ( f, true );
398 BOOST_TEST_EQ( a.load(), T(zero_add - (distance_limits< T, D >::max)()) );
399 }
400 {
401 Wrapper<T> wrapper(zero_value);
402 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
403 bool f = a.sub_and_test((distance_limits< T, D >::min)());
404 BOOST_TEST_EQ( f, ((distance_limits< T, D >::min)() != 0) );
405 // Be very careful as to not cause signed overflow on negation
406 unsigned_promoted_difference_type umin = static_cast< unsigned_promoted_difference_type >(
407 static_cast< promoted_difference_type >((distance_limits< T, D >::min)()));
408 umin = -umin;
409 promoted_difference_type neg_min;
410 std::memcpy(&neg_min, &umin, sizeof(neg_min));
411 BOOST_TEST_EQ( a.load(), T(zero_add + neg_min) );
412 }
413 }
414
415 #if defined(BOOST_MSVC)
416 #pragma warning(pop)
417 #endif
418
419 template< template< typename > class Wrapper, typename T, typename D, typename AddType >
test_additive_operators_with_type(T value,D delta)420 void test_additive_operators_with_type(T value, D delta)
421 {
422 /* note: the tests explicitly cast the result of any addition
423 to the type to be tested to force truncation of the result to
424 the correct range in case of overflow */
425
426 // explicit add/sub
427 {
428 Wrapper<T> wrapper(value);
429 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
430 T n = a.fetch_add(delta);
431 BOOST_TEST_EQ( a.load(), T((AddType)value + delta) );
432 BOOST_TEST_EQ( n, value );
433 }
434
435 {
436 Wrapper<T> wrapper(value);
437 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
438 T n = a.fetch_sub(delta);
439 BOOST_TEST_EQ( a.load(), T((AddType)value - delta) );
440 BOOST_TEST_EQ( n, value );
441 }
442
443 // add/sub with an immediate
444 {
445 Wrapper<T> wrapper(value);
446 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
447 T n = a.fetch_add(1);
448 BOOST_TEST_EQ( a.load(), T((AddType)value + 1) );
449 BOOST_TEST_EQ( n, value );
450 }
451
452 {
453 Wrapper<T> wrapper(value);
454 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
455 T n = a.fetch_sub(1);
456 BOOST_TEST_EQ( a.load(), T((AddType)value - 1) );
457 BOOST_TEST_EQ( n, value );
458 }
459
460 {
461 Wrapper<T> wrapper(value);
462 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
463 T n = a.fetch_add(76);
464 BOOST_TEST_EQ( a.load(), T((AddType)value + 76) );
465 BOOST_TEST_EQ( n, value );
466 }
467
468 {
469 Wrapper<T> wrapper(value);
470 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
471 T n = a.fetch_sub(76);
472 BOOST_TEST_EQ( a.load(), T((AddType)value - 76) );
473 BOOST_TEST_EQ( n, value );
474 }
475
476 if ((std::numeric_limits< D >::max)() >= 4097)
477 {
478 Wrapper<T> wrapper(value);
479 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
480 T n = a.fetch_add((D)4097);
481 BOOST_TEST_EQ( a.load(), T((AddType)value + (D)4097) );
482 BOOST_TEST_EQ( n, value );
483 }
484
485 if ((std::numeric_limits< D >::max)() >= 4097)
486 {
487 Wrapper<T> wrapper(value);
488 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
489 T n = a.fetch_sub((D)4097);
490 BOOST_TEST_EQ( a.load(), T((AddType)value - (D)4097) );
491 BOOST_TEST_EQ( n, value );
492 }
493
494 // overloaded modify/assign
495 {
496 Wrapper<T> wrapper(value);
497 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
498 T n = (a += delta);
499 BOOST_TEST_EQ( a.load(), T((AddType)value + delta) );
500 BOOST_TEST_EQ( n, T((AddType)value + delta) );
501 }
502
503 {
504 Wrapper<T> wrapper(value);
505 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
506 T n = (a -= delta);
507 BOOST_TEST_EQ( a.load(), T((AddType)value - delta) );
508 BOOST_TEST_EQ( n, T((AddType)value - delta) );
509 }
510
511 // overloaded increment/decrement
512 {
513 Wrapper<T> wrapper(value);
514 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
515 T n = a++;
516 BOOST_TEST_EQ( a.load(), T((AddType)value + 1) );
517 BOOST_TEST_EQ( n, value );
518 }
519
520 {
521 Wrapper<T> wrapper(value);
522 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
523 T n = ++a;
524 BOOST_TEST_EQ( a.load(), T((AddType)value + 1) );
525 BOOST_TEST_EQ( n, T((AddType)value + 1) );
526 }
527
528 {
529 Wrapper<T> wrapper(value);
530 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
531 T n = a--;
532 BOOST_TEST_EQ( a.load(), T((AddType)value - 1) );
533 BOOST_TEST_EQ( n, value );
534 }
535
536 {
537 Wrapper<T> wrapper(value);
538 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
539 T n = --a;
540 BOOST_TEST_EQ( a.load(), T((AddType)value - 1) );
541 BOOST_TEST_EQ( n, T((AddType)value - 1) );
542 }
543
544 // Operations returning the actual resulting value
545 {
546 Wrapper<T> wrapper(value);
547 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
548 T n = a.add(delta);
549 BOOST_TEST_EQ( a.load(), T((AddType)value + delta) );
550 BOOST_TEST_EQ( n, T((AddType)value + delta) );
551 }
552
553 {
554 Wrapper<T> wrapper(value);
555 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
556 T n = a.sub(delta);
557 BOOST_TEST_EQ( a.load(), T((AddType)value - delta) );
558 BOOST_TEST_EQ( n, T((AddType)value - delta) );
559 }
560
561 // The same with an immediate
562 {
563 Wrapper<T> wrapper(value);
564 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
565 T n = a.add(1);
566 BOOST_TEST_EQ( a.load(), T((AddType)value + 1) );
567 BOOST_TEST_EQ( n, T((AddType)value + 1) );
568 }
569
570 {
571 Wrapper<T> wrapper(value);
572 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
573 T n = a.sub(1);
574 BOOST_TEST_EQ( a.load(), T((AddType)value - 1) );
575 BOOST_TEST_EQ( n, T((AddType)value - 1) );
576 }
577
578 {
579 Wrapper<T> wrapper(value);
580 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
581 T n = a.add(76);
582 BOOST_TEST_EQ( a.load(), T((AddType)value + 76) );
583 BOOST_TEST_EQ( n, T((AddType)value + 76) );
584 }
585
586 {
587 Wrapper<T> wrapper(value);
588 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
589 T n = a.sub(76);
590 BOOST_TEST_EQ( a.load(), T((AddType)value - 76) );
591 BOOST_TEST_EQ( n, T((AddType)value - 76) );
592 }
593
594 if ((std::numeric_limits< D >::max)() >= 4097)
595 {
596 Wrapper<T> wrapper(value);
597 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
598 T n = a.add((D)4097);
599 BOOST_TEST_EQ( a.load(), T((AddType)value + (D)4097) );
600 BOOST_TEST_EQ( n, T((AddType)value + (D)4097) );
601 }
602
603 if ((std::numeric_limits< D >::max)() >= 4097)
604 {
605 Wrapper<T> wrapper(value);
606 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
607 T n = a.sub((D)4097);
608 BOOST_TEST_EQ( a.load(), T((AddType)value - (D)4097) );
609 BOOST_TEST_EQ( n, T((AddType)value - (D)4097) );
610 }
611
612 // Opaque operations
613 {
614 Wrapper<T> wrapper(value);
615 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
616 a.opaque_add(delta);
617 BOOST_TEST_EQ( a.load(), T((AddType)value + delta) );
618 }
619
620 {
621 Wrapper<T> wrapper(value);
622 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
623 a.opaque_sub(delta);
624 BOOST_TEST_EQ( a.load(), T((AddType)value - delta) );
625 }
626
627 // The same with an immediate
628 {
629 Wrapper<T> wrapper(value);
630 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
631 a.opaque_add(1);
632 BOOST_TEST_EQ( a.load(), T((AddType)value + 1) );
633 }
634
635 {
636 Wrapper<T> wrapper(value);
637 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
638 a.opaque_sub(1);
639 BOOST_TEST_EQ( a.load(), T((AddType)value - 1) );
640 }
641
642 {
643 Wrapper<T> wrapper(value);
644 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
645 a.opaque_add(76);
646 BOOST_TEST_EQ( a.load(), T((AddType)value + 76) );
647 }
648
649 {
650 Wrapper<T> wrapper(value);
651 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
652 a.opaque_sub(76);
653 BOOST_TEST_EQ( a.load(), T((AddType)value - 76) );
654 }
655
656 if ((std::numeric_limits< D >::max)() >= 4097)
657 {
658 Wrapper<T> wrapper(value);
659 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
660 a.opaque_add((D)4097);
661 BOOST_TEST_EQ( a.load(), T((AddType)value + (D)4097) );
662 }
663
664 if ((std::numeric_limits< D >::max)() >= 4097)
665 {
666 Wrapper<T> wrapper(value);
667 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
668 a.opaque_sub((D)4097);
669 BOOST_TEST_EQ( a.load(), T((AddType)value - (D)4097) );
670 }
671
672 // Modify and test operations
673 test_additive_operators_with_type_and_test< Wrapper, T, D, AddType >();
674 }
675
676 template< template< typename > class Wrapper, typename T, typename D >
test_additive_operators(T value,D delta)677 void test_additive_operators(T value, D delta)
678 {
679 test_additive_operators_with_type< Wrapper, T, D, T >(value, delta);
680 }
681
682 template< template< typename > class Wrapper, typename T >
test_negation()683 void test_negation()
684 {
685 {
686 Wrapper<T> wrapper((T)1);
687 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
688 T n = a.fetch_negate();
689 BOOST_TEST_EQ( a.load(), (T)-1 );
690 BOOST_TEST_EQ( n, (T)1 );
691
692 n = a.fetch_negate();
693 BOOST_TEST_EQ( a.load(), (T)1 );
694 BOOST_TEST_EQ( n, (T)-1 );
695 }
696 {
697 Wrapper<T> wrapper((T)1);
698 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
699 T n = a.negate();
700 BOOST_TEST_EQ( a.load(), (T)-1 );
701 BOOST_TEST_EQ( n, (T)-1 );
702
703 n = a.negate();
704 BOOST_TEST_EQ( a.load(), (T)1 );
705 BOOST_TEST_EQ( n, (T)1 );
706 }
707 {
708 Wrapper<T> wrapper((T)1);
709 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
710 a.opaque_negate();
711 BOOST_TEST_EQ( a.load(), (T)-1 );
712
713 a.opaque_negate();
714 BOOST_TEST_EQ( a.load(), (T)1 );
715 }
716 {
717 Wrapper<T> wrapper((T)1);
718 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
719 bool f = a.negate_and_test();
720 BOOST_TEST_EQ( f, true );
721 BOOST_TEST_EQ( a.load(), (T)-1 );
722
723 f = a.negate_and_test();
724 BOOST_TEST_EQ( f, true );
725 BOOST_TEST_EQ( a.load(), (T)1 );
726 }
727 {
728 Wrapper<T> wrapper((T)0);
729 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
730 bool f = a.negate_and_test();
731 BOOST_TEST_EQ( f, false );
732 BOOST_TEST_EQ( a.load(), (T)0 );
733 }
734 }
735
736 template< template< typename > class Wrapper, typename T >
test_additive_wrap(T value)737 void test_additive_wrap(T value)
738 {
739 {
740 Wrapper<T> wrapper(value);
741 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
742 T n = a.fetch_add(1) + (T)1;
743 BOOST_TEST_EQ( a.load(), n );
744 }
745 {
746 Wrapper<T> wrapper(value);
747 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
748 T n = a.fetch_sub(1) - (T)1;
749 BOOST_TEST_EQ( a.load(), n );
750 }
751 }
752
753 template< template< typename > class Wrapper, typename T >
test_bit_operators(T value,T delta)754 void test_bit_operators(T value, T delta)
755 {
756 // explicit and/or/xor
757 {
758 Wrapper<T> wrapper(value);
759 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
760 T n = a.fetch_and(delta);
761 BOOST_TEST_EQ( a.load(), T(value & delta) );
762 BOOST_TEST_EQ( n, value );
763 }
764
765 {
766 Wrapper<T> wrapper(value);
767 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
768 T n = a.fetch_or(delta);
769 BOOST_TEST_EQ( a.load(), T(value | delta) );
770 BOOST_TEST_EQ( n, value );
771 }
772
773 {
774 Wrapper<T> wrapper(value);
775 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
776 T n = a.fetch_xor(delta);
777 BOOST_TEST_EQ( a.load(), T(value ^ delta) );
778 BOOST_TEST_EQ( n, value );
779 }
780
781 {
782 Wrapper<T> wrapper(value);
783 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
784 T n = a.fetch_complement();
785 BOOST_TEST_EQ( a.load(), T(~value) );
786 BOOST_TEST_EQ( n, value );
787 }
788
789 // and/or/xor with an immediate. The immediates below are chosen to either be encodable in an instruction or not for various target architectures.
790 {
791 Wrapper<T> wrapper(value);
792 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
793 T n = a.fetch_and((T)1);
794 BOOST_TEST_EQ( a.load(), T(value & (T)1) );
795 BOOST_TEST_EQ( n, value );
796 }
797
798 {
799 Wrapper<T> wrapper(value);
800 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
801 T n = a.fetch_or((T)1);
802 BOOST_TEST_EQ( a.load(), T(value | (T)1) );
803 BOOST_TEST_EQ( n, value );
804 }
805
806 {
807 Wrapper<T> wrapper(value);
808 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
809 T n = a.fetch_xor((T)1);
810 BOOST_TEST_EQ( a.load(), T(value ^ (T)1) );
811 BOOST_TEST_EQ( n, value );
812 }
813
814 // The following constants are not encodable in AArch64
815 {
816 Wrapper<T> wrapper(value);
817 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
818 T n = a.fetch_and((T)76);
819 BOOST_TEST_EQ( a.load(), T(value & (T)76) );
820 BOOST_TEST_EQ( n, value );
821 }
822
823 {
824 Wrapper<T> wrapper(value);
825 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
826 T n = a.fetch_or((T)76);
827 BOOST_TEST_EQ( a.load(), T(value | (T)76) );
828 BOOST_TEST_EQ( n, value );
829 }
830
831 {
832 Wrapper<T> wrapper(value);
833 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
834 T n = a.fetch_xor((T)76);
835 BOOST_TEST_EQ( a.load(), T(value ^ (T)76) );
836 BOOST_TEST_EQ( n, value );
837 }
838
839 // overloaded modify/assign
840 {
841 Wrapper<T> wrapper(value);
842 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
843 T n = (a &= delta);
844 BOOST_TEST_EQ( a.load(), T(value & delta) );
845 BOOST_TEST_EQ( n, T(value & delta) );
846 }
847
848 {
849 Wrapper<T> wrapper(value);
850 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
851 T n = (a |= delta);
852 BOOST_TEST_EQ( a.load(), T(value | delta) );
853 BOOST_TEST_EQ( n, T(value | delta) );
854 }
855
856 {
857 Wrapper<T> wrapper(value);
858 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
859 T n = (a ^= delta);
860 BOOST_TEST_EQ( a.load(), T(value ^ delta) );
861 BOOST_TEST_EQ( n, T(value ^ delta) );
862 }
863
864 // Operations returning the actual resulting value
865 {
866 Wrapper<T> wrapper(value);
867 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
868 T n = a.bitwise_and(delta);
869 BOOST_TEST_EQ( a.load(), T(value & delta) );
870 BOOST_TEST_EQ( n, T(value & delta) );
871 }
872
873 {
874 Wrapper<T> wrapper(value);
875 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
876 T n = a.bitwise_or(delta);
877 BOOST_TEST_EQ( a.load(), T(value | delta) );
878 BOOST_TEST_EQ( n, T(value | delta) );
879 }
880
881 {
882 Wrapper<T> wrapper(value);
883 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
884 T n = a.bitwise_xor(delta);
885 BOOST_TEST_EQ( a.load(), T(value ^ delta) );
886 BOOST_TEST_EQ( n, T(value ^ delta) );
887 }
888
889 {
890 Wrapper<T> wrapper(value);
891 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
892 T n = a.bitwise_complement();
893 BOOST_TEST_EQ( a.load(), T(~value) );
894 BOOST_TEST_EQ( n, T(~value) );
895 }
896
897 // The same with an immediate
898 {
899 Wrapper<T> wrapper(value);
900 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
901 T n = a.bitwise_and((T)1);
902 BOOST_TEST_EQ( a.load(), T(value & (T)1) );
903 BOOST_TEST_EQ( n, T(value & (T)1) );
904 }
905
906 {
907 Wrapper<T> wrapper(value);
908 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
909 T n = a.bitwise_or((T)1);
910 BOOST_TEST_EQ( a.load(), T(value | (T)1) );
911 BOOST_TEST_EQ( n, T(value | (T)1) );
912 }
913
914 {
915 Wrapper<T> wrapper(value);
916 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
917 T n = a.bitwise_xor((T)1);
918 BOOST_TEST_EQ( a.load(), T(value ^ (T)1) );
919 BOOST_TEST_EQ( n, T(value ^ (T)1) );
920 }
921
922 // The following constants are not encodable in AArch64
923 {
924 Wrapper<T> wrapper(value);
925 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
926 T n = a.bitwise_and((T)76);
927 BOOST_TEST_EQ( a.load(), T(value & (T)76) );
928 BOOST_TEST_EQ( n, T(value & (T)76) );
929 }
930
931 {
932 Wrapper<T> wrapper(value);
933 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
934 T n = a.bitwise_or((T)76);
935 BOOST_TEST_EQ( a.load(), T(value | (T)76) );
936 BOOST_TEST_EQ( n, T(value | (T)76) );
937 }
938
939 {
940 Wrapper<T> wrapper(value);
941 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
942 T n = a.bitwise_xor((T)76);
943 BOOST_TEST_EQ( a.load(), T(value ^ (T)76) );
944 BOOST_TEST_EQ( n, T(value ^ (T)76) );
945 }
946
947 // Opaque operations
948 {
949 Wrapper<T> wrapper(value);
950 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
951 a.opaque_and(delta);
952 BOOST_TEST_EQ( a.load(), T(value & delta) );
953 }
954
955 {
956 Wrapper<T> wrapper(value);
957 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
958 a.opaque_or(delta);
959 BOOST_TEST_EQ( a.load(), T(value | delta) );
960 }
961
962 {
963 Wrapper<T> wrapper(value);
964 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
965 a.opaque_xor(delta);
966 BOOST_TEST_EQ( a.load(), T(value ^ delta) );
967 }
968
969 {
970 Wrapper<T> wrapper(value);
971 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
972 a.opaque_complement();
973 BOOST_TEST_EQ( a.load(), T(~value) );
974 }
975
976 // The same with an immediate
977 {
978 Wrapper<T> wrapper(value);
979 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
980 a.opaque_and((T)1);
981 BOOST_TEST_EQ( a.load(), T(value & (T)1) );
982 }
983
984 {
985 Wrapper<T> wrapper(value);
986 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
987 a.opaque_or((T)1);
988 BOOST_TEST_EQ( a.load(), T(value | (T)1) );
989 }
990
991 {
992 Wrapper<T> wrapper(value);
993 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
994 a.opaque_xor((T)1);
995 BOOST_TEST_EQ( a.load(), T(value ^ (T)1) );
996 }
997
998 // The following constants are not encodable in AArch64
999 {
1000 Wrapper<T> wrapper(value);
1001 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
1002 a.opaque_and((T)76);
1003 BOOST_TEST_EQ( a.load(), T(value & (T)76) );
1004 }
1005
1006 {
1007 Wrapper<T> wrapper(value);
1008 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
1009 a.opaque_or((T)76);
1010 BOOST_TEST_EQ( a.load(), T(value | (T)76) );
1011 }
1012
1013 {
1014 Wrapper<T> wrapper(value);
1015 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
1016 a.opaque_xor((T)76);
1017 BOOST_TEST_EQ( a.load(), T(value ^ (T)76) );
1018 }
1019
1020 // Modify and test operations
1021 {
1022 Wrapper<T> wrapper((T)1);
1023 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
1024 bool f = a.and_and_test((T)1);
1025 BOOST_TEST_EQ( f, true );
1026 BOOST_TEST_EQ( a.load(), T(1) );
1027
1028 f = a.and_and_test((T)0);
1029 BOOST_TEST_EQ( f, false );
1030 BOOST_TEST_EQ( a.load(), T(0) );
1031
1032 f = a.and_and_test((T)0);
1033 BOOST_TEST_EQ( f, false );
1034 BOOST_TEST_EQ( a.load(), T(0) );
1035 }
1036
1037 {
1038 Wrapper<T> wrapper((T)0);
1039 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
1040 bool f = a.or_and_test((T)0);
1041 BOOST_TEST_EQ( f, false );
1042 BOOST_TEST_EQ( a.load(), T(0) );
1043
1044 f = a.or_and_test((T)1);
1045 BOOST_TEST_EQ( f, true );
1046 BOOST_TEST_EQ( a.load(), T(1) );
1047
1048 f = a.or_and_test((T)1);
1049 BOOST_TEST_EQ( f, true );
1050 BOOST_TEST_EQ( a.load(), T(1) );
1051 }
1052
1053 {
1054 Wrapper<T> wrapper((T)0);
1055 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
1056 bool f = a.xor_and_test((T)0);
1057 BOOST_TEST_EQ( f, false );
1058 BOOST_TEST_EQ( a.load(), T(0) );
1059
1060 f = a.xor_and_test((T)1);
1061 BOOST_TEST_EQ( f, true );
1062 BOOST_TEST_EQ( a.load(), T(1) );
1063
1064 f = a.xor_and_test((T)1);
1065 BOOST_TEST_EQ( f, false );
1066 BOOST_TEST_EQ( a.load(), T(0) );
1067 }
1068
1069 {
1070 Wrapper<T> wrapper((T)0);
1071 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
1072 bool f = a.complement_and_test();
1073 BOOST_TEST_EQ( f, true );
1074 BOOST_TEST_EQ( a.load(), static_cast< T >(~static_cast< T >(0)) );
1075
1076 f = a.complement_and_test();
1077 BOOST_TEST_EQ( f, false );
1078 BOOST_TEST_EQ( a.load(), T(0) );
1079 }
1080
1081 // Bit test and modify operations
1082 {
1083 Wrapper<T> wrapper((T)42);
1084 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
1085 bool f = a.bit_test_and_set(0);
1086 BOOST_TEST_EQ( f, false );
1087 BOOST_TEST_EQ( a.load(), T(43) );
1088
1089 f = a.bit_test_and_set(1);
1090 BOOST_TEST_EQ( f, true );
1091 BOOST_TEST_EQ( a.load(), T(43) );
1092
1093 f = a.bit_test_and_set(2);
1094 BOOST_TEST_EQ( f, false );
1095 BOOST_TEST_EQ( a.load(), T(47) );
1096 }
1097
1098 {
1099 Wrapper<T> wrapper((T)42);
1100 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
1101 bool f = a.bit_test_and_reset(0);
1102 BOOST_TEST_EQ( f, false );
1103 BOOST_TEST_EQ( a.load(), T(42) );
1104
1105 f = a.bit_test_and_reset(1);
1106 BOOST_TEST_EQ( f, true );
1107 BOOST_TEST_EQ( a.load(), T(40) );
1108
1109 f = a.bit_test_and_set(2);
1110 BOOST_TEST_EQ( f, false );
1111 BOOST_TEST_EQ( a.load(), T(44) );
1112 }
1113
1114 {
1115 Wrapper<T> wrapper((T)42);
1116 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
1117 bool f = a.bit_test_and_complement(0);
1118 BOOST_TEST_EQ( f, false );
1119 BOOST_TEST_EQ( a.load(), T(43) );
1120
1121 f = a.bit_test_and_complement(1);
1122 BOOST_TEST_EQ( f, true );
1123 BOOST_TEST_EQ( a.load(), T(41) );
1124
1125 f = a.bit_test_and_complement(2);
1126 BOOST_TEST_EQ( f, false );
1127 BOOST_TEST_EQ( a.load(), T(45) );
1128 }
1129
1130 // Test that a runtime value works for the bit index. This is important for asm block constraints.
1131 {
1132 unsigned int runtime_bit_index = std::rand() & 7u;
1133 Wrapper<T> wrapper((T)42);
1134 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
1135
1136 a.bit_test_and_set(runtime_bit_index);
1137 a.bit_test_and_reset(runtime_bit_index);
1138 a.bit_test_and_complement(runtime_bit_index);
1139 }
1140 }
1141
1142 template< template< typename > class Wrapper, typename T >
do_test_integral_api(boost::false_type)1143 void do_test_integral_api(boost::false_type)
1144 {
1145 test_base_operators< Wrapper, T >(42, 43, 44);
1146 test_additive_operators< Wrapper, T, T >(42, 17);
1147 test_bit_operators< Wrapper, T >((T)0x5f5f5f5f5f5f5f5fULL, (T)0xf5f5f5f5f5f5f5f5ULL);
1148
1149 /* test for unsigned overflow/underflow */
1150 test_additive_operators< Wrapper, T, T >((T)-1, 1);
1151 test_additive_operators< Wrapper, T, T >(0, 1);
1152 /* test for signed overflow/underflow */
1153 test_additive_operators< Wrapper, T, T >(((T)-1) >> (sizeof(T) * 8 - 1), 1);
1154 test_additive_operators< Wrapper, T, T >(1 + (((T)-1) >> (sizeof(T) * 8 - 1)), 1);
1155 }
1156
1157 template< template< typename > class Wrapper, typename T >
do_test_integral_api(boost::true_type)1158 void do_test_integral_api(boost::true_type)
1159 {
1160 do_test_integral_api< Wrapper, T >(boost::false_type());
1161
1162 test_additive_wrap< Wrapper, T >(0u);
1163 BOOST_CONSTEXPR_OR_CONST T all_ones = ~(T)0u;
1164 test_additive_wrap< Wrapper, T >(all_ones);
1165 BOOST_CONSTEXPR_OR_CONST T max_signed_twos_compl = all_ones >> 1;
1166 test_additive_wrap< Wrapper, T >(all_ones ^ max_signed_twos_compl);
1167 test_additive_wrap< Wrapper, T >(max_signed_twos_compl);
1168 }
1169
1170 template< template< typename > class Wrapper, typename T >
test_integral_api(void)1171 inline void test_integral_api(void)
1172 {
1173 do_test_integral_api< Wrapper, T >(boost::is_unsigned<T>());
1174
1175 if (boost::is_signed<T>::value)
1176 test_negation< Wrapper, T >();
1177 }
1178
1179 template< template< typename > class Wrapper, typename T >
test_lock_free_integral_api(boost::true_type)1180 inline void test_lock_free_integral_api(boost::true_type)
1181 {
1182 test_integral_api< Wrapper, T >();
1183 }
1184
1185 template< template< typename > class Wrapper, typename T >
test_lock_free_integral_api(boost::false_type)1186 inline void test_lock_free_integral_api(boost::false_type)
1187 {
1188 }
1189
1190 template< template< typename > class Wrapper, typename T >
test_lock_free_integral_api(void)1191 inline void test_lock_free_integral_api(void)
1192 {
1193 test_lock_free_integral_api< Wrapper, T >(boost::integral_constant< bool, Wrapper< T >::atomic_type::is_always_lock_free >());
1194 }
1195
1196 #if !defined(BOOST_ATOMIC_NO_FLOATING_POINT)
1197
1198 template< template< typename > class Wrapper, typename T, typename D >
test_fp_additive_operators(T value,D delta)1199 void test_fp_additive_operators(T value, D delta)
1200 {
1201 // explicit add/sub
1202 {
1203 Wrapper<T> wrapper(value);
1204 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
1205 T n = a.fetch_add(delta);
1206 BOOST_TEST_EQ( a.load(), approx(T(value + delta)) );
1207 BOOST_TEST_EQ( n, approx(value) );
1208 }
1209
1210 {
1211 Wrapper<T> wrapper(value);
1212 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
1213 T n = a.fetch_sub(delta);
1214 BOOST_TEST_EQ( a.load(), approx(T(value - delta)) );
1215 BOOST_TEST_EQ( n, approx(value) );
1216 }
1217
1218 // overloaded modify/assign
1219 {
1220 Wrapper<T> wrapper(value);
1221 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
1222 T n = (a += delta);
1223 BOOST_TEST_EQ( a.load(), approx(T(value + delta)) );
1224 BOOST_TEST_EQ( n, approx(T(value + delta)) );
1225 }
1226
1227 {
1228 Wrapper<T> wrapper(value);
1229 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
1230 T n = (a -= delta);
1231 BOOST_TEST_EQ( a.load(), approx(T(value - delta)) );
1232 BOOST_TEST_EQ( n, approx(T(value - delta)) );
1233 }
1234
1235 // Operations returning the actual resulting value
1236 {
1237 Wrapper<T> wrapper(value);
1238 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
1239 T n = a.add(delta);
1240 BOOST_TEST_EQ( a.load(), approx(T(value + delta)) );
1241 BOOST_TEST_EQ( n, approx(T(value + delta)) );
1242 }
1243
1244 {
1245 Wrapper<T> wrapper(value);
1246 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
1247 T n = a.sub(delta);
1248 BOOST_TEST_EQ( a.load(), approx(T(value - delta)) );
1249 BOOST_TEST_EQ( n, approx(T(value - delta)) );
1250 }
1251
1252 // Opaque operations
1253 {
1254 Wrapper<T> wrapper(value);
1255 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
1256 a.opaque_add(delta);
1257 BOOST_TEST_EQ( a.load(), approx(T(value + delta)) );
1258 }
1259
1260 {
1261 Wrapper<T> wrapper(value);
1262 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
1263 a.opaque_sub(delta);
1264 BOOST_TEST_EQ( a.load(), approx(T(value - delta)) );
1265 }
1266 }
1267
1268 template< template< typename > class Wrapper, typename T >
test_fp_negation()1269 void test_fp_negation()
1270 {
1271 {
1272 Wrapper<T> wrapper((T)1);
1273 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
1274 T n = a.fetch_negate();
1275 BOOST_TEST_EQ( a.load(), approx((T)-1) );
1276 BOOST_TEST_EQ( n, approx((T)1) );
1277
1278 n = a.fetch_negate();
1279 BOOST_TEST_EQ( a.load(), approx((T)1) );
1280 BOOST_TEST_EQ( n, approx((T)-1) );
1281 }
1282 {
1283 Wrapper<T> wrapper((T)1);
1284 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
1285 T n = a.negate();
1286 BOOST_TEST_EQ( a.load(), approx((T)-1) );
1287 BOOST_TEST_EQ( n, approx((T)-1) );
1288
1289 n = a.negate();
1290 BOOST_TEST_EQ( a.load(), approx((T)1) );
1291 BOOST_TEST_EQ( n, approx((T)1) );
1292 }
1293 {
1294 Wrapper<T> wrapper((T)1);
1295 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
1296 a.opaque_negate();
1297 BOOST_TEST_EQ( a.load(), approx((T)-1) );
1298
1299 a.opaque_negate();
1300 BOOST_TEST_EQ( a.load(), approx((T)1) );
1301 }
1302 }
1303
1304 #endif // !defined(BOOST_ATOMIC_NO_FLOATING_POINT)
1305
1306 template< template< typename > class Wrapper, typename T >
test_floating_point_api(void)1307 void test_floating_point_api(void)
1308 {
1309 // Note: When support for floating point is disabled, even the base operation tests may fail because
1310 // the generic template specialization does not account for garbage in padding bits that are present in some FP types.
1311 #if !defined(BOOST_ATOMIC_NO_FLOATING_POINT)
1312 test_base_operators< Wrapper, T >(static_cast<T>(42.1), static_cast<T>(43.2), static_cast<T>(44.3));
1313
1314 test_fp_additive_operators< Wrapper, T, T >(static_cast<T>(42.5), static_cast<T>(17.7));
1315 test_fp_additive_operators< Wrapper, T, T >(static_cast<T>(-42.5), static_cast<T>(-17.7));
1316
1317 test_fp_negation< Wrapper, T >();
1318 #endif
1319 }
1320
1321 template< template< typename > class Wrapper, typename T >
test_lock_free_floating_point_api(boost::true_type)1322 inline void test_lock_free_floating_point_api(boost::true_type)
1323 {
1324 test_floating_point_api< Wrapper, T >();
1325 }
1326
1327 template< template< typename > class Wrapper, typename T >
test_lock_free_floating_point_api(boost::false_type)1328 inline void test_lock_free_floating_point_api(boost::false_type)
1329 {
1330 }
1331
1332 template< template< typename > class Wrapper, typename T >
test_lock_free_floating_point_api(void)1333 inline void test_lock_free_floating_point_api(void)
1334 {
1335 test_lock_free_floating_point_api< Wrapper, T >(boost::integral_constant< bool, Wrapper< T >::atomic_type::is_always_lock_free >());
1336 }
1337
1338
1339 template< template< typename > class Wrapper, typename T >
test_pointer_api(void)1340 void test_pointer_api(void)
1341 {
1342 std::vector< T > values;
1343 values.resize(5000); // make the vector large enough to accommodate pointer arithmetics in the additive tests
1344
1345 test_base_operators< Wrapper, T* >(&values[0], &values[1], &values[2]);
1346 test_additive_operators< Wrapper, T* >(&values[1], 1);
1347
1348 test_base_operators< Wrapper, void* >(&values[0], &values[1], &values[2]);
1349
1350 #if defined(BOOST_HAS_INTPTR_T)
1351 Wrapper<void*> wrapper_ptr;
1352 typename Wrapper<void*>::atomic_reference_type ptr = wrapper_ptr.a;
1353 Wrapper<boost::intptr_t> wrapper_integral;
1354 typename Wrapper<boost::intptr_t>::atomic_reference_type integral = wrapper_integral.a;
1355 BOOST_TEST_EQ( ptr.is_lock_free(), integral.is_lock_free() );
1356 #endif
1357 }
1358
1359 enum test_enum
1360 {
1361 foo, bar, baz
1362 };
1363
1364 template< template< typename > class Wrapper, typename T >
test_lock_free_pointer_api(boost::true_type)1365 inline void test_lock_free_pointer_api(boost::true_type)
1366 {
1367 test_pointer_api< Wrapper, T >();
1368 }
1369
1370 template< template< typename > class Wrapper, typename T >
test_lock_free_pointer_api(boost::false_type)1371 inline void test_lock_free_pointer_api(boost::false_type)
1372 {
1373 }
1374
1375 template< template< typename > class Wrapper, typename T >
test_lock_free_pointer_api(void)1376 inline void test_lock_free_pointer_api(void)
1377 {
1378 test_lock_free_pointer_api< Wrapper, T >(boost::integral_constant< bool, Wrapper< T >::atomic_type::is_always_lock_free >());
1379 }
1380
1381
1382 template< template< typename > class Wrapper >
test_enum_api(void)1383 void test_enum_api(void)
1384 {
1385 test_base_operators< Wrapper >(foo, bar, baz);
1386 }
1387
1388 template< template< typename > class Wrapper >
test_lock_free_enum_api(boost::true_type)1389 inline void test_lock_free_enum_api(boost::true_type)
1390 {
1391 test_enum_api< Wrapper >();
1392 }
1393
1394 template< template< typename > class Wrapper >
test_lock_free_enum_api(boost::false_type)1395 inline void test_lock_free_enum_api(boost::false_type)
1396 {
1397 }
1398
1399 template< template< typename > class Wrapper >
test_lock_free_enum_api(void)1400 inline void test_lock_free_enum_api(void)
1401 {
1402 test_lock_free_enum_api< Wrapper >(boost::integral_constant< bool, Wrapper< test_enum >::atomic_type::is_always_lock_free >());
1403 }
1404
1405
1406 template< typename T >
1407 struct test_struct
1408 {
1409 typedef T value_type;
1410 value_type i;
operator ==test_struct1411 inline bool operator==(test_struct const& c) const { return i == c.i; }
operator !=test_struct1412 inline bool operator!=(test_struct const& c) const { return !operator==(c); }
1413 };
1414
1415 template< typename Char, typename Traits, typename T >
operator <<(std::basic_ostream<Char,Traits> & strm,test_struct<T> const & s)1416 inline std::basic_ostream< Char, Traits >& operator<< (std::basic_ostream< Char, Traits >& strm, test_struct< T > const& s)
1417 {
1418 test_stream << "{" << s.i << "}";
1419 return strm;
1420 }
1421
1422 template< template< typename > class Wrapper, typename T >
test_struct_api(void)1423 void test_struct_api(void)
1424 {
1425 T a = {1}, b = {2}, c = {3};
1426
1427 test_base_operators< Wrapper >(a, b, c);
1428
1429 {
1430 Wrapper<T> wrapper_sa;
1431 typename Wrapper<T>::atomic_reference_type sa = wrapper_sa.a;
1432 Wrapper<typename T::value_type> wrapper_si;
1433 typename Wrapper<typename T::value_type>::atomic_reference_type si = wrapper_si.a;
1434 BOOST_TEST_EQ( sa.is_lock_free(), si.is_lock_free() );
1435 }
1436 }
1437
1438 template< typename T >
1439 struct test_struct_x2
1440 {
1441 typedef T value_type;
1442 value_type i, j;
operator ==test_struct_x21443 inline bool operator==(test_struct_x2 const& c) const { return i == c.i && j == c.j; }
operator !=test_struct_x21444 inline bool operator!=(test_struct_x2 const& c) const { return !operator==(c); }
1445 };
1446
1447 template< typename Char, typename Traits, typename T >
operator <<(std::basic_ostream<Char,Traits> & strm,test_struct_x2<T> const & s)1448 inline std::basic_ostream< Char, Traits >& operator<< (std::basic_ostream< Char, Traits >& strm, test_struct_x2< T > const& s)
1449 {
1450 test_stream << "{" << s.i << ", " << s.j << "}";
1451 return strm;
1452 }
1453
1454 template< template< typename > class Wrapper, typename T >
test_struct_x2_api(void)1455 void test_struct_x2_api(void)
1456 {
1457 T a = {1, 1}, b = {2, 2}, c = {3, 3};
1458
1459 test_base_operators< Wrapper >(a, b, c);
1460 }
1461
1462 struct large_struct
1463 {
1464 unsigned char data[256u];
1465
operator ==large_struct1466 inline bool operator==(large_struct const& c) const
1467 {
1468 return std::memcmp(data, &c.data, sizeof(data)) == 0;
1469 }
operator !=large_struct1470 inline bool operator!=(large_struct const& c) const
1471 {
1472 return std::memcmp(data, &c.data, sizeof(data)) != 0;
1473 }
1474 };
1475
1476 template< typename Char, typename Traits >
operator <<(std::basic_ostream<Char,Traits> & strm,large_struct const &)1477 inline std::basic_ostream< Char, Traits >& operator<< (std::basic_ostream< Char, Traits >& strm, large_struct const&)
1478 {
1479 strm << "[large_struct]";
1480 return strm;
1481 }
1482
1483 template< template< typename > class Wrapper >
test_large_struct_api(void)1484 void test_large_struct_api(void)
1485 {
1486 large_struct a = {{1}}, b = {{2}}, c = {{3}};
1487 test_base_operators< Wrapper >(a, b, c);
1488 }
1489
1490 struct test_struct_with_ctor
1491 {
1492 typedef unsigned int value_type;
1493 value_type i;
test_struct_with_ctortest_struct_with_ctor1494 test_struct_with_ctor() : i(0x01234567) {}
operator ==test_struct_with_ctor1495 inline bool operator==(test_struct_with_ctor const& c) const { return i == c.i; }
operator !=test_struct_with_ctor1496 inline bool operator!=(test_struct_with_ctor const& c) const { return !operator==(c); }
1497 };
1498
1499 template< typename Char, typename Traits >
operator <<(std::basic_ostream<Char,Traits> & strm,test_struct_with_ctor const & s)1500 inline std::basic_ostream< Char, Traits >& operator<< (std::basic_ostream< Char, Traits >& strm, test_struct_with_ctor const& s)
1501 {
1502 strm << "{" << s.i << "}";
1503 return strm;
1504 }
1505
1506 template< template< typename > class Wrapper >
test_struct_with_ctor_api(void)1507 void test_struct_with_ctor_api(void)
1508 {
1509 {
1510 test_struct_with_ctor s;
1511 Wrapper<test_struct_with_ctor> wrapper_sa;
1512 typename Wrapper<test_struct_with_ctor>::atomic_reference_type sa = wrapper_sa.a;
1513 // Check that the default constructor was called
1514 BOOST_TEST( sa.load() == s );
1515 }
1516
1517 test_struct_with_ctor a, b, c;
1518 a.i = 1;
1519 b.i = 2;
1520 c.i = 3;
1521
1522 test_base_operators< Wrapper >(a, b, c);
1523 }
1524
1525 #endif
1526