mirror of
https://github.com/boostorg/atomic.git
synced 2026-02-01 08:12:07 +00:00
Added implementation of inter-process atomics.
The inter-process atomics have ipc_ prefixes: ipc_atomic, ipc_atomic_ref and ipc_atomic_flag. These types are similar to their unprefixed counterparts with the following distinctions: - The operations are provided with an added precondition that is_lock_free() returns true. - All operations, including waiting/notifying operations, are address-free, so the types are suitable for inter-process communication. - The new has_native_wait_notify() operation and always_has_native_wait_notify static constant allow to test if the target platform has native support for address-free waiting/notifying operations. If it does not, a generic implementation is used based on a busy wait. - The new set of capability macros added. The macros are named BOOST_ATOMIC_HAS_NATIVE_<T>_IPC_WAIT_NOTIFY and indicate whether address-free waiting/notifying operations are supported natively for a given type. Additionally, to unify interface and implementation of different components, the has_native_wait_notify() operation and always_has_native_wait_notify static constant were added to non-IPC atomic types as well. Added BOOST_ATOMIC_HAS_NATIVE_<T>_WAIT_NOTIFY capability macros to indicate native support for inter-thread waiting/notifying operations. Also, added is_lock_free() and is_always_lock_free to atomic_flag. This commit adds implementation, docs and tests.
This commit is contained in:
@@ -31,6 +31,10 @@ test-suite atomic
|
||||
[ run wait_ref_api.cpp : : : <define>BOOST_ATOMIC_FORCE_FALLBACK : fallback_wait_ref_api ]
|
||||
[ run wait_fuzz.cpp ]
|
||||
[ run wait_fuzz.cpp : : : <define>BOOST_ATOMIC_FORCE_FALLBACK : fallback_wait_fuzz ]
|
||||
[ run ipc_atomic_api.cpp ]
|
||||
[ run ipc_atomic_ref_api.cpp ]
|
||||
[ run ipc_wait_api.cpp ]
|
||||
[ run ipc_wait_ref_api.cpp ]
|
||||
[ run atomicity.cpp ]
|
||||
[ run atomicity_ref.cpp ]
|
||||
[ run ordering.cpp ]
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <boost/type.hpp>
|
||||
#include <boost/core/enable_if.hpp>
|
||||
#include <boost/type_traits/integral_constant.hpp>
|
||||
#include <boost/type_traits/alignment_of.hpp>
|
||||
#include <boost/type_traits/is_pointer.hpp>
|
||||
@@ -32,17 +33,49 @@
|
||||
|
||||
const unsigned int max_weak_cas_loops = 1000;
|
||||
|
||||
/* provide helpers that exercise whether the API
|
||||
functions of "boost::atomic" provide the correct
|
||||
operational semantic in the case of sequential
|
||||
execution */
|
||||
template< typename T >
|
||||
struct is_atomic :
|
||||
public boost::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct is_atomic< boost::atomic< T > > :
|
||||
public boost::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct is_atomic< boost::ipc_atomic< T > > :
|
||||
public boost::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct is_atomic_ref :
|
||||
public boost::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct is_atomic_ref< boost::atomic_ref< T > > :
|
||||
public boost::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct is_atomic_ref< boost::ipc_atomic_ref< T > > :
|
||||
public boost::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template< typename Flag >
|
||||
inline void test_flag_api(void)
|
||||
{
|
||||
#ifndef BOOST_ATOMIC_NO_ATOMIC_FLAG_INIT
|
||||
boost::atomic_flag f = BOOST_ATOMIC_FLAG_INIT;
|
||||
Flag f = BOOST_ATOMIC_FLAG_INIT;
|
||||
#else
|
||||
boost::atomic_flag f;
|
||||
Flag f;
|
||||
#endif
|
||||
|
||||
BOOST_TEST( !f.test() );
|
||||
@@ -56,22 +89,22 @@ inline void test_flag_api(void)
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
inline void test_atomic_type_traits(boost::type< boost::atomic< T > >)
|
||||
inline typename boost::enable_if< is_atomic< T > >::type test_atomic_type_traits(boost::type< T >)
|
||||
{
|
||||
BOOST_TEST_GE(sizeof(boost::atomic< T >), sizeof(T));
|
||||
BOOST_TEST_GE(sizeof(T), sizeof(typename T::value_type));
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
inline void test_atomic_type_traits(boost::type< boost::atomic_ref< T > >)
|
||||
inline typename boost::enable_if< is_atomic_ref< T > >::type test_atomic_type_traits(boost::type< T >)
|
||||
{
|
||||
if (boost::atomic_ref< T >::is_always_lock_free)
|
||||
if (T::is_always_lock_free)
|
||||
{
|
||||
BOOST_TEST_GE(boost::atomic_ref< T >::required_alignment, boost::alignment_of< T >::value);
|
||||
BOOST_TEST_GE(T::required_alignment, boost::alignment_of< typename T::value_type >::value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Lock-based implementation should not require alignment higher than alignof(T)
|
||||
BOOST_TEST_EQ(boost::atomic_ref< T >::required_alignment, boost::alignment_of< T >::value);
|
||||
BOOST_TEST_EQ(T::required_alignment, boost::alignment_of< typename T::value_type >::value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
int main(int, char *[])
|
||||
{
|
||||
test_flag_api();
|
||||
test_flag_api< boost::atomic_flag >();
|
||||
|
||||
test_integral_api< atomic_wrapper, char >();
|
||||
test_integral_api< atomic_wrapper, signed char >();
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
|
||||
#include <boost/atomic/atomic.hpp>
|
||||
#include <boost/atomic/atomic_ref.hpp>
|
||||
#include <boost/atomic/atomic_flag.hpp>
|
||||
#include <boost/atomic/ipc_atomic.hpp>
|
||||
#include <boost/atomic/ipc_atomic_ref.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include "aligned_object.hpp"
|
||||
|
||||
@@ -40,4 +41,31 @@ struct atomic_ref_wrapper
|
||||
explicit atomic_ref_wrapper(T const& value) : object(value), a(object.get()) {}
|
||||
};
|
||||
|
||||
//! Wrapper type for ipc_atomic template
|
||||
template< typename T >
|
||||
struct ipc_atomic_wrapper
|
||||
{
|
||||
typedef boost::ipc_atomic< T > atomic_type;
|
||||
typedef atomic_type& atomic_reference_type;
|
||||
|
||||
atomic_type a;
|
||||
|
||||
BOOST_DEFAULTED_FUNCTION(ipc_atomic_wrapper(), {})
|
||||
explicit ipc_atomic_wrapper(T const& value) : a(value) {}
|
||||
};
|
||||
|
||||
//! Wrapper type for atomic_ref template
|
||||
template< typename T >
|
||||
struct ipc_atomic_ref_wrapper
|
||||
{
|
||||
typedef boost::ipc_atomic_ref< T > atomic_type;
|
||||
typedef atomic_type const& atomic_reference_type;
|
||||
|
||||
aligned_object< T, atomic_type::required_alignment > object;
|
||||
const atomic_type a;
|
||||
|
||||
ipc_atomic_ref_wrapper() : a(object.get()) {}
|
||||
explicit ipc_atomic_ref_wrapper(T const& value) : object(value), a(object.get()) {}
|
||||
};
|
||||
|
||||
#endif // BOOST_ATOMIC_TEST_ATOMIC_WRAPPER_HPP_INCLUDED_
|
||||
|
||||
67
test/ipc_atomic_api.cpp
Normal file
67
test/ipc_atomic_api.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright (c) 2020 Andrey Semashev
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/atomic/ipc_atomic.hpp>
|
||||
#include <boost/atomic/ipc_atomic_flag.hpp>
|
||||
#include <boost/atomic/capabilities.hpp>
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/cstdint.hpp>
|
||||
|
||||
#include "api_test_helpers.hpp"
|
||||
|
||||
int main(int, char *[])
|
||||
{
|
||||
#if BOOST_ATOMIC_FLAG_LOCK_FREE == 2
|
||||
test_flag_api< boost::ipc_atomic_flag >();
|
||||
#endif
|
||||
|
||||
#if BOOST_ATOMIC_INT8_LOCK_FREE == 2
|
||||
test_integral_api< ipc_atomic_wrapper, boost::uint8_t >();
|
||||
test_integral_api< ipc_atomic_wrapper, boost::int8_t >();
|
||||
#endif
|
||||
#if BOOST_ATOMIC_INT16_LOCK_FREE == 2
|
||||
test_integral_api< ipc_atomic_wrapper, boost::uint16_t >();
|
||||
test_integral_api< ipc_atomic_wrapper, boost::int16_t >();
|
||||
#endif
|
||||
#if BOOST_ATOMIC_INT32_LOCK_FREE == 2
|
||||
test_integral_api< ipc_atomic_wrapper, boost::uint32_t >();
|
||||
test_integral_api< ipc_atomic_wrapper, boost::int32_t >();
|
||||
#endif
|
||||
#if BOOST_ATOMIC_INT64_LOCK_FREE == 2
|
||||
test_integral_api< ipc_atomic_wrapper, boost::uint64_t >();
|
||||
test_integral_api< ipc_atomic_wrapper, boost::int64_t >();
|
||||
#endif
|
||||
#if defined(BOOST_HAS_INT128) && !defined(BOOST_ATOMIC_TESTS_NO_INT128) && BOOST_ATOMIC_INT128_LOCK_FREE == 2
|
||||
test_integral_api< ipc_atomic_wrapper, boost::int128_type >();
|
||||
test_integral_api< ipc_atomic_wrapper, boost::uint128_type >();
|
||||
#endif
|
||||
|
||||
#if !defined(BOOST_ATOMIC_NO_FLOATING_POINT)
|
||||
#if BOOST_ATOMIC_FLOAT_LOCK_FREE == 2
|
||||
test_floating_point_api< ipc_atomic_wrapper, float >();
|
||||
#endif
|
||||
#if BOOST_ATOMIC_DOUBLE_LOCK_FREE == 2
|
||||
test_floating_point_api< ipc_atomic_wrapper, double >();
|
||||
#endif
|
||||
#if BOOST_ATOMIC_LONG_DOUBLE_LOCK_FREE == 2
|
||||
test_floating_point_api< ipc_atomic_wrapper, long double >();
|
||||
#endif
|
||||
#if defined(BOOST_HAS_FLOAT128) && !defined(BOOST_ATOMIC_TESTS_NO_FLOAT128) && BOOST_ATOMIC_FLOAT128_LOCK_FREE == 2
|
||||
test_floating_point_api< ipc_atomic_wrapper, boost::float128_type >();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if BOOST_ATOMIC_POINTER_LOCK_FREE == 2
|
||||
test_pointer_api< ipc_atomic_wrapper, int >();
|
||||
#endif
|
||||
|
||||
#if BOOST_ATOMIC_INT_LOCK_FREE == 2
|
||||
test_enum_api< ipc_atomic_wrapper >();
|
||||
#endif
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
62
test/ipc_atomic_ref_api.cpp
Normal file
62
test/ipc_atomic_ref_api.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) 2020 Andrey Semashev
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/atomic/ipc_atomic_ref.hpp>
|
||||
#include <boost/atomic/capabilities.hpp>
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/cstdint.hpp>
|
||||
|
||||
#include "api_test_helpers.hpp"
|
||||
|
||||
int main(int, char *[])
|
||||
{
|
||||
#if BOOST_ATOMIC_INT8_LOCK_FREE == 2
|
||||
test_integral_api< ipc_atomic_ref_wrapper, boost::uint8_t >();
|
||||
test_integral_api< ipc_atomic_ref_wrapper, boost::int8_t >();
|
||||
#endif
|
||||
#if BOOST_ATOMIC_INT16_LOCK_FREE == 2
|
||||
test_integral_api< ipc_atomic_ref_wrapper, boost::uint16_t >();
|
||||
test_integral_api< ipc_atomic_ref_wrapper, boost::int16_t >();
|
||||
#endif
|
||||
#if BOOST_ATOMIC_INT32_LOCK_FREE == 2
|
||||
test_integral_api< ipc_atomic_ref_wrapper, boost::uint32_t >();
|
||||
test_integral_api< ipc_atomic_ref_wrapper, boost::int32_t >();
|
||||
#endif
|
||||
#if BOOST_ATOMIC_INT64_LOCK_FREE == 2
|
||||
test_integral_api< ipc_atomic_ref_wrapper, boost::uint64_t >();
|
||||
test_integral_api< ipc_atomic_ref_wrapper, boost::int64_t >();
|
||||
#endif
|
||||
#if defined(BOOST_HAS_INT128) && !defined(BOOST_ATOMIC_TESTS_NO_INT128) && BOOST_ATOMIC_INT128_LOCK_FREE == 2
|
||||
test_integral_api< ipc_atomic_ref_wrapper, boost::int128_type >();
|
||||
test_integral_api< ipc_atomic_ref_wrapper, boost::uint128_type >();
|
||||
#endif
|
||||
|
||||
#if !defined(BOOST_ATOMIC_NO_FLOATING_POINT)
|
||||
#if BOOST_ATOMIC_FLOAT_LOCK_FREE == 2
|
||||
test_floating_point_api< ipc_atomic_ref_wrapper, float >();
|
||||
#endif
|
||||
#if BOOST_ATOMIC_DOUBLE_LOCK_FREE == 2
|
||||
test_floating_point_api< ipc_atomic_ref_wrapper, double >();
|
||||
#endif
|
||||
#if BOOST_ATOMIC_LONG_DOUBLE_LOCK_FREE == 2
|
||||
test_floating_point_api< ipc_atomic_ref_wrapper, long double >();
|
||||
#endif
|
||||
#if defined(BOOST_HAS_FLOAT128) && !defined(BOOST_ATOMIC_TESTS_NO_FLOAT128) && BOOST_ATOMIC_FLOAT128_LOCK_FREE == 2
|
||||
test_floating_point_api< ipc_atomic_ref_wrapper, boost::float128_type >();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if BOOST_ATOMIC_POINTER_LOCK_FREE == 2
|
||||
test_pointer_api< ipc_atomic_ref_wrapper, int >();
|
||||
#endif
|
||||
|
||||
#if BOOST_ATOMIC_INT_LOCK_FREE == 2
|
||||
test_enum_api< ipc_atomic_ref_wrapper >();
|
||||
#endif
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
39
test/ipc_wait_api.cpp
Normal file
39
test/ipc_wait_api.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2020 Andrey Semashev
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/atomic/ipc_atomic.hpp>
|
||||
#include <boost/atomic/ipc_atomic_flag.hpp>
|
||||
#include <boost/atomic/capabilities.hpp>
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/cstdint.hpp>
|
||||
|
||||
#include "ipc_wait_test_helpers.hpp"
|
||||
|
||||
int main(int, char *[])
|
||||
{
|
||||
#if BOOST_ATOMIC_FLAG_LOCK_FREE == 2
|
||||
test_flag_wait_notify_api();
|
||||
#endif
|
||||
|
||||
#if BOOST_ATOMIC_INT8_LOCK_FREE == 2
|
||||
test_wait_notify_api< ipc_atomic_wrapper, boost::uint8_t >(1, 2, 3);
|
||||
#endif
|
||||
#if BOOST_ATOMIC_INT16_LOCK_FREE == 2
|
||||
test_wait_notify_api< ipc_atomic_wrapper, boost::uint16_t >(1, 2, 3);
|
||||
#endif
|
||||
#if BOOST_ATOMIC_INT32_LOCK_FREE == 2
|
||||
test_wait_notify_api< ipc_atomic_wrapper, boost::uint32_t >(1, 2, 3);
|
||||
#endif
|
||||
#if BOOST_ATOMIC_INT64_LOCK_FREE == 2
|
||||
test_wait_notify_api< ipc_atomic_wrapper, boost::uint64_t >(1, 2, 3);
|
||||
#endif
|
||||
#if defined(BOOST_HAS_INT128) && !defined(BOOST_ATOMIC_TESTS_NO_INT128) && BOOST_ATOMIC_INT128_LOCK_FREE == 2
|
||||
test_wait_notify_api< ipc_atomic_wrapper, boost::uint128_type >(1, 2, 3);
|
||||
#endif
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
34
test/ipc_wait_ref_api.cpp
Normal file
34
test/ipc_wait_ref_api.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2020 Andrey Semashev
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/atomic/ipc_atomic_ref.hpp>
|
||||
#include <boost/atomic/capabilities.hpp>
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/cstdint.hpp>
|
||||
|
||||
#include "ipc_wait_test_helpers.hpp"
|
||||
|
||||
int main(int, char *[])
|
||||
{
|
||||
#if BOOST_ATOMIC_INT8_LOCK_FREE == 2
|
||||
test_wait_notify_api< ipc_atomic_ref_wrapper, boost::uint8_t >(1, 2, 3);
|
||||
#endif
|
||||
#if BOOST_ATOMIC_INT16_LOCK_FREE == 2
|
||||
test_wait_notify_api< ipc_atomic_ref_wrapper, boost::uint16_t >(1, 2, 3);
|
||||
#endif
|
||||
#if BOOST_ATOMIC_INT32_LOCK_FREE == 2
|
||||
test_wait_notify_api< ipc_atomic_ref_wrapper, boost::uint32_t >(1, 2, 3);
|
||||
#endif
|
||||
#if BOOST_ATOMIC_INT64_LOCK_FREE == 2
|
||||
test_wait_notify_api< ipc_atomic_ref_wrapper, boost::uint64_t >(1, 2, 3);
|
||||
#endif
|
||||
#if defined(BOOST_HAS_INT128) && !defined(BOOST_ATOMIC_TESTS_NO_INT128) && BOOST_ATOMIC_INT128_LOCK_FREE == 2
|
||||
test_wait_notify_api< ipc_atomic_ref_wrapper, boost::uint128_type >(1, 2, 3);
|
||||
#endif
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
309
test/ipc_wait_test_helpers.hpp
Normal file
309
test/ipc_wait_test_helpers.hpp
Normal file
@@ -0,0 +1,309 @@
|
||||
// Copyright (c) 2020 Andrey Semashev
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_ATOMIC_TEST_IPC_WAIT_TEST_HELPERS_HPP_INCLUDED_
|
||||
#define BOOST_ATOMIC_TEST_IPC_WAIT_TEST_HELPERS_HPP_INCLUDED_
|
||||
|
||||
#include <boost/memory_order.hpp>
|
||||
#include <boost/atomic/ipc_atomic_flag.hpp>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/chrono/chrono.hpp>
|
||||
#include <boost/bind/bind.hpp>
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include <boost/thread/barrier.hpp>
|
||||
#include "atomic_wrapper.hpp"
|
||||
#include "lightweight_test_stream.hpp"
|
||||
|
||||
namespace chrono = boost::chrono;
|
||||
|
||||
#if defined(BOOST_CHRONO_HAS_CLOCK_STEADY)
|
||||
typedef chrono::steady_clock test_clock;
|
||||
#else
|
||||
typedef chrono::system_clock test_clock;
|
||||
#endif
|
||||
|
||||
//! Since some of the tests below are allowed to fail, we retry up to this many times to pass the test
|
||||
BOOST_CONSTEXPR_OR_CONST unsigned int test_retry_count = 5u;
|
||||
|
||||
//! The test verifies that the wait operation returns immediately if the passed value does not match the atomic value
|
||||
template< template< typename > class Wrapper, typename T >
|
||||
inline void test_wait_value_mismatch(T value1, T value2)
|
||||
{
|
||||
Wrapper< T > m_wrapper(value1);
|
||||
|
||||
T received_value = m_wrapper.a.wait(value2);
|
||||
BOOST_TEST(received_value == value1);
|
||||
}
|
||||
|
||||
/*!
|
||||
* The test verifies that notify_one releases one blocked thread and that the released thread receives the modified atomic value.
|
||||
*
|
||||
* Technically, this test is allowed to fail since wait() is allowed to return spuriously. However, normally this should not happen.
|
||||
*/
|
||||
template< template< typename > class Wrapper, typename T >
|
||||
class notify_one_test
|
||||
{
|
||||
private:
|
||||
struct thread_state
|
||||
{
|
||||
T m_received_value;
|
||||
test_clock::time_point m_wakeup_time;
|
||||
|
||||
explicit thread_state(T value) : m_received_value(value)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
Wrapper< T > m_wrapper;
|
||||
|
||||
char m_padding[1024];
|
||||
|
||||
T m_value1, m_value2, m_value3;
|
||||
|
||||
boost::barrier m_barrier;
|
||||
|
||||
thread_state m_thread1_state;
|
||||
thread_state m_thread2_state;
|
||||
|
||||
public:
|
||||
explicit notify_one_test(T value1, T value2, T value3) :
|
||||
m_wrapper(value1),
|
||||
m_value1(value1),
|
||||
m_value2(value2),
|
||||
m_value3(value3),
|
||||
m_barrier(3),
|
||||
m_thread1_state(value1),
|
||||
m_thread2_state(value1)
|
||||
{
|
||||
}
|
||||
|
||||
bool run()
|
||||
{
|
||||
boost::thread thread1(¬ify_one_test::thread_func, this, &m_thread1_state);
|
||||
boost::thread thread2(¬ify_one_test::thread_func, this, &m_thread2_state);
|
||||
|
||||
m_barrier.wait();
|
||||
|
||||
test_clock::time_point start_time = test_clock::now();
|
||||
|
||||
boost::this_thread::sleep_for(chrono::milliseconds(200));
|
||||
|
||||
m_wrapper.a.store(m_value2, boost::memory_order_release);
|
||||
m_wrapper.a.notify_one();
|
||||
|
||||
boost::this_thread::sleep_for(chrono::milliseconds(200));
|
||||
|
||||
m_wrapper.a.store(m_value3, boost::memory_order_release);
|
||||
m_wrapper.a.notify_one();
|
||||
|
||||
if (!thread1.try_join_for(chrono::seconds(3)))
|
||||
{
|
||||
BOOST_ERROR("Thread 1 failed to join");
|
||||
std::abort();
|
||||
}
|
||||
if (!thread2.try_join_for(chrono::seconds(3)))
|
||||
{
|
||||
BOOST_ERROR("Thread 2 failed to join");
|
||||
std::abort();
|
||||
}
|
||||
|
||||
thread_state* first_state = &m_thread1_state;
|
||||
thread_state* second_state = &m_thread2_state;
|
||||
if (second_state->m_wakeup_time < first_state->m_wakeup_time)
|
||||
std::swap(first_state, second_state);
|
||||
|
||||
if (m_wrapper.a.has_native_wait_notify())
|
||||
{
|
||||
if ((first_state->m_wakeup_time - start_time) < chrono::milliseconds(200))
|
||||
{
|
||||
std::cout << "notify_one_test: first thread woke up too soon: " << chrono::duration_cast< chrono::milliseconds >(first_state->m_wakeup_time - start_time).count() << " ms" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((second_state->m_wakeup_time - start_time) < chrono::milliseconds(400))
|
||||
{
|
||||
std::cout << "notify_one_test: second thread woke up too soon: " << chrono::duration_cast< chrono::milliseconds >(second_state->m_wakeup_time - start_time).count() << " ms" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ(first_state->m_received_value, m_value2);
|
||||
BOOST_TEST_EQ(second_state->m_received_value, m_value3);
|
||||
}
|
||||
else
|
||||
{
|
||||
// With the emulated wait/notify the threads are most likely to return prior to notify
|
||||
BOOST_TEST_EQ(first_state->m_received_value, m_value2);
|
||||
BOOST_TEST(second_state->m_received_value == m_value2 || second_state->m_received_value == m_value3);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
void thread_func(thread_state* state)
|
||||
{
|
||||
m_barrier.wait();
|
||||
|
||||
state->m_received_value = m_wrapper.a.wait(m_value1);
|
||||
state->m_wakeup_time = test_clock::now();
|
||||
}
|
||||
};
|
||||
|
||||
template< template< typename > class Wrapper, typename T >
|
||||
inline void test_notify_one(T value1, T value2, T value3)
|
||||
{
|
||||
for (unsigned int i = 0u; i < test_retry_count; ++i)
|
||||
{
|
||||
notify_one_test< Wrapper, T > test(value1, value2, value3);
|
||||
if (test.run())
|
||||
return;
|
||||
}
|
||||
|
||||
BOOST_ERROR("notify_one_test could not complete because blocked thread wake up too soon");
|
||||
}
|
||||
|
||||
/*!
|
||||
* The test verifies that notify_all releases all blocked threads and that the released threads receive the modified atomic value.
|
||||
*
|
||||
* Technically, this test is allowed to fail since wait() is allowed to return spuriously. However, normally this should not happen.
|
||||
*/
|
||||
template< template< typename > class Wrapper, typename T >
|
||||
class notify_all_test
|
||||
{
|
||||
private:
|
||||
struct thread_state
|
||||
{
|
||||
T m_received_value;
|
||||
test_clock::time_point m_wakeup_time;
|
||||
|
||||
explicit thread_state(T value) : m_received_value(value)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
Wrapper< T > m_wrapper;
|
||||
|
||||
char m_padding[1024];
|
||||
|
||||
T m_value1, m_value2;
|
||||
|
||||
boost::barrier m_barrier;
|
||||
|
||||
thread_state m_thread1_state;
|
||||
thread_state m_thread2_state;
|
||||
|
||||
public:
|
||||
explicit notify_all_test(T value1, T value2) :
|
||||
m_wrapper(value1),
|
||||
m_value1(value1),
|
||||
m_value2(value2),
|
||||
m_barrier(3),
|
||||
m_thread1_state(value1),
|
||||
m_thread2_state(value1)
|
||||
{
|
||||
}
|
||||
|
||||
bool run()
|
||||
{
|
||||
boost::thread thread1(¬ify_all_test::thread_func, this, &m_thread1_state);
|
||||
boost::thread thread2(¬ify_all_test::thread_func, this, &m_thread2_state);
|
||||
|
||||
m_barrier.wait();
|
||||
|
||||
test_clock::time_point start_time = test_clock::now();
|
||||
|
||||
boost::this_thread::sleep_for(chrono::milliseconds(200));
|
||||
|
||||
m_wrapper.a.store(m_value2, boost::memory_order_release);
|
||||
m_wrapper.a.notify_all();
|
||||
|
||||
if (!thread1.try_join_for(chrono::seconds(3)))
|
||||
{
|
||||
BOOST_ERROR("Thread 1 failed to join");
|
||||
std::abort();
|
||||
}
|
||||
if (!thread2.try_join_for(chrono::seconds(3)))
|
||||
{
|
||||
BOOST_ERROR("Thread 2 failed to join");
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if (m_wrapper.a.has_native_wait_notify())
|
||||
{
|
||||
if ((m_thread1_state.m_wakeup_time - start_time) < chrono::milliseconds(200))
|
||||
{
|
||||
std::cout << "notify_all_test: first thread woke up too soon: " << chrono::duration_cast< chrono::milliseconds >(m_thread1_state.m_wakeup_time - start_time).count() << " ms" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((m_thread2_state.m_wakeup_time - start_time) < chrono::milliseconds(200))
|
||||
{
|
||||
std::cout << "notify_all_test: second thread woke up too soon: " << chrono::duration_cast< chrono::milliseconds >(m_thread2_state.m_wakeup_time - start_time).count() << " ms" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ(m_thread1_state.m_received_value, m_value2);
|
||||
BOOST_TEST_EQ(m_thread2_state.m_received_value, m_value2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
void thread_func(thread_state* state)
|
||||
{
|
||||
m_barrier.wait();
|
||||
|
||||
state->m_received_value = m_wrapper.a.wait(m_value1);
|
||||
state->m_wakeup_time = test_clock::now();
|
||||
}
|
||||
};
|
||||
|
||||
template< template< typename > class Wrapper, typename T >
|
||||
inline void test_notify_all(T value1, T value2)
|
||||
{
|
||||
for (unsigned int i = 0u; i < test_retry_count; ++i)
|
||||
{
|
||||
notify_all_test< Wrapper, T > test(value1, value2);
|
||||
if (test.run())
|
||||
return;
|
||||
}
|
||||
|
||||
BOOST_ERROR("notify_all_test could not complete because blocked thread wake up too soon");
|
||||
}
|
||||
|
||||
//! Invokes all wait/notify tests
|
||||
template< template< typename > class Wrapper, typename T >
|
||||
void test_wait_notify_api(T value1, T value2, T value3)
|
||||
{
|
||||
test_wait_value_mismatch< Wrapper >(value1, value2);
|
||||
test_notify_one< Wrapper >(value1, value2, value3);
|
||||
test_notify_all< Wrapper >(value1, value2);
|
||||
}
|
||||
|
||||
|
||||
inline void test_flag_wait_notify_api()
|
||||
{
|
||||
#ifndef BOOST_ATOMIC_NO_ATOMIC_FLAG_INIT
|
||||
boost::ipc_atomic_flag f = BOOST_ATOMIC_FLAG_INIT;
|
||||
#else
|
||||
boost::ipc_atomic_flag f;
|
||||
#endif
|
||||
|
||||
bool received_value = f.wait(true);
|
||||
BOOST_TEST(!received_value);
|
||||
f.notify_one();
|
||||
f.notify_all();
|
||||
}
|
||||
|
||||
#endif // BOOST_ATOMIC_TEST_IPC_WAIT_TEST_HELPERS_HPP_INCLUDED_
|
||||
@@ -4,7 +4,9 @@
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/atomic.hpp>
|
||||
#include <boost/atomic/atomic.hpp>
|
||||
#include <boost/atomic/atomic_flag.hpp>
|
||||
#include <boost/atomic/capabilities.hpp>
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/cstdint.hpp>
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
// while incrementing the atomic object. The test ends when the atomic counter reaches the predefined limit.
|
||||
// The goal of the test is to verify that (a) it doesn't crash and (b) all threads get unblocked in the end.
|
||||
|
||||
#include <boost/atomic.hpp>
|
||||
#include <boost/memory_order.hpp>
|
||||
#include <boost/atomic/atomic.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <boost/config.hpp>
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/atomic.hpp>
|
||||
#include <boost/atomic/atomic_ref.hpp>
|
||||
#include <boost/atomic/capabilities.hpp>
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/cstdint.hpp>
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
#ifndef BOOST_ATOMIC_TEST_WAIT_TEST_HELPERS_HPP_INCLUDED_
|
||||
#define BOOST_ATOMIC_TEST_WAIT_TEST_HELPERS_HPP_INCLUDED_
|
||||
|
||||
#include <boost/atomic.hpp>
|
||||
#include <boost/memory_order.hpp>
|
||||
#include <boost/atomic/atomic_flag.hpp>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
Reference in New Issue
Block a user