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 <iostream>
17 #include <boost/config.hpp>
18 #include <boost/cstdint.hpp>
19 #include <boost/type.hpp>
20 #include <boost/core/enable_if.hpp>
21 #include <boost/type_traits/integral_constant.hpp>
22 #include <boost/type_traits/alignment_of.hpp>
23 #include <boost/type_traits/is_pointer.hpp>
24 #include <boost/type_traits/is_signed.hpp>
25 #include <boost/type_traits/is_unsigned.hpp>
26 #include <boost/type_traits/make_signed.hpp>
27 #include <boost/type_traits/make_unsigned.hpp>
28 #include <boost/type_traits/conditional.hpp>
29
30 #include "lightweight_test_stream.hpp"
31 #include "value_with_epsilon.hpp"
32 #include "atomic_wrapper.hpp"
33
34 const unsigned int max_weak_cas_loops = 1000;
35
36 template< typename T >
37 struct is_atomic :
38 public boost::false_type
39 {
40 };
41
42 template< typename T >
43 struct is_atomic< boost::atomic< T > > :
44 public boost::true_type
45 {
46 };
47
48 template< typename T >
49 struct is_atomic< boost::ipc_atomic< T > > :
50 public boost::true_type
51 {
52 };
53
54 template< typename T >
55 struct is_atomic_ref :
56 public boost::false_type
57 {
58 };
59
60 template< typename T >
61 struct is_atomic_ref< boost::atomic_ref< T > > :
62 public boost::true_type
63 {
64 };
65
66 template< typename T >
67 struct is_atomic_ref< boost::ipc_atomic_ref< T > > :
68 public boost::true_type
69 {
70 };
71
72 template< typename Flag >
test_flag_api(void)73 inline void test_flag_api(void)
74 {
75 #ifndef BOOST_ATOMIC_NO_ATOMIC_FLAG_INIT
76 Flag f = BOOST_ATOMIC_FLAG_INIT;
77 #else
78 Flag f;
79 #endif
80
81 BOOST_TEST( !f.test() );
82 BOOST_TEST( !f.test_and_set() );
83 BOOST_TEST( f.test() );
84 BOOST_TEST( f.test_and_set() );
85 BOOST_TEST( f.test() );
86 f.clear();
87 BOOST_TEST( !f.test() );
88 BOOST_TEST( !f.test_and_set() );
89 }
90
91 template< typename T >
test_atomic_type_traits(boost::type<T>)92 inline typename boost::enable_if< is_atomic< T > >::type test_atomic_type_traits(boost::type< T >)
93 {
94 BOOST_TEST_GE(sizeof(T), sizeof(typename T::value_type));
95 }
96
97 template< typename T >
test_atomic_type_traits(boost::type<T>)98 inline typename boost::enable_if< is_atomic_ref< T > >::type test_atomic_type_traits(boost::type< T >)
99 {
100 if (T::is_always_lock_free)
101 {
102 BOOST_TEST_GE(T::required_alignment, boost::alignment_of< typename T::value_type >::value);
103 }
104 else
105 {
106 // Lock-based implementation should not require alignment higher than alignof(T)
107 BOOST_TEST_EQ(T::required_alignment, boost::alignment_of< typename T::value_type >::value);
108 }
109 }
110
111 template< template< typename > class Wrapper, typename T >
test_base_operators(T value1,T value2,T value3)112 void test_base_operators(T value1, T value2, T value3)
113 {
114 test_atomic_type_traits(boost::type< typename Wrapper<T>::atomic_type >());
115
116 // explicit load/store
117 {
118 Wrapper<T> wrapper(value1);
119 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
120 BOOST_TEST_EQ( a.load(), value1 );
121 }
122
123 {
124 Wrapper<T> wrapper(value1);
125 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
126 a.store(value2);
127 BOOST_TEST_EQ( a.load(), value2 );
128 }
129
130 // overloaded assignment/conversion
131 {
132 Wrapper<T> wrapper(value1);
133 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
134 BOOST_TEST( value1 == a );
135 }
136
137 {
138 Wrapper<T> wrapper(value1);
139 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
140 a = value2;
141 BOOST_TEST( value2 == a );
142 }
143
144 // exchange-type operators
145 {
146 Wrapper<T> wrapper(value1);
147 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
148 T n = a.exchange(value2);
149 BOOST_TEST_EQ( a.load(), value2 );
150 BOOST_TEST_EQ( n, value1 );
151 }
152
153 {
154 Wrapper<T> wrapper(value1);
155 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
156 T expected = value1;
157 bool success = a.compare_exchange_strong(expected, value3);
158 BOOST_TEST( success );
159 BOOST_TEST_EQ( a.load(), value3 );
160 BOOST_TEST_EQ( expected, value1 );
161 }
162
163 {
164 Wrapper<T> wrapper(value1);
165 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
166 T expected = value2;
167 bool success = a.compare_exchange_strong(expected, value3);
168 BOOST_TEST( !success );
169 BOOST_TEST_EQ( a.load(), value1 );
170 BOOST_TEST_EQ( expected, value1 );
171 }
172
173 {
174 Wrapper<T> wrapper(value1);
175 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
176 T expected;
177 unsigned int loops = 0;
178 bool success = false;
179 do
180 {
181 expected = value1;
182 success = a.compare_exchange_weak(expected, value3);
183 ++loops;
184 }
185 while (!success && loops < max_weak_cas_loops);
186 BOOST_TEST( success );
187 BOOST_TEST_EQ( a.load(), value3 );
188 BOOST_TEST_EQ( expected, value1 );
189 }
190
191 {
192 Wrapper<T> wrapper(value1);
193 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
194 T expected;
195 unsigned int loops = 0;
196 bool success = false;
197 do
198 {
199 expected = value2;
200 success = a.compare_exchange_weak(expected, value3);
201 if (expected != value2)
202 break;
203 ++loops;
204 }
205 while (!success && loops < max_weak_cas_loops);
206 BOOST_TEST( !success );
207 BOOST_TEST_EQ( a.load(), value1 );
208 BOOST_TEST_EQ( expected, value1 );
209 }
210 }
211
212 //! Tests whether boost::atomic supports constexpr constructor. Note that boost::atomic_ref (as std::atomic_ref) does not support constexpr constructor.
213 template< typename T >
test_constexpr_ctor()214 void test_constexpr_ctor()
215 {
216 #ifndef BOOST_ATOMIC_DETAIL_NO_CXX11_CONSTEXPR_UNION_INIT
217 constexpr T value(0);
218 constexpr boost::atomic<T> tester(value);
219 BOOST_TEST( tester == value );
220 #endif
221 }
222
223 //! The type traits provides max and min values of type D that can be added/subtracted to T(0) without signed overflow
224 template< typename T, typename D, bool IsSigned = boost::is_signed< D >::value >
225 struct distance_limits
226 {
227 //! Difference type D promoted to the width of type T
228 typedef typename boost::conditional<
229 IsSigned,
230 boost::make_signed< T >,
231 boost::make_unsigned< T >
232 >::type::type promoted_difference_type;
233
BOOST_PREVENT_MACRO_SUBSTITUTIONdistance_limits234 static D min BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
235 {
236 return (std::numeric_limits< D >::min)();
237 }
BOOST_PREVENT_MACRO_SUBSTITUTIONdistance_limits238 static D max BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
239 {
240 return (std::numeric_limits< D >::max)();
241 }
242 };
243
244 #if defined(BOOST_MSVC)
245 #pragma warning(push)
246 // 'static_cast': truncation of constant value. There is no actual truncation happening because
247 // the cast is only performed if the value fits in the range of the result.
248 #pragma warning(disable: 4309)
249 #endif
250
251 template< typename T, typename D >
252 struct distance_limits< T*, D, true >
253 {
254 //! Difference type D promoted to the width of type T
255 typedef std::ptrdiff_t promoted_difference_type;
256
BOOST_PREVENT_MACRO_SUBSTITUTIONdistance_limits257 static D min BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
258 {
259 const std::ptrdiff_t ptrdiff = (std::numeric_limits< std::ptrdiff_t >::min)() / static_cast< std::ptrdiff_t >(sizeof(T));
260 const D diff = (std::numeric_limits< D >::min)();
261 // Both values are negative. Return the closest value to zero.
262 return diff < ptrdiff ? static_cast< D >(ptrdiff) : diff;
263 }
BOOST_PREVENT_MACRO_SUBSTITUTIONdistance_limits264 static D max BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
265 {
266 const std::ptrdiff_t ptrdiff = (std::numeric_limits< std::ptrdiff_t >::max)() / static_cast< std::ptrdiff_t >(sizeof(T));
267 const D diff = (std::numeric_limits< D >::max)();
268 // Both values are positive. Return the closest value to zero.
269 return diff > ptrdiff ? static_cast< D >(ptrdiff) : diff;
270 }
271 };
272
273 template< typename T, typename D >
274 struct distance_limits< T*, D, false >
275 {
276 //! Difference type D promoted to the width of type T
277 typedef std::size_t promoted_difference_type;
278
BOOST_PREVENT_MACRO_SUBSTITUTIONdistance_limits279 static D min BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
280 {
281 return (std::numeric_limits< D >::min)();
282 }
BOOST_PREVENT_MACRO_SUBSTITUTIONdistance_limits283 static D max BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
284 {
285 const std::size_t ptrdiff = static_cast< std::size_t >((std::numeric_limits< std::ptrdiff_t >::max)()) / sizeof(T);
286 const D diff = (std::numeric_limits< D >::max)();
287 return diff > ptrdiff ? static_cast< D >(ptrdiff) : diff;
288 }
289 };
290
291 #if defined(BOOST_HAS_INT128)
292
293 // At least libstdc++ does not specialize std::numeric_limits for __int128 in strict mode (i.e. with GNU extensions disabled).
294 // So we have to specialize the limits ourself. We assume two's complement signed representation.
295 template< typename T, bool IsSigned >
296 struct distance_limits< T, boost::int128_type, IsSigned >
297 {
298 //! Difference type D promoted to the width of type T
299 typedef boost::int128_type promoted_difference_type;
300
BOOST_PREVENT_MACRO_SUBSTITUTIONdistance_limits301 static boost::int128_type min BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
302 {
303 return -(max)() - 1;
304 }
BOOST_PREVENT_MACRO_SUBSTITUTIONdistance_limits305 static boost::int128_type max BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
306 {
307 return static_cast< boost::int128_type >((~static_cast< boost::uint128_type >(0u)) >> 1);
308 }
309 };
310
311 template< typename T, bool IsSigned >
312 struct distance_limits< T, boost::uint128_type, IsSigned >
313 {
314 //! Difference type D promoted to the width of type T
315 typedef boost::uint128_type promoted_difference_type;
316
BOOST_PREVENT_MACRO_SUBSTITUTIONdistance_limits317 static boost::uint128_type min BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
318 {
319 return 0u;
320 }
BOOST_PREVENT_MACRO_SUBSTITUTIONdistance_limits321 static boost::uint128_type max BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT
322 {
323 return ~static_cast< boost::uint128_type >(0u);
324 }
325 };
326
327 #endif // defined(BOOST_HAS_INT128)
328
329 #if defined(BOOST_MSVC)
330 #pragma warning(pop)
331 #endif
332
333 #if defined(BOOST_MSVC)
334 #pragma warning(push)
335 // unary minus operator applied to unsigned type, result still unsigned
336 #pragma warning(disable: 4146)
337 #endif
338
339 template< template< typename > class Wrapper, typename T, typename D, typename AddType >
test_additive_operators_with_type_and_test()340 void test_additive_operators_with_type_and_test()
341 {
342 #if defined(UBSAN)
343 // clang UBSAN flags this test when AddType is a pointer as it considers subtracting from a null pointer (zero_add) an UB
344 if (boost::is_pointer< AddType >::value)
345 return;
346 #endif
347
348 // Note: This set of tests is extracted to a separate function because otherwise MSVC-10 for x64 generates broken code
349 typedef typename distance_limits< T, D >::promoted_difference_type promoted_difference_type;
350 typedef typename boost::make_unsigned< promoted_difference_type >::type unsigned_promoted_difference_type;
351 const T zero_value = 0;
352 const D zero_diff = 0;
353 const D one_diff = 1;
354 const AddType zero_add = 0;
355 {
356 Wrapper<T> wrapper(zero_value);
357 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
358 bool f = a.add_and_test(zero_diff);
359 BOOST_TEST_EQ( f, false );
360 BOOST_TEST_EQ( a.load(), zero_value );
361
362 f = a.add_and_test(one_diff);
363 BOOST_TEST_EQ( f, true );
364 BOOST_TEST_EQ( a.load(), T(zero_add + one_diff) );
365 }
366 {
367 Wrapper<T> wrapper(zero_value);
368 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
369 bool f = a.add_and_test((distance_limits< T, D >::max)());
370 BOOST_TEST_EQ( f, true );
371 BOOST_TEST_EQ( a.load(), T(zero_add + (distance_limits< T, D >::max)()) );
372 }
373 {
374 Wrapper<T> wrapper(zero_value);
375 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
376 bool f = a.add_and_test((distance_limits< T, D >::min)());
377 BOOST_TEST_EQ( f, ((distance_limits< T, D >::min)() != 0) );
378 BOOST_TEST_EQ( a.load(), T(zero_add + (distance_limits< T, D >::min)()) );
379 }
380
381 {
382 Wrapper<T> wrapper(zero_value);
383 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
384 bool f = a.sub_and_test(zero_diff);
385 BOOST_TEST_EQ( f, false );
386 BOOST_TEST_EQ( a.load(), zero_value );
387
388 f = a.sub_and_test(one_diff);
389 BOOST_TEST_EQ( f, true );
390 BOOST_TEST_EQ( a.load(), T(zero_add - one_diff) );
391 }
392 {
393 Wrapper<T> wrapper(zero_value);
394 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
395 bool f = a.sub_and_test((distance_limits< T, D >::max)());
396 BOOST_TEST_EQ( f, true );
397 BOOST_TEST_EQ( a.load(), T(zero_add - (distance_limits< T, D >::max)()) );
398 }
399 {
400 Wrapper<T> wrapper(zero_value);
401 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
402 bool f = a.sub_and_test((distance_limits< T, D >::min)());
403 BOOST_TEST_EQ( f, ((distance_limits< T, D >::min)() != 0) );
404 // Be very careful as to not cause signed overflow on negation
405 unsigned_promoted_difference_type umin = static_cast< unsigned_promoted_difference_type >(
406 static_cast< promoted_difference_type >((distance_limits< T, D >::min)()));
407 umin = -umin;
408 promoted_difference_type neg_min;
409 std::memcpy(&neg_min, &umin, sizeof(neg_min));
410 BOOST_TEST_EQ( a.load(), T(zero_add + neg_min) );
411 }
412 }
413
414 #if defined(BOOST_MSVC)
415 #pragma warning(pop)
416 #endif
417
418 template< template< typename > class Wrapper, typename T, typename D, typename AddType >
test_additive_operators_with_type(T value,D delta)419 void test_additive_operators_with_type(T value, D delta)
420 {
421 /* note: the tests explicitly cast the result of any addition
422 to the type to be tested to force truncation of the result to
423 the correct range in case of overflow */
424
425 // explicit add/sub
426 {
427 Wrapper<T> wrapper(value);
428 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
429 T n = a.fetch_add(delta);
430 BOOST_TEST_EQ( a.load(), T((AddType)value + delta) );
431 BOOST_TEST_EQ( n, value );
432 }
433
434 {
435 Wrapper<T> wrapper(value);
436 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
437 T n = a.fetch_sub(delta);
438 BOOST_TEST_EQ( a.load(), T((AddType)value - delta) );
439 BOOST_TEST_EQ( n, value );
440 }
441
442 // overloaded modify/assign
443 {
444 Wrapper<T> wrapper(value);
445 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
446 T n = (a += delta);
447 BOOST_TEST_EQ( a.load(), T((AddType)value + delta) );
448 BOOST_TEST_EQ( n, T((AddType)value + delta) );
449 }
450
451 {
452 Wrapper<T> wrapper(value);
453 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
454 T n = (a -= delta);
455 BOOST_TEST_EQ( a.load(), T((AddType)value - delta) );
456 BOOST_TEST_EQ( n, T((AddType)value - delta) );
457 }
458
459 // overloaded increment/decrement
460 {
461 Wrapper<T> wrapper(value);
462 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
463 T n = a++;
464 BOOST_TEST_EQ( a.load(), T((AddType)value + 1) );
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;
472 BOOST_TEST_EQ( a.load(), T((AddType)value + 1) );
473 BOOST_TEST_EQ( n, T((AddType)value + 1) );
474 }
475
476 {
477 Wrapper<T> wrapper(value);
478 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
479 T n = a--;
480 BOOST_TEST_EQ( a.load(), T((AddType)value - 1) );
481 BOOST_TEST_EQ( n, value );
482 }
483
484 {
485 Wrapper<T> wrapper(value);
486 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
487 T n = --a;
488 BOOST_TEST_EQ( a.load(), T((AddType)value - 1) );
489 BOOST_TEST_EQ( n, T((AddType)value - 1) );
490 }
491
492 // Operations returning the actual resulting value
493 {
494 Wrapper<T> wrapper(value);
495 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
496 T n = a.add(delta);
497 BOOST_TEST_EQ( a.load(), T((AddType)value + delta) );
498 BOOST_TEST_EQ( n, T((AddType)value + delta) );
499 }
500
501 {
502 Wrapper<T> wrapper(value);
503 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
504 T n = a.sub(delta);
505 BOOST_TEST_EQ( a.load(), T((AddType)value - delta) );
506 BOOST_TEST_EQ( n, T((AddType)value - delta) );
507 }
508
509 // Opaque operations
510 {
511 Wrapper<T> wrapper(value);
512 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
513 a.opaque_add(delta);
514 BOOST_TEST_EQ( a.load(), T((AddType)value + delta) );
515 }
516
517 {
518 Wrapper<T> wrapper(value);
519 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
520 a.opaque_sub(delta);
521 BOOST_TEST_EQ( a.load(), T((AddType)value - delta) );
522 }
523
524 // Modify and test operations
525 test_additive_operators_with_type_and_test< Wrapper, T, D, AddType >();
526 }
527
528 template< template< typename > class Wrapper, typename T, typename D >
test_additive_operators(T value,D delta)529 void test_additive_operators(T value, D delta)
530 {
531 test_additive_operators_with_type< Wrapper, T, D, T >(value, delta);
532 }
533
534 template< template< typename > class Wrapper, typename T >
test_negation()535 void test_negation()
536 {
537 {
538 Wrapper<T> wrapper((T)1);
539 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
540 T n = a.fetch_negate();
541 BOOST_TEST_EQ( a.load(), (T)-1 );
542 BOOST_TEST_EQ( n, (T)1 );
543
544 n = a.fetch_negate();
545 BOOST_TEST_EQ( a.load(), (T)1 );
546 BOOST_TEST_EQ( n, (T)-1 );
547 }
548 {
549 Wrapper<T> wrapper((T)1);
550 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
551 T n = a.negate();
552 BOOST_TEST_EQ( a.load(), (T)-1 );
553 BOOST_TEST_EQ( n, (T)-1 );
554
555 n = a.negate();
556 BOOST_TEST_EQ( a.load(), (T)1 );
557 BOOST_TEST_EQ( n, (T)1 );
558 }
559 {
560 Wrapper<T> wrapper((T)1);
561 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
562 a.opaque_negate();
563 BOOST_TEST_EQ( a.load(), (T)-1 );
564
565 a.opaque_negate();
566 BOOST_TEST_EQ( a.load(), (T)1 );
567 }
568 {
569 Wrapper<T> wrapper((T)1);
570 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
571 bool f = a.negate_and_test();
572 BOOST_TEST_EQ( f, true );
573 BOOST_TEST_EQ( a.load(), (T)-1 );
574
575 f = a.negate_and_test();
576 BOOST_TEST_EQ( f, true );
577 BOOST_TEST_EQ( a.load(), (T)1 );
578 }
579 {
580 Wrapper<T> wrapper((T)0);
581 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
582 bool f = a.negate_and_test();
583 BOOST_TEST_EQ( f, false );
584 BOOST_TEST_EQ( a.load(), (T)0 );
585 }
586 }
587
588 template< template< typename > class Wrapper, typename T >
test_additive_wrap(T value)589 void test_additive_wrap(T value)
590 {
591 {
592 Wrapper<T> wrapper(value);
593 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
594 T n = a.fetch_add(1) + (T)1;
595 BOOST_TEST_EQ( a.load(), n );
596 }
597 {
598 Wrapper<T> wrapper(value);
599 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
600 T n = a.fetch_sub(1) - (T)1;
601 BOOST_TEST_EQ( a.load(), n );
602 }
603 }
604
605 template< template< typename > class Wrapper, typename T >
test_bit_operators(T value,T delta)606 void test_bit_operators(T value, T delta)
607 {
608 // explicit and/or/xor
609 {
610 Wrapper<T> wrapper(value);
611 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
612 T n = a.fetch_and(delta);
613 BOOST_TEST_EQ( a.load(), T(value & delta) );
614 BOOST_TEST_EQ( n, value );
615 }
616
617 {
618 Wrapper<T> wrapper(value);
619 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
620 T n = a.fetch_or(delta);
621 BOOST_TEST_EQ( a.load(), T(value | delta) );
622 BOOST_TEST_EQ( n, value );
623 }
624
625 {
626 Wrapper<T> wrapper(value);
627 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
628 T n = a.fetch_xor(delta);
629 BOOST_TEST_EQ( a.load(), T(value ^ delta) );
630 BOOST_TEST_EQ( n, value );
631 }
632
633 {
634 Wrapper<T> wrapper(value);
635 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
636 T n = a.fetch_complement();
637 BOOST_TEST_EQ( a.load(), T(~value) );
638 BOOST_TEST_EQ( n, value );
639 }
640
641 // overloaded modify/assign
642 {
643 Wrapper<T> wrapper(value);
644 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
645 T n = (a &= delta);
646 BOOST_TEST_EQ( a.load(), T(value & delta) );
647 BOOST_TEST_EQ( n, T(value & delta) );
648 }
649
650 {
651 Wrapper<T> wrapper(value);
652 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
653 T n = (a |= delta);
654 BOOST_TEST_EQ( a.load(), T(value | delta) );
655 BOOST_TEST_EQ( n, T(value | delta) );
656 }
657
658 {
659 Wrapper<T> wrapper(value);
660 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
661 T n = (a ^= delta);
662 BOOST_TEST_EQ( a.load(), T(value ^ delta) );
663 BOOST_TEST_EQ( n, T(value ^ delta) );
664 }
665
666 // Operations returning the actual resulting value
667 {
668 Wrapper<T> wrapper(value);
669 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
670 T n = a.bitwise_and(delta);
671 BOOST_TEST_EQ( a.load(), T(value & delta) );
672 BOOST_TEST_EQ( n, T(value & delta) );
673 }
674
675 {
676 Wrapper<T> wrapper(value);
677 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
678 T n = a.bitwise_or(delta);
679 BOOST_TEST_EQ( a.load(), T(value | delta) );
680 BOOST_TEST_EQ( n, T(value | delta) );
681 }
682
683 {
684 Wrapper<T> wrapper(value);
685 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
686 T n = a.bitwise_xor(delta);
687 BOOST_TEST_EQ( a.load(), T(value ^ delta) );
688 BOOST_TEST_EQ( n, T(value ^ delta) );
689 }
690
691 {
692 Wrapper<T> wrapper(value);
693 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
694 T n = a.bitwise_complement();
695 BOOST_TEST_EQ( a.load(), T(~value) );
696 BOOST_TEST_EQ( n, T(~value) );
697 }
698
699 // Opaque operations
700 {
701 Wrapper<T> wrapper(value);
702 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
703 a.opaque_and(delta);
704 BOOST_TEST_EQ( a.load(), T(value & delta) );
705 }
706
707 {
708 Wrapper<T> wrapper(value);
709 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
710 a.opaque_or(delta);
711 BOOST_TEST_EQ( a.load(), T(value | delta) );
712 }
713
714 {
715 Wrapper<T> wrapper(value);
716 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
717 a.opaque_xor(delta);
718 BOOST_TEST_EQ( a.load(), T(value ^ delta) );
719 }
720
721 {
722 Wrapper<T> wrapper(value);
723 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
724 a.opaque_complement();
725 BOOST_TEST_EQ( a.load(), T(~value) );
726 }
727
728 // Modify and test operations
729 {
730 Wrapper<T> wrapper((T)1);
731 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
732 bool f = a.and_and_test((T)1);
733 BOOST_TEST_EQ( f, true );
734 BOOST_TEST_EQ( a.load(), T(1) );
735
736 f = a.and_and_test((T)0);
737 BOOST_TEST_EQ( f, false );
738 BOOST_TEST_EQ( a.load(), T(0) );
739
740 f = a.and_and_test((T)0);
741 BOOST_TEST_EQ( f, false );
742 BOOST_TEST_EQ( a.load(), T(0) );
743 }
744
745 {
746 Wrapper<T> wrapper((T)0);
747 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
748 bool f = a.or_and_test((T)0);
749 BOOST_TEST_EQ( f, false );
750 BOOST_TEST_EQ( a.load(), T(0) );
751
752 f = a.or_and_test((T)1);
753 BOOST_TEST_EQ( f, true );
754 BOOST_TEST_EQ( a.load(), T(1) );
755
756 f = a.or_and_test((T)1);
757 BOOST_TEST_EQ( f, true );
758 BOOST_TEST_EQ( a.load(), T(1) );
759 }
760
761 {
762 Wrapper<T> wrapper((T)0);
763 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
764 bool f = a.xor_and_test((T)0);
765 BOOST_TEST_EQ( f, false );
766 BOOST_TEST_EQ( a.load(), T(0) );
767
768 f = a.xor_and_test((T)1);
769 BOOST_TEST_EQ( f, true );
770 BOOST_TEST_EQ( a.load(), T(1) );
771
772 f = a.xor_and_test((T)1);
773 BOOST_TEST_EQ( f, false );
774 BOOST_TEST_EQ( a.load(), T(0) );
775 }
776
777 {
778 Wrapper<T> wrapper((T)0);
779 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
780 bool f = a.complement_and_test();
781 BOOST_TEST_EQ( f, true );
782 BOOST_TEST_EQ( a.load(), static_cast< T >(~static_cast< T >(0)) );
783
784 f = a.complement_and_test();
785 BOOST_TEST_EQ( f, false );
786 BOOST_TEST_EQ( a.load(), T(0) );
787 }
788
789 // Bit test and modify operations
790 {
791 Wrapper<T> wrapper((T)42);
792 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
793 bool f = a.bit_test_and_set(0);
794 BOOST_TEST_EQ( f, false );
795 BOOST_TEST_EQ( a.load(), T(43) );
796
797 f = a.bit_test_and_set(1);
798 BOOST_TEST_EQ( f, true );
799 BOOST_TEST_EQ( a.load(), T(43) );
800
801 f = a.bit_test_and_set(2);
802 BOOST_TEST_EQ( f, false );
803 BOOST_TEST_EQ( a.load(), T(47) );
804 }
805
806 {
807 Wrapper<T> wrapper((T)42);
808 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
809 bool f = a.bit_test_and_reset(0);
810 BOOST_TEST_EQ( f, false );
811 BOOST_TEST_EQ( a.load(), T(42) );
812
813 f = a.bit_test_and_reset(1);
814 BOOST_TEST_EQ( f, true );
815 BOOST_TEST_EQ( a.load(), T(40) );
816
817 f = a.bit_test_and_set(2);
818 BOOST_TEST_EQ( f, false );
819 BOOST_TEST_EQ( a.load(), T(44) );
820 }
821
822 {
823 Wrapper<T> wrapper((T)42);
824 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
825 bool f = a.bit_test_and_complement(0);
826 BOOST_TEST_EQ( f, false );
827 BOOST_TEST_EQ( a.load(), T(43) );
828
829 f = a.bit_test_and_complement(1);
830 BOOST_TEST_EQ( f, true );
831 BOOST_TEST_EQ( a.load(), T(41) );
832
833 f = a.bit_test_and_complement(2);
834 BOOST_TEST_EQ( f, false );
835 BOOST_TEST_EQ( a.load(), T(45) );
836 }
837
838 // Test that a runtime value works for the bit index. This is important for asm block constraints.
839 {
840 unsigned int runtime_bit_index = std::rand() & 7u;
841 Wrapper<T> wrapper((T)42);
842 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
843
844 a.bit_test_and_set(runtime_bit_index);
845 a.bit_test_and_reset(runtime_bit_index);
846 a.bit_test_and_complement(runtime_bit_index);
847 }
848 }
849
850 template< template< typename > class Wrapper, typename T >
do_test_integral_api(boost::false_type)851 void do_test_integral_api(boost::false_type)
852 {
853 test_base_operators< Wrapper, T >(42, 43, 44);
854 test_additive_operators< Wrapper, T, T >(42, 17);
855 test_bit_operators< Wrapper, T >((T)0x5f5f5f5f5f5f5f5fULL, (T)0xf5f5f5f5f5f5f5f5ULL);
856
857 /* test for unsigned overflow/underflow */
858 test_additive_operators< Wrapper, T, T >((T)-1, 1);
859 test_additive_operators< Wrapper, T, T >(0, 1);
860 /* test for signed overflow/underflow */
861 test_additive_operators< Wrapper, T, T >(((T)-1) >> (sizeof(T) * 8 - 1), 1);
862 test_additive_operators< Wrapper, T, T >(1 + (((T)-1) >> (sizeof(T) * 8 - 1)), 1);
863 }
864
865 template< template< typename > class Wrapper, typename T >
do_test_integral_api(boost::true_type)866 void do_test_integral_api(boost::true_type)
867 {
868 do_test_integral_api< Wrapper, T >(boost::false_type());
869
870 test_additive_wrap< Wrapper, T >(0u);
871 BOOST_CONSTEXPR_OR_CONST T all_ones = ~(T)0u;
872 test_additive_wrap< Wrapper, T >(all_ones);
873 BOOST_CONSTEXPR_OR_CONST T max_signed_twos_compl = all_ones >> 1;
874 test_additive_wrap< Wrapper, T >(all_ones ^ max_signed_twos_compl);
875 test_additive_wrap< Wrapper, T >(max_signed_twos_compl);
876 }
877
878 template< template< typename > class Wrapper, typename T >
test_integral_api(void)879 inline void test_integral_api(void)
880 {
881 do_test_integral_api< Wrapper, T >(boost::is_unsigned<T>());
882
883 if (boost::is_signed<T>::value)
884 test_negation< Wrapper, T >();
885 }
886
887 template< template< typename > class Wrapper, typename T >
test_lock_free_integral_api(boost::true_type)888 inline void test_lock_free_integral_api(boost::true_type)
889 {
890 test_integral_api< Wrapper, T >();
891 }
892
893 template< template< typename > class Wrapper, typename T >
test_lock_free_integral_api(boost::false_type)894 inline void test_lock_free_integral_api(boost::false_type)
895 {
896 }
897
898 template< template< typename > class Wrapper, typename T >
test_lock_free_integral_api(void)899 inline void test_lock_free_integral_api(void)
900 {
901 test_lock_free_integral_api< Wrapper, T >(boost::integral_constant< bool, Wrapper< T >::atomic_type::is_always_lock_free >());
902 }
903
904 #if !defined(BOOST_ATOMIC_NO_FLOATING_POINT)
905
906 template< template< typename > class Wrapper, typename T, typename D >
test_fp_additive_operators(T value,D delta)907 void test_fp_additive_operators(T value, D delta)
908 {
909 // explicit add/sub
910 {
911 Wrapper<T> wrapper(value);
912 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
913 T n = a.fetch_add(delta);
914 BOOST_TEST_EQ( a.load(), approx(T(value + delta)) );
915 BOOST_TEST_EQ( n, approx(value) );
916 }
917
918 {
919 Wrapper<T> wrapper(value);
920 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
921 T n = a.fetch_sub(delta);
922 BOOST_TEST_EQ( a.load(), approx(T(value - delta)) );
923 BOOST_TEST_EQ( n, approx(value) );
924 }
925
926 // overloaded modify/assign
927 {
928 Wrapper<T> wrapper(value);
929 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
930 T n = (a += delta);
931 BOOST_TEST_EQ( a.load(), approx(T(value + delta)) );
932 BOOST_TEST_EQ( n, approx(T(value + delta)) );
933 }
934
935 {
936 Wrapper<T> wrapper(value);
937 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
938 T n = (a -= delta);
939 BOOST_TEST_EQ( a.load(), approx(T(value - delta)) );
940 BOOST_TEST_EQ( n, approx(T(value - delta)) );
941 }
942
943 // Operations returning the actual resulting value
944 {
945 Wrapper<T> wrapper(value);
946 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
947 T n = a.add(delta);
948 BOOST_TEST_EQ( a.load(), approx(T(value + delta)) );
949 BOOST_TEST_EQ( n, approx(T(value + delta)) );
950 }
951
952 {
953 Wrapper<T> wrapper(value);
954 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
955 T n = a.sub(delta);
956 BOOST_TEST_EQ( a.load(), approx(T(value - delta)) );
957 BOOST_TEST_EQ( n, approx(T(value - delta)) );
958 }
959
960 // Opaque operations
961 {
962 Wrapper<T> wrapper(value);
963 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
964 a.opaque_add(delta);
965 BOOST_TEST_EQ( a.load(), approx(T(value + delta)) );
966 }
967
968 {
969 Wrapper<T> wrapper(value);
970 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
971 a.opaque_sub(delta);
972 BOOST_TEST_EQ( a.load(), approx(T(value - delta)) );
973 }
974 }
975
976 template< template< typename > class Wrapper, typename T >
test_fp_negation()977 void test_fp_negation()
978 {
979 {
980 Wrapper<T> wrapper((T)1);
981 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
982 T n = a.fetch_negate();
983 BOOST_TEST_EQ( a.load(), approx((T)-1) );
984 BOOST_TEST_EQ( n, approx((T)1) );
985
986 n = a.fetch_negate();
987 BOOST_TEST_EQ( a.load(), approx((T)1) );
988 BOOST_TEST_EQ( n, approx((T)-1) );
989 }
990 {
991 Wrapper<T> wrapper((T)1);
992 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
993 T n = a.negate();
994 BOOST_TEST_EQ( a.load(), approx((T)-1) );
995 BOOST_TEST_EQ( n, approx((T)-1) );
996
997 n = a.negate();
998 BOOST_TEST_EQ( a.load(), approx((T)1) );
999 BOOST_TEST_EQ( n, approx((T)1) );
1000 }
1001 {
1002 Wrapper<T> wrapper((T)1);
1003 typename Wrapper<T>::atomic_reference_type a = wrapper.a;
1004 a.opaque_negate();
1005 BOOST_TEST_EQ( a.load(), approx((T)-1) );
1006
1007 a.opaque_negate();
1008 BOOST_TEST_EQ( a.load(), approx((T)1) );
1009 }
1010 }
1011
1012 #endif // !defined(BOOST_ATOMIC_NO_FLOATING_POINT)
1013
1014 template< template< typename > class Wrapper, typename T >
test_floating_point_api(void)1015 void test_floating_point_api(void)
1016 {
1017 // Note: When support for floating point is disabled, even the base operation tests may fail because
1018 // the generic template specialization does not account for garbage in padding bits that are present in some FP types.
1019 #if !defined(BOOST_ATOMIC_NO_FLOATING_POINT)
1020 test_base_operators< Wrapper, T >(static_cast<T>(42.1), static_cast<T>(43.2), static_cast<T>(44.3));
1021
1022 test_fp_additive_operators< Wrapper, T, T >(static_cast<T>(42.5), static_cast<T>(17.7));
1023 test_fp_additive_operators< Wrapper, T, T >(static_cast<T>(-42.5), static_cast<T>(-17.7));
1024
1025 test_fp_negation< Wrapper, T >();
1026 #endif
1027 }
1028
1029 template< template< typename > class Wrapper, typename T >
test_lock_free_floating_point_api(boost::true_type)1030 inline void test_lock_free_floating_point_api(boost::true_type)
1031 {
1032 test_floating_point_api< Wrapper, T >();
1033 }
1034
1035 template< template< typename > class Wrapper, typename T >
test_lock_free_floating_point_api(boost::false_type)1036 inline void test_lock_free_floating_point_api(boost::false_type)
1037 {
1038 }
1039
1040 template< template< typename > class Wrapper, typename T >
test_lock_free_floating_point_api(void)1041 inline void test_lock_free_floating_point_api(void)
1042 {
1043 test_lock_free_floating_point_api< Wrapper, T >(boost::integral_constant< bool, Wrapper< T >::atomic_type::is_always_lock_free >());
1044 }
1045
1046
1047 template< template< typename > class Wrapper, typename T >
test_pointer_api(void)1048 void test_pointer_api(void)
1049 {
1050 T values[3];
1051
1052 test_base_operators< Wrapper, T* >(&values[0], &values[1], &values[2]);
1053 test_additive_operators< Wrapper, T*>(&values[1], 1);
1054
1055 test_base_operators< Wrapper, void*>(&values[0], &values[1], &values[2]);
1056
1057 #if defined(BOOST_HAS_INTPTR_T)
1058 Wrapper<void*> wrapper_ptr;
1059 typename Wrapper<void*>::atomic_reference_type ptr = wrapper_ptr.a;
1060 Wrapper<boost::intptr_t> wrapper_integral;
1061 typename Wrapper<boost::intptr_t>::atomic_reference_type integral = wrapper_integral.a;
1062 BOOST_TEST_EQ( ptr.is_lock_free(), integral.is_lock_free() );
1063 #endif
1064 }
1065
1066 enum test_enum
1067 {
1068 foo, bar, baz
1069 };
1070
1071 template< template< typename > class Wrapper, typename T >
test_lock_free_pointer_api(boost::true_type)1072 inline void test_lock_free_pointer_api(boost::true_type)
1073 {
1074 test_pointer_api< Wrapper, T >();
1075 }
1076
1077 template< template< typename > class Wrapper, typename T >
test_lock_free_pointer_api(boost::false_type)1078 inline void test_lock_free_pointer_api(boost::false_type)
1079 {
1080 }
1081
1082 template< template< typename > class Wrapper, typename T >
test_lock_free_pointer_api(void)1083 inline void test_lock_free_pointer_api(void)
1084 {
1085 test_lock_free_pointer_api< Wrapper, T >(boost::integral_constant< bool, Wrapper< T >::atomic_type::is_always_lock_free >());
1086 }
1087
1088
1089 template< template< typename > class Wrapper >
test_enum_api(void)1090 void test_enum_api(void)
1091 {
1092 test_base_operators< Wrapper >(foo, bar, baz);
1093 }
1094
1095 template< template< typename > class Wrapper >
test_lock_free_enum_api(boost::true_type)1096 inline void test_lock_free_enum_api(boost::true_type)
1097 {
1098 test_enum_api< Wrapper >();
1099 }
1100
1101 template< template< typename > class Wrapper >
test_lock_free_enum_api(boost::false_type)1102 inline void test_lock_free_enum_api(boost::false_type)
1103 {
1104 }
1105
1106 template< template< typename > class Wrapper >
test_lock_free_enum_api(void)1107 inline void test_lock_free_enum_api(void)
1108 {
1109 test_lock_free_enum_api< Wrapper >(boost::integral_constant< bool, Wrapper< test_enum >::atomic_type::is_always_lock_free >());
1110 }
1111
1112
1113 template< typename T >
1114 struct test_struct
1115 {
1116 typedef T value_type;
1117 value_type i;
operator ==test_struct1118 inline bool operator==(test_struct const& c) const { return i == c.i; }
operator !=test_struct1119 inline bool operator!=(test_struct const& c) const { return !operator==(c); }
1120 };
1121
1122 template< typename Char, typename Traits, typename T >
operator <<(std::basic_ostream<Char,Traits> & strm,test_struct<T> const & s)1123 inline std::basic_ostream< Char, Traits >& operator<< (std::basic_ostream< Char, Traits >& strm, test_struct< T > const& s)
1124 {
1125 test_stream << "{" << s.i << "}";
1126 return strm;
1127 }
1128
1129 template< template< typename > class Wrapper, typename T >
test_struct_api(void)1130 void test_struct_api(void)
1131 {
1132 T a = {1}, b = {2}, c = {3};
1133
1134 test_base_operators< Wrapper >(a, b, c);
1135
1136 {
1137 Wrapper<T> wrapper_sa;
1138 typename Wrapper<T>::atomic_reference_type sa = wrapper_sa.a;
1139 Wrapper<typename T::value_type> wrapper_si;
1140 typename Wrapper<typename T::value_type>::atomic_reference_type si = wrapper_si.a;
1141 BOOST_TEST_EQ( sa.is_lock_free(), si.is_lock_free() );
1142 }
1143 }
1144
1145 template< typename T >
1146 struct test_struct_x2
1147 {
1148 typedef T value_type;
1149 value_type i, j;
operator ==test_struct_x21150 inline bool operator==(test_struct_x2 const& c) const { return i == c.i && j == c.j; }
operator !=test_struct_x21151 inline bool operator!=(test_struct_x2 const& c) const { return !operator==(c); }
1152 };
1153
1154 template< typename Char, typename Traits, typename T >
operator <<(std::basic_ostream<Char,Traits> & strm,test_struct_x2<T> const & s)1155 inline std::basic_ostream< Char, Traits >& operator<< (std::basic_ostream< Char, Traits >& strm, test_struct_x2< T > const& s)
1156 {
1157 test_stream << "{" << s.i << ", " << s.j << "}";
1158 return strm;
1159 }
1160
1161 template< template< typename > class Wrapper, typename T >
test_struct_x2_api(void)1162 void test_struct_x2_api(void)
1163 {
1164 T a = {1, 1}, b = {2, 2}, c = {3, 3};
1165
1166 test_base_operators< Wrapper >(a, b, c);
1167 }
1168
1169 struct large_struct
1170 {
1171 unsigned char data[256u];
1172
operator ==large_struct1173 inline bool operator==(large_struct const& c) const
1174 {
1175 return std::memcmp(data, &c.data, sizeof(data)) == 0;
1176 }
operator !=large_struct1177 inline bool operator!=(large_struct const& c) const
1178 {
1179 return std::memcmp(data, &c.data, sizeof(data)) != 0;
1180 }
1181 };
1182
1183 template< typename Char, typename Traits >
operator <<(std::basic_ostream<Char,Traits> & strm,large_struct const &)1184 inline std::basic_ostream< Char, Traits >& operator<< (std::basic_ostream< Char, Traits >& strm, large_struct const&)
1185 {
1186 strm << "[large_struct]";
1187 return strm;
1188 }
1189
1190 template< template< typename > class Wrapper >
test_large_struct_api(void)1191 void test_large_struct_api(void)
1192 {
1193 large_struct a = {{1}}, b = {{2}}, c = {{3}};
1194 test_base_operators< Wrapper >(a, b, c);
1195 }
1196
1197 struct test_struct_with_ctor
1198 {
1199 typedef unsigned int value_type;
1200 value_type i;
test_struct_with_ctortest_struct_with_ctor1201 test_struct_with_ctor() : i(0x01234567) {}
operator ==test_struct_with_ctor1202 inline bool operator==(test_struct_with_ctor const& c) const { return i == c.i; }
operator !=test_struct_with_ctor1203 inline bool operator!=(test_struct_with_ctor const& c) const { return !operator==(c); }
1204 };
1205
1206 template< typename Char, typename Traits >
operator <<(std::basic_ostream<Char,Traits> & strm,test_struct_with_ctor const & s)1207 inline std::basic_ostream< Char, Traits >& operator<< (std::basic_ostream< Char, Traits >& strm, test_struct_with_ctor const& s)
1208 {
1209 strm << "{" << s.i << "}";
1210 return strm;
1211 }
1212
1213 template< template< typename > class Wrapper >
test_struct_with_ctor_api(void)1214 void test_struct_with_ctor_api(void)
1215 {
1216 {
1217 test_struct_with_ctor s;
1218 Wrapper<test_struct_with_ctor> wrapper_sa;
1219 typename Wrapper<test_struct_with_ctor>::atomic_reference_type sa = wrapper_sa.a;
1220 // Check that the default constructor was called
1221 BOOST_TEST( sa.load() == s );
1222 }
1223
1224 test_struct_with_ctor a, b, c;
1225 a.i = 1;
1226 b.i = 2;
1227 c.i = 3;
1228
1229 test_base_operators< Wrapper >(a, b, c);
1230 }
1231
1232 #endif
1233