mirror of
https://gitlab.com/libeigen/eigen.git
synced 2026-04-10 11:34:33 +08:00
Thread pool
This commit is contained in:
committed by
Rasmus Munk Larsen
parent
9eb8e2afba
commit
94f57867fe
@@ -173,6 +173,7 @@ add_custom_target(BuildOfficial)
|
||||
|
||||
ei_add_test(rand)
|
||||
ei_add_test(meta)
|
||||
ei_add_test(maxsizevector)
|
||||
ei_add_test(numext)
|
||||
ei_add_test(sizeof)
|
||||
ei_add_test(dynalloc)
|
||||
@@ -311,7 +312,9 @@ ei_add_test(initializer_list_construction)
|
||||
ei_add_test(diagonal_matrix_variadic_ctor)
|
||||
ei_add_test(serializer)
|
||||
ei_add_test(tuple_test)
|
||||
|
||||
ei_add_test(threads_eventcount "-pthread" "${CMAKE_THREAD_LIBS_INIT}")
|
||||
ei_add_test(threads_runqueue "-pthread" "${CMAKE_THREAD_LIBS_INIT}")
|
||||
ei_add_test(threads_non_blocking_thread_pool "-pthread" "${CMAKE_THREAD_LIBS_INIT}")
|
||||
add_executable(bug1213 bug1213.cpp bug1213_main.cpp)
|
||||
|
||||
check_cxx_compiler_flag("-ffast-math" COMPILER_SUPPORT_FASTMATH)
|
||||
|
||||
77
test/maxsizevector.cpp
Normal file
77
test/maxsizevector.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include "main.h"
|
||||
|
||||
#include <exception> // std::exception
|
||||
|
||||
#include <Eigen/src/Core/util/MaxSizeVector.h>
|
||||
|
||||
struct Foo
|
||||
{
|
||||
static Index object_count;
|
||||
static Index object_limit;
|
||||
EIGEN_ALIGN_TO_BOUNDARY(128) int dummy;
|
||||
|
||||
Foo(int x=0) : dummy(x)
|
||||
{
|
||||
#ifdef EIGEN_EXCEPTIONS
|
||||
// TODO: Is this the correct way to handle this?
|
||||
if (Foo::object_count > Foo::object_limit) { std::cout << "\nThrow!\n"; throw Foo::Fail(); }
|
||||
#endif
|
||||
std::cout << '+';
|
||||
++Foo::object_count;
|
||||
eigen_assert((std::uintptr_t(this) & (127)) == 0);
|
||||
}
|
||||
Foo(const Foo&)
|
||||
{
|
||||
std::cout << 'c';
|
||||
++Foo::object_count;
|
||||
eigen_assert((std::uintptr_t(this) & (127)) == 0);
|
||||
}
|
||||
|
||||
~Foo()
|
||||
{
|
||||
std::cout << '~';
|
||||
--Foo::object_count;
|
||||
}
|
||||
|
||||
class Fail : public std::exception {};
|
||||
};
|
||||
|
||||
Index Foo::object_count = 0;
|
||||
Index Foo::object_limit = 0;
|
||||
|
||||
|
||||
|
||||
EIGEN_DECLARE_TEST(cxx11_maxsizevector)
|
||||
{
|
||||
typedef MaxSizeVector<Foo> VectorX;
|
||||
Foo::object_count = 0;
|
||||
for(int r = 0; r < g_repeat; r++) {
|
||||
Index rows = internal::random<Index>(3,30);
|
||||
Foo::object_limit = internal::random<Index>(0, rows - 2);
|
||||
std::cout << "object_limit = " << Foo::object_limit << std::endl;
|
||||
bool exception_raised = false;
|
||||
#ifdef EIGEN_EXCEPTIONS
|
||||
try
|
||||
{
|
||||
#endif
|
||||
std::cout << "\nVectorX m(" << rows << ");\n";
|
||||
VectorX vect(rows);
|
||||
for(int i=0; i<rows; ++i)
|
||||
vect.push_back(Foo());
|
||||
#ifdef EIGEN_EXCEPTIONS
|
||||
VERIFY(false); // not reached if exceptions are enabled
|
||||
}
|
||||
catch (const Foo::Fail&) { exception_raised = true; }
|
||||
VERIFY(exception_raised);
|
||||
#endif
|
||||
VERIFY_IS_EQUAL(Index(0), Foo::object_count);
|
||||
|
||||
{
|
||||
Foo::object_limit = rows+1;
|
||||
VectorX vect2(rows, Foo());
|
||||
VERIFY_IS_EQUAL(Foo::object_count, rows);
|
||||
}
|
||||
VERIFY_IS_EQUAL(Index(0), Foo::object_count);
|
||||
std::cout << '\n';
|
||||
}
|
||||
}
|
||||
347
test/meta.cpp
347
test/meta.cpp
@@ -9,6 +9,9 @@
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include <array>
|
||||
#include <Eigen/src/Core/util/Meta.h>
|
||||
|
||||
template<typename From, typename To>
|
||||
bool check_is_convertible(const From&, const To&)
|
||||
{
|
||||
@@ -131,3 +134,347 @@ EIGEN_DECLARE_TEST(meta)
|
||||
VERIFY_META_SQRT(1024);
|
||||
VERIFY_META_SQRT(1025);
|
||||
}
|
||||
|
||||
using Eigen::internal::is_same;
|
||||
using Eigen::internal::type_list;
|
||||
using Eigen::internal::numeric_list;
|
||||
using Eigen::internal::gen_numeric_list;
|
||||
using Eigen::internal::gen_numeric_list_reversed;
|
||||
using Eigen::internal::gen_numeric_list_swapped_pair;
|
||||
using Eigen::internal::gen_numeric_list_repeated;
|
||||
using Eigen::internal::concat;
|
||||
using Eigen::internal::mconcat;
|
||||
using Eigen::internal::take;
|
||||
using Eigen::internal::skip;
|
||||
using Eigen::internal::slice;
|
||||
using Eigen::internal::get;
|
||||
using Eigen::internal::id_numeric;
|
||||
using Eigen::internal::id_type;
|
||||
using Eigen::internal::is_same_gf;
|
||||
using Eigen::internal::apply_op_from_left;
|
||||
using Eigen::internal::apply_op_from_right;
|
||||
using Eigen::internal::contained_in_list;
|
||||
using Eigen::internal::contained_in_list_gf;
|
||||
using Eigen::internal::arg_prod;
|
||||
using Eigen::internal::arg_sum;
|
||||
using Eigen::internal::sum_op;
|
||||
using Eigen::internal::product_op;
|
||||
using Eigen::internal::array_reverse;
|
||||
using Eigen::internal::array_sum;
|
||||
using Eigen::internal::array_prod;
|
||||
using Eigen::internal::array_reduce;
|
||||
using Eigen::internal::array_zip;
|
||||
using Eigen::internal::array_zip_and_reduce;
|
||||
using Eigen::internal::array_apply;
|
||||
using Eigen::internal::array_apply_and_reduce;
|
||||
using Eigen::internal::repeat;
|
||||
using Eigen::internal::instantiate_by_c_array;
|
||||
|
||||
struct dummy_a {};
|
||||
struct dummy_b {};
|
||||
struct dummy_c {};
|
||||
struct dummy_d {};
|
||||
struct dummy_e {};
|
||||
|
||||
// dummy operation for testing apply
|
||||
template<typename A, typename B> struct dummy_op;
|
||||
template<> struct dummy_op<dummy_a, dummy_b> { typedef dummy_c type; };
|
||||
template<> struct dummy_op<dummy_b, dummy_a> { typedef dummy_d type; };
|
||||
template<> struct dummy_op<dummy_b, dummy_c> { typedef dummy_a type; };
|
||||
template<> struct dummy_op<dummy_c, dummy_b> { typedef dummy_d type; };
|
||||
template<> struct dummy_op<dummy_c, dummy_a> { typedef dummy_b type; };
|
||||
template<> struct dummy_op<dummy_a, dummy_c> { typedef dummy_d type; };
|
||||
template<> struct dummy_op<dummy_a, dummy_a> { typedef dummy_e type; };
|
||||
template<> struct dummy_op<dummy_b, dummy_b> { typedef dummy_e type; };
|
||||
template<> struct dummy_op<dummy_c, dummy_c> { typedef dummy_e type; };
|
||||
|
||||
template<typename A, typename B> struct dummy_test { constexpr static bool value = false; constexpr static int global_flags = 0; };
|
||||
template<> struct dummy_test<dummy_a, dummy_a> { constexpr static bool value = true; constexpr static int global_flags = 1; };
|
||||
template<> struct dummy_test<dummy_b, dummy_b> { constexpr static bool value = true; constexpr static int global_flags = 2; };
|
||||
template<> struct dummy_test<dummy_c, dummy_c> { constexpr static bool value = true; constexpr static int global_flags = 4; };
|
||||
|
||||
struct times2_op { template<typename A> static A run(A v) { return v * 2; } };
|
||||
|
||||
struct dummy_inst
|
||||
{
|
||||
int c;
|
||||
|
||||
dummy_inst() : c(0) {}
|
||||
explicit dummy_inst(int) : c(1) {}
|
||||
dummy_inst(int, int) : c(2) {}
|
||||
dummy_inst(int, int, int) : c(3) {}
|
||||
dummy_inst(int, int, int, int) : c(4) {}
|
||||
dummy_inst(int, int, int, int, int) : c(5) {}
|
||||
};
|
||||
|
||||
static void test_gen_numeric_list()
|
||||
{
|
||||
VERIFY((is_same<typename gen_numeric_list<int, 0>::type, numeric_list<int>>::value));
|
||||
VERIFY((is_same<typename gen_numeric_list<int, 1>::type, numeric_list<int, 0>>::value));
|
||||
VERIFY((is_same<typename gen_numeric_list<int, 2>::type, numeric_list<int, 0, 1>>::value));
|
||||
VERIFY((is_same<typename gen_numeric_list<int, 5>::type, numeric_list<int, 0, 1, 2, 3, 4>>::value));
|
||||
VERIFY((is_same<typename gen_numeric_list<int, 10>::type, numeric_list<int, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9>>::value));
|
||||
|
||||
VERIFY((is_same<typename gen_numeric_list<int, 0, 42>::type, numeric_list<int>>::value));
|
||||
VERIFY((is_same<typename gen_numeric_list<int, 1, 42>::type, numeric_list<int, 42>>::value));
|
||||
VERIFY((is_same<typename gen_numeric_list<int, 2, 42>::type, numeric_list<int, 42, 43>>::value));
|
||||
VERIFY((is_same<typename gen_numeric_list<int, 5, 42>::type, numeric_list<int, 42, 43, 44, 45, 46>>::value));
|
||||
VERIFY((is_same<typename gen_numeric_list<int, 10, 42>::type, numeric_list<int, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51>>::value));
|
||||
|
||||
VERIFY((is_same<typename gen_numeric_list_reversed<int, 0>::type, numeric_list<int>>::value));
|
||||
VERIFY((is_same<typename gen_numeric_list_reversed<int, 1>::type, numeric_list<int, 0>>::value));
|
||||
VERIFY((is_same<typename gen_numeric_list_reversed<int, 2>::type, numeric_list<int, 1, 0>>::value));
|
||||
VERIFY((is_same<typename gen_numeric_list_reversed<int, 5>::type, numeric_list<int, 4, 3, 2, 1, 0>>::value));
|
||||
VERIFY((is_same<typename gen_numeric_list_reversed<int, 10>::type, numeric_list<int, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0>>::value));
|
||||
|
||||
VERIFY((is_same<typename gen_numeric_list_reversed<int, 0, 42>::type, numeric_list<int>>::value));
|
||||
VERIFY((is_same<typename gen_numeric_list_reversed<int, 1, 42>::type, numeric_list<int, 42>>::value));
|
||||
VERIFY((is_same<typename gen_numeric_list_reversed<int, 2, 42>::type, numeric_list<int, 43, 42>>::value));
|
||||
VERIFY((is_same<typename gen_numeric_list_reversed<int, 5, 42>::type, numeric_list<int, 46, 45, 44, 43, 42>>::value));
|
||||
VERIFY((is_same<typename gen_numeric_list_reversed<int, 10, 42>::type, numeric_list<int, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42>>::value));
|
||||
|
||||
VERIFY((is_same<typename gen_numeric_list_swapped_pair<int, 0, 2, 3>::type, numeric_list<int>>::value));
|
||||
VERIFY((is_same<typename gen_numeric_list_swapped_pair<int, 1, 2, 3>::type, numeric_list<int, 0>>::value));
|
||||
VERIFY((is_same<typename gen_numeric_list_swapped_pair<int, 2, 2, 3>::type, numeric_list<int, 0, 1>>::value));
|
||||
VERIFY((is_same<typename gen_numeric_list_swapped_pair<int, 5, 2, 3>::type, numeric_list<int, 0, 1, 3, 2, 4>>::value));
|
||||
VERIFY((is_same<typename gen_numeric_list_swapped_pair<int, 10, 2, 3>::type, numeric_list<int, 0, 1, 3, 2, 4, 5, 6, 7, 8, 9>>::value));
|
||||
|
||||
VERIFY((is_same<typename gen_numeric_list_swapped_pair<int, 0, 44, 45, 42>::type, numeric_list<int>>::value));
|
||||
VERIFY((is_same<typename gen_numeric_list_swapped_pair<int, 1, 44, 45, 42>::type, numeric_list<int, 42>>::value));
|
||||
VERIFY((is_same<typename gen_numeric_list_swapped_pair<int, 2, 44, 45, 42>::type, numeric_list<int, 42, 43>>::value));
|
||||
VERIFY((is_same<typename gen_numeric_list_swapped_pair<int, 5, 44, 45, 42>::type, numeric_list<int, 42, 43, 45, 44, 46>>::value));
|
||||
VERIFY((is_same<typename gen_numeric_list_swapped_pair<int, 10, 44, 45, 42>::type, numeric_list<int, 42, 43, 45, 44, 46, 47, 48, 49, 50, 51>>::value));
|
||||
|
||||
VERIFY((is_same<typename gen_numeric_list_repeated<int, 0, 0>::type, numeric_list<int>>::value));
|
||||
VERIFY((is_same<typename gen_numeric_list_repeated<int, 1, 0>::type, numeric_list<int, 0>>::value));
|
||||
VERIFY((is_same<typename gen_numeric_list_repeated<int, 2, 0>::type, numeric_list<int, 0, 0>>::value));
|
||||
VERIFY((is_same<typename gen_numeric_list_repeated<int, 5, 0>::type, numeric_list<int, 0, 0, 0, 0, 0>>::value));
|
||||
VERIFY((is_same<typename gen_numeric_list_repeated<int, 10, 0>::type, numeric_list<int, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>>::value));
|
||||
}
|
||||
|
||||
static void test_concat()
|
||||
{
|
||||
VERIFY((is_same<typename concat<type_list<dummy_a, dummy_a>, type_list<>>::type, type_list<dummy_a, dummy_a>>::value));
|
||||
VERIFY((is_same<typename concat<type_list<>, type_list<dummy_a, dummy_a>>::type, type_list<dummy_a, dummy_a>>::value));
|
||||
VERIFY((is_same<typename concat<type_list<dummy_a, dummy_a>, type_list<dummy_a, dummy_a>>::type, type_list<dummy_a, dummy_a, dummy_a, dummy_a>>::value));
|
||||
VERIFY((is_same<typename concat<type_list<dummy_a, dummy_a>, type_list<dummy_b, dummy_c>>::type, type_list<dummy_a, dummy_a, dummy_b, dummy_c>>::value));
|
||||
VERIFY((is_same<typename concat<type_list<dummy_a>, type_list<dummy_b, dummy_c>>::type, type_list<dummy_a, dummy_b, dummy_c>>::value));
|
||||
|
||||
VERIFY((is_same<typename concat<numeric_list<int, 0, 0>, numeric_list<int>>::type, numeric_list<int, 0, 0>>::value));
|
||||
VERIFY((is_same<typename concat<numeric_list<int>, numeric_list<int, 0, 0>>::type, numeric_list<int, 0, 0>>::value));
|
||||
VERIFY((is_same<typename concat<numeric_list<int, 0, 0>, numeric_list<int, 0, 0>>::type, numeric_list<int, 0, 0, 0, 0>>::value));
|
||||
VERIFY((is_same<typename concat<numeric_list<int, 0, 0>, numeric_list<int, 1, 2>>::type, numeric_list<int, 0, 0, 1, 2>>::value));
|
||||
VERIFY((is_same<typename concat<numeric_list<int, 0>, numeric_list<int, 1, 2>>::type, numeric_list<int, 0, 1, 2>>::value));
|
||||
|
||||
VERIFY((is_same<typename mconcat<type_list<dummy_a>>::type, type_list<dummy_a>>::value));
|
||||
VERIFY((is_same<typename mconcat<type_list<dummy_a>, type_list<dummy_b>>::type, type_list<dummy_a, dummy_b>>::value));
|
||||
VERIFY((is_same<typename mconcat<type_list<dummy_a>, type_list<dummy_b>, type_list<dummy_c>>::type, type_list<dummy_a, dummy_b, dummy_c>>::value));
|
||||
VERIFY((is_same<typename mconcat<type_list<dummy_a>, type_list<dummy_b, dummy_c>>::type, type_list<dummy_a, dummy_b, dummy_c>>::value));
|
||||
VERIFY((is_same<typename mconcat<type_list<dummy_a, dummy_b>, type_list<dummy_c>>::type, type_list<dummy_a, dummy_b, dummy_c>>::value));
|
||||
|
||||
VERIFY((is_same<typename mconcat<numeric_list<int, 0>>::type, numeric_list<int, 0>>::value));
|
||||
VERIFY((is_same<typename mconcat<numeric_list<int, 0>, numeric_list<int, 1>>::type, numeric_list<int, 0, 1>>::value));
|
||||
VERIFY((is_same<typename mconcat<numeric_list<int, 0>, numeric_list<int, 1>, numeric_list<int, 2>>::type, numeric_list<int, 0, 1, 2>>::value));
|
||||
VERIFY((is_same<typename mconcat<numeric_list<int, 0>, numeric_list<int, 1, 2>>::type, numeric_list<int, 0, 1, 2>>::value));
|
||||
VERIFY((is_same<typename mconcat<numeric_list<int, 0, 1>, numeric_list<int, 2>>::type, numeric_list<int, 0, 1, 2>>::value));
|
||||
}
|
||||
|
||||
static void test_slice()
|
||||
{
|
||||
typedef type_list<dummy_a, dummy_a, dummy_b, dummy_b, dummy_c, dummy_c> tl;
|
||||
typedef numeric_list<int, 0, 1, 2, 3, 4, 5> il;
|
||||
|
||||
VERIFY((is_same<typename take<0, tl>::type, type_list<>>::value));
|
||||
VERIFY((is_same<typename take<1, tl>::type, type_list<dummy_a>>::value));
|
||||
VERIFY((is_same<typename take<2, tl>::type, type_list<dummy_a, dummy_a>>::value));
|
||||
VERIFY((is_same<typename take<3, tl>::type, type_list<dummy_a, dummy_a, dummy_b>>::value));
|
||||
VERIFY((is_same<typename take<4, tl>::type, type_list<dummy_a, dummy_a, dummy_b, dummy_b>>::value));
|
||||
VERIFY((is_same<typename take<5, tl>::type, type_list<dummy_a, dummy_a, dummy_b, dummy_b, dummy_c>>::value));
|
||||
VERIFY((is_same<typename take<6, tl>::type, type_list<dummy_a, dummy_a, dummy_b, dummy_b, dummy_c, dummy_c>>::value));
|
||||
|
||||
VERIFY((is_same<typename take<0, il>::type, numeric_list<int>>::value));
|
||||
VERIFY((is_same<typename take<1, il>::type, numeric_list<int, 0>>::value));
|
||||
VERIFY((is_same<typename take<2, il>::type, numeric_list<int, 0, 1>>::value));
|
||||
VERIFY((is_same<typename take<3, il>::type, numeric_list<int, 0, 1, 2>>::value));
|
||||
VERIFY((is_same<typename take<4, il>::type, numeric_list<int, 0, 1, 2, 3>>::value));
|
||||
VERIFY((is_same<typename take<5, il>::type, numeric_list<int, 0, 1, 2, 3, 4>>::value));
|
||||
VERIFY((is_same<typename take<6, il>::type, numeric_list<int, 0, 1, 2, 3, 4, 5>>::value));
|
||||
|
||||
VERIFY((is_same<typename skip<0, tl>::type, type_list<dummy_a, dummy_a, dummy_b, dummy_b, dummy_c, dummy_c>>::value));
|
||||
VERIFY((is_same<typename skip<1, tl>::type, type_list<dummy_a, dummy_b, dummy_b, dummy_c, dummy_c>>::value));
|
||||
VERIFY((is_same<typename skip<2, tl>::type, type_list<dummy_b, dummy_b, dummy_c, dummy_c>>::value));
|
||||
VERIFY((is_same<typename skip<3, tl>::type, type_list<dummy_b, dummy_c, dummy_c>>::value));
|
||||
VERIFY((is_same<typename skip<4, tl>::type, type_list<dummy_c, dummy_c>>::value));
|
||||
VERIFY((is_same<typename skip<5, tl>::type, type_list<dummy_c>>::value));
|
||||
VERIFY((is_same<typename skip<6, tl>::type, type_list<>>::value));
|
||||
|
||||
VERIFY((is_same<typename skip<0, il>::type, numeric_list<int, 0, 1, 2, 3, 4, 5>>::value));
|
||||
VERIFY((is_same<typename skip<1, il>::type, numeric_list<int, 1, 2, 3, 4, 5>>::value));
|
||||
VERIFY((is_same<typename skip<2, il>::type, numeric_list<int, 2, 3, 4, 5>>::value));
|
||||
VERIFY((is_same<typename skip<3, il>::type, numeric_list<int, 3, 4, 5>>::value));
|
||||
VERIFY((is_same<typename skip<4, il>::type, numeric_list<int, 4, 5>>::value));
|
||||
VERIFY((is_same<typename skip<5, il>::type, numeric_list<int, 5>>::value));
|
||||
VERIFY((is_same<typename skip<6, il>::type, numeric_list<int>>::value));
|
||||
|
||||
VERIFY((is_same<typename slice<0, 3, tl>::type, typename take<3, tl>::type>::value));
|
||||
VERIFY((is_same<typename slice<0, 3, il>::type, typename take<3, il>::type>::value));
|
||||
VERIFY((is_same<typename slice<1, 3, tl>::type, type_list<dummy_a, dummy_b, dummy_b>>::value));
|
||||
VERIFY((is_same<typename slice<1, 3, il>::type, numeric_list<int, 1, 2, 3>>::value));
|
||||
}
|
||||
|
||||
static void test_get()
|
||||
{
|
||||
typedef type_list<dummy_a, dummy_a, dummy_b, dummy_b, dummy_c, dummy_c> tl;
|
||||
typedef numeric_list<int, 4, 8, 15, 16, 23, 42> il;
|
||||
|
||||
VERIFY((is_same<typename get<0, tl>::type, dummy_a>::value));
|
||||
VERIFY((is_same<typename get<1, tl>::type, dummy_a>::value));
|
||||
VERIFY((is_same<typename get<2, tl>::type, dummy_b>::value));
|
||||
VERIFY((is_same<typename get<3, tl>::type, dummy_b>::value));
|
||||
VERIFY((is_same<typename get<4, tl>::type, dummy_c>::value));
|
||||
VERIFY((is_same<typename get<5, tl>::type, dummy_c>::value));
|
||||
|
||||
VERIFY_IS_EQUAL(((int)get<0, il>::value), 4);
|
||||
VERIFY_IS_EQUAL(((int)get<1, il>::value), 8);
|
||||
VERIFY_IS_EQUAL(((int)get<2, il>::value), 15);
|
||||
VERIFY_IS_EQUAL(((int)get<3, il>::value), 16);
|
||||
VERIFY_IS_EQUAL(((int)get<4, il>::value), 23);
|
||||
VERIFY_IS_EQUAL(((int)get<5, il>::value), 42);
|
||||
}
|
||||
|
||||
static void test_id_helper(dummy_a a, dummy_a b, dummy_a c)
|
||||
{
|
||||
(void)a;
|
||||
(void)b;
|
||||
(void)c;
|
||||
}
|
||||
|
||||
template<int... ii>
|
||||
static void test_id_numeric()
|
||||
{
|
||||
test_id_helper(typename id_numeric<int, ii, dummy_a>::type()...);
|
||||
}
|
||||
|
||||
template<typename... tt>
|
||||
static void test_id_type()
|
||||
{
|
||||
test_id_helper(typename id_type<tt, dummy_a>::type()...);
|
||||
}
|
||||
|
||||
static void test_id()
|
||||
{
|
||||
// don't call VERIFY here, just assume it works if it compiles
|
||||
// (otherwise it will complain that it can't find the function)
|
||||
test_id_numeric<1, 4, 6>();
|
||||
test_id_type<dummy_a, dummy_b, dummy_c>();
|
||||
}
|
||||
|
||||
static void test_is_same_gf()
|
||||
{
|
||||
VERIFY((!is_same_gf<dummy_a, dummy_b>::value));
|
||||
VERIFY((!!is_same_gf<dummy_a, dummy_a>::value));
|
||||
VERIFY_IS_EQUAL((!!is_same_gf<dummy_a, dummy_b>::global_flags), false);
|
||||
VERIFY_IS_EQUAL((!!is_same_gf<dummy_a, dummy_a>::global_flags), false);
|
||||
}
|
||||
|
||||
static void test_apply_op()
|
||||
{
|
||||
typedef type_list<dummy_a, dummy_b, dummy_c> tl;
|
||||
VERIFY((!!is_same<typename apply_op_from_left<dummy_op, dummy_a, tl>::type, type_list<dummy_e, dummy_c, dummy_d>>::value));
|
||||
VERIFY((!!is_same<typename apply_op_from_right<dummy_op, dummy_a, tl>::type, type_list<dummy_e, dummy_d, dummy_b>>::value));
|
||||
}
|
||||
|
||||
static void test_contained_in_list()
|
||||
{
|
||||
typedef type_list<dummy_a, dummy_b, dummy_c> tl;
|
||||
|
||||
VERIFY((!!contained_in_list<is_same, dummy_a, tl>::value));
|
||||
VERIFY((!!contained_in_list<is_same, dummy_b, tl>::value));
|
||||
VERIFY((!!contained_in_list<is_same, dummy_c, tl>::value));
|
||||
VERIFY((!contained_in_list<is_same, dummy_d, tl>::value));
|
||||
VERIFY((!contained_in_list<is_same, dummy_e, tl>::value));
|
||||
|
||||
VERIFY((!!contained_in_list_gf<dummy_test, dummy_a, tl>::value));
|
||||
VERIFY((!!contained_in_list_gf<dummy_test, dummy_b, tl>::value));
|
||||
VERIFY((!!contained_in_list_gf<dummy_test, dummy_c, tl>::value));
|
||||
VERIFY((!contained_in_list_gf<dummy_test, dummy_d, tl>::value));
|
||||
VERIFY((!contained_in_list_gf<dummy_test, dummy_e, tl>::value));
|
||||
|
||||
VERIFY_IS_EQUAL(((int)contained_in_list_gf<dummy_test, dummy_a, tl>::global_flags), 1);
|
||||
VERIFY_IS_EQUAL(((int)contained_in_list_gf<dummy_test, dummy_b, tl>::global_flags), 2);
|
||||
VERIFY_IS_EQUAL(((int)contained_in_list_gf<dummy_test, dummy_c, tl>::global_flags), 4);
|
||||
VERIFY_IS_EQUAL(((int)contained_in_list_gf<dummy_test, dummy_d, tl>::global_flags), 0);
|
||||
VERIFY_IS_EQUAL(((int)contained_in_list_gf<dummy_test, dummy_e, tl>::global_flags), 0);
|
||||
}
|
||||
|
||||
static void test_arg_reductions()
|
||||
{
|
||||
VERIFY_IS_EQUAL(arg_sum(1,2,3,4), 10);
|
||||
VERIFY_IS_EQUAL(arg_prod(1,2,3,4), 24);
|
||||
VERIFY_IS_APPROX(arg_sum(0.5, 2, 5), 7.5);
|
||||
VERIFY_IS_APPROX(arg_prod(0.5, 2, 5), 5.0);
|
||||
}
|
||||
|
||||
static void test_array_reverse_and_reduce()
|
||||
{
|
||||
array<int, 6> a{{4, 8, 15, 16, 23, 42}};
|
||||
array<int, 6> b{{42, 23, 16, 15, 8, 4}};
|
||||
|
||||
// there is no operator<< for std::array, so VERIFY_IS_EQUAL will
|
||||
// not compile
|
||||
VERIFY((array_reverse(a) == b));
|
||||
VERIFY((array_reverse(b) == a));
|
||||
VERIFY_IS_EQUAL((array_sum(a)), 108);
|
||||
VERIFY_IS_EQUAL((array_sum(b)), 108);
|
||||
VERIFY_IS_EQUAL((array_prod(a)), 7418880);
|
||||
VERIFY_IS_EQUAL((array_prod(b)), 7418880);
|
||||
}
|
||||
|
||||
static void test_array_zip_and_apply()
|
||||
{
|
||||
array<int, 6> a{{4, 8, 15, 16, 23, 42}};
|
||||
array<int, 6> b{{0, 1, 2, 3, 4, 5}};
|
||||
array<int, 6> c{{4, 9, 17, 19, 27, 47}};
|
||||
array<int, 6> d{{0, 8, 30, 48, 92, 210}};
|
||||
array<int, 6> e{{0, 2, 4, 6, 8, 10}};
|
||||
|
||||
VERIFY((array_zip<sum_op>(a, b) == c));
|
||||
VERIFY((array_zip<product_op>(a, b) == d));
|
||||
VERIFY((array_apply<times2_op>(b) == e));
|
||||
VERIFY_IS_EQUAL((array_apply_and_reduce<sum_op, times2_op>(a)), 216);
|
||||
VERIFY_IS_EQUAL((array_apply_and_reduce<sum_op, times2_op>(b)), 30);
|
||||
VERIFY_IS_EQUAL((array_zip_and_reduce<product_op, sum_op>(a, b)), 14755932);
|
||||
VERIFY_IS_EQUAL((array_zip_and_reduce<sum_op, product_op>(a, b)), 388);
|
||||
}
|
||||
|
||||
static void test_array_misc()
|
||||
{
|
||||
array<int, 3> a3{{1, 1, 1}};
|
||||
array<int, 6> a6{{2, 2, 2, 2, 2, 2}};
|
||||
VERIFY((repeat<3, int>(1) == a3));
|
||||
VERIFY((repeat<6, int>(2) == a6));
|
||||
|
||||
int data[5] = { 0, 1, 2, 3, 4 };
|
||||
VERIFY_IS_EQUAL((instantiate_by_c_array<dummy_inst, int, 0>(data).c), 0);
|
||||
VERIFY_IS_EQUAL((instantiate_by_c_array<dummy_inst, int, 1>(data).c), 1);
|
||||
VERIFY_IS_EQUAL((instantiate_by_c_array<dummy_inst, int, 2>(data).c), 2);
|
||||
VERIFY_IS_EQUAL((instantiate_by_c_array<dummy_inst, int, 3>(data).c), 3);
|
||||
VERIFY_IS_EQUAL((instantiate_by_c_array<dummy_inst, int, 4>(data).c), 4);
|
||||
VERIFY_IS_EQUAL((instantiate_by_c_array<dummy_inst, int, 5>(data).c), 5);
|
||||
}
|
||||
|
||||
EIGEN_DECLARE_TEST(cxx11_meta)
|
||||
{
|
||||
CALL_SUBTEST(test_gen_numeric_list());
|
||||
CALL_SUBTEST(test_concat());
|
||||
CALL_SUBTEST(test_slice());
|
||||
CALL_SUBTEST(test_get());
|
||||
CALL_SUBTEST(test_id());
|
||||
CALL_SUBTEST(test_is_same_gf());
|
||||
CALL_SUBTEST(test_apply_op());
|
||||
CALL_SUBTEST(test_contained_in_list());
|
||||
CALL_SUBTEST(test_arg_reductions());
|
||||
CALL_SUBTEST(test_array_reverse_and_reduce());
|
||||
CALL_SUBTEST(test_array_zip_and_apply());
|
||||
CALL_SUBTEST(test_array_misc());
|
||||
}
|
||||
|
||||
142
test/threads_eventcount.cpp
Normal file
142
test/threads_eventcount.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// Copyright (C) 2016 Dmitry Vyukov <dvyukov@google.com>
|
||||
// Copyright (C) 2016 Benoit Steiner <benoit.steiner.goog@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#define EIGEN_USE_THREADS
|
||||
#include "main.h"
|
||||
#include <Eigen/ThreadPool>
|
||||
|
||||
// Visual studio doesn't implement a rand_r() function since its
|
||||
// implementation of rand() is already thread safe
|
||||
int rand_reentrant(unsigned int* s) {
|
||||
#if EIGEN_COMP_MSVC_STRICT
|
||||
EIGEN_UNUSED_VARIABLE(s);
|
||||
return rand();
|
||||
#else
|
||||
return rand_r(s);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void test_basic_eventcount()
|
||||
{
|
||||
MaxSizeVector<EventCount::Waiter> waiters(1);
|
||||
waiters.resize(1);
|
||||
EventCount ec(waiters);
|
||||
EventCount::Waiter& w = waiters[0];
|
||||
ec.Notify(false);
|
||||
ec.Prewait();
|
||||
ec.Notify(true);
|
||||
ec.CommitWait(&w);
|
||||
ec.Prewait();
|
||||
ec.CancelWait();
|
||||
}
|
||||
|
||||
// Fake bounded counter-based queue.
|
||||
struct TestQueue {
|
||||
std::atomic<int> val_;
|
||||
static const int kQueueSize = 10;
|
||||
|
||||
TestQueue() : val_() {}
|
||||
|
||||
~TestQueue() { VERIFY_IS_EQUAL(val_.load(), 0); }
|
||||
|
||||
bool Push() {
|
||||
int val = val_.load(std::memory_order_relaxed);
|
||||
for (;;) {
|
||||
VERIFY_GE(val, 0);
|
||||
VERIFY_LE(val, kQueueSize);
|
||||
if (val == kQueueSize) return false;
|
||||
if (val_.compare_exchange_weak(val, val + 1, std::memory_order_relaxed))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool Pop() {
|
||||
int val = val_.load(std::memory_order_relaxed);
|
||||
for (;;) {
|
||||
VERIFY_GE(val, 0);
|
||||
VERIFY_LE(val, kQueueSize);
|
||||
if (val == 0) return false;
|
||||
if (val_.compare_exchange_weak(val, val - 1, std::memory_order_relaxed))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool Empty() { return val_.load(std::memory_order_relaxed) == 0; }
|
||||
};
|
||||
|
||||
const int TestQueue::kQueueSize;
|
||||
|
||||
// A number of producers send messages to a set of consumers using a set of
|
||||
// fake queues. Ensure that it does not crash, consumers don't deadlock and
|
||||
// number of blocked and unblocked threads match.
|
||||
static void test_stress_eventcount()
|
||||
{
|
||||
const int kThreads = std::thread::hardware_concurrency();
|
||||
static const int kEvents = 1 << 16;
|
||||
static const int kQueues = 10;
|
||||
|
||||
MaxSizeVector<EventCount::Waiter> waiters(kThreads);
|
||||
waiters.resize(kThreads);
|
||||
EventCount ec(waiters);
|
||||
TestQueue queues[kQueues];
|
||||
|
||||
std::vector<std::unique_ptr<std::thread>> producers;
|
||||
for (int i = 0; i < kThreads; i++) {
|
||||
producers.emplace_back(new std::thread([&ec, &queues]() {
|
||||
unsigned int rnd = static_cast<unsigned int>(std::hash<std::thread::id>()(std::this_thread::get_id()));
|
||||
for (int j = 0; j < kEvents; j++) {
|
||||
unsigned idx = rand_reentrant(&rnd) % kQueues;
|
||||
if (queues[idx].Push()) {
|
||||
ec.Notify(false);
|
||||
continue;
|
||||
}
|
||||
EIGEN_THREAD_YIELD();
|
||||
j--;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<std::thread>> consumers;
|
||||
for (int i = 0; i < kThreads; i++) {
|
||||
consumers.emplace_back(new std::thread([&ec, &queues, &waiters, i]() {
|
||||
EventCount::Waiter& w = waiters[i];
|
||||
unsigned int rnd = static_cast<unsigned int>(std::hash<std::thread::id>()(std::this_thread::get_id()));
|
||||
for (int j = 0; j < kEvents; j++) {
|
||||
unsigned idx = rand_reentrant(&rnd) % kQueues;
|
||||
if (queues[idx].Pop()) continue;
|
||||
j--;
|
||||
ec.Prewait();
|
||||
bool empty = true;
|
||||
for (int q = 0; q < kQueues; q++) {
|
||||
if (!queues[q].Empty()) {
|
||||
empty = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!empty) {
|
||||
ec.CancelWait();
|
||||
continue;
|
||||
}
|
||||
ec.CommitWait(&w);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
for (int i = 0; i < kThreads; i++) {
|
||||
producers[i]->join();
|
||||
consumers[i]->join();
|
||||
}
|
||||
}
|
||||
|
||||
EIGEN_DECLARE_TEST(cxx11_eventcount)
|
||||
{
|
||||
CALL_SUBTEST(test_basic_eventcount());
|
||||
CALL_SUBTEST(test_stress_eventcount());
|
||||
}
|
||||
179
test/threads_non_blocking_thread_pool.cpp
Normal file
179
test/threads_non_blocking_thread_pool.cpp
Normal file
@@ -0,0 +1,179 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// Copyright (C) 2016 Dmitry Vyukov <dvyukov@google.com>
|
||||
// Copyright (C) 2016 Benoit Steiner <benoit.steiner.goog@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#define EIGEN_USE_THREADS
|
||||
#include "main.h"
|
||||
#include "Eigen/ThreadPool"
|
||||
|
||||
static void test_create_destroy_empty_pool()
|
||||
{
|
||||
// Just create and destroy the pool. This will wind up and tear down worker
|
||||
// threads. Ensure there are no issues in that logic.
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
ThreadPool tp(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void test_parallelism(bool allow_spinning)
|
||||
{
|
||||
// Test we never-ever fail to match available tasks with idle threads.
|
||||
const int kThreads = 16; // code below expects that this is a multiple of 4
|
||||
ThreadPool tp(kThreads, allow_spinning);
|
||||
VERIFY_IS_EQUAL(tp.NumThreads(), kThreads);
|
||||
VERIFY_IS_EQUAL(tp.CurrentThreadId(), -1);
|
||||
for (int iter = 0; iter < 100; ++iter) {
|
||||
std::atomic<int> running(0);
|
||||
std::atomic<int> done(0);
|
||||
std::atomic<int> phase(0);
|
||||
// Schedule kThreads tasks and ensure that they all are running.
|
||||
for (int i = 0; i < kThreads; ++i) {
|
||||
tp.Schedule([&]() {
|
||||
const int thread_id = tp.CurrentThreadId();
|
||||
VERIFY_GE(thread_id, 0);
|
||||
VERIFY_LE(thread_id, kThreads - 1);
|
||||
running++;
|
||||
while (phase < 1) {
|
||||
}
|
||||
done++;
|
||||
});
|
||||
}
|
||||
while (running != kThreads) {
|
||||
}
|
||||
running = 0;
|
||||
phase = 1;
|
||||
// Now, while the previous tasks exit, schedule another kThreads tasks and
|
||||
// ensure that they are running.
|
||||
for (int i = 0; i < kThreads; ++i) {
|
||||
tp.Schedule([&, i]() {
|
||||
running++;
|
||||
while (phase < 2) {
|
||||
}
|
||||
// When all tasks are running, half of tasks exit, quarter of tasks
|
||||
// continue running and quarter of tasks schedule another 2 tasks each.
|
||||
// Concurrently main thread schedules another quarter of tasks.
|
||||
// This gives us another kThreads tasks and we ensure that they all
|
||||
// are running.
|
||||
if (i < kThreads / 2) {
|
||||
} else if (i < 3 * kThreads / 4) {
|
||||
running++;
|
||||
while (phase < 3) {
|
||||
}
|
||||
done++;
|
||||
} else {
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
tp.Schedule([&]() {
|
||||
running++;
|
||||
while (phase < 3) {
|
||||
}
|
||||
done++;
|
||||
});
|
||||
}
|
||||
}
|
||||
done++;
|
||||
});
|
||||
}
|
||||
while (running != kThreads) {
|
||||
}
|
||||
running = 0;
|
||||
phase = 2;
|
||||
for (int i = 0; i < kThreads / 4; ++i) {
|
||||
tp.Schedule([&]() {
|
||||
running++;
|
||||
while (phase < 3) {
|
||||
}
|
||||
done++;
|
||||
});
|
||||
}
|
||||
while (running != kThreads) {
|
||||
}
|
||||
phase = 3;
|
||||
while (done != 3 * kThreads) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void test_cancel()
|
||||
{
|
||||
ThreadPool tp(2);
|
||||
|
||||
// Schedule a large number of closure that each sleeps for one second. This
|
||||
// will keep the thread pool busy for much longer than the default test timeout.
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
tp.Schedule([]() {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
|
||||
});
|
||||
}
|
||||
|
||||
// Cancel the processing of all the closures that are still pending.
|
||||
tp.Cancel();
|
||||
}
|
||||
|
||||
static void test_pool_partitions() {
|
||||
const int kThreads = 2;
|
||||
ThreadPool tp(kThreads);
|
||||
|
||||
// Assign each thread to its own partition, so that stealing other work only
|
||||
// occurs globally when a thread is idle.
|
||||
std::vector<std::pair<unsigned, unsigned>> steal_partitions(kThreads);
|
||||
for (int i = 0; i < kThreads; ++i) {
|
||||
steal_partitions[i] = std::make_pair(i, i + 1);
|
||||
}
|
||||
tp.SetStealPartitions(steal_partitions);
|
||||
|
||||
std::atomic<int> running(0);
|
||||
std::atomic<int> done(0);
|
||||
std::atomic<int> phase(0);
|
||||
|
||||
// Schedule kThreads tasks and ensure that they all are running.
|
||||
for (int i = 0; i < kThreads; ++i) {
|
||||
tp.Schedule([&]() {
|
||||
const int thread_id = tp.CurrentThreadId();
|
||||
VERIFY_GE(thread_id, 0);
|
||||
VERIFY_LE(thread_id, kThreads - 1);
|
||||
++running;
|
||||
while (phase < 1) {
|
||||
}
|
||||
++done;
|
||||
});
|
||||
}
|
||||
while (running != kThreads) {
|
||||
}
|
||||
// Schedule each closure to only run on thread 'i' and verify that it does.
|
||||
for (int i = 0; i < kThreads; ++i) {
|
||||
tp.ScheduleWithHint(
|
||||
[&, i]() {
|
||||
++running;
|
||||
const int thread_id = tp.CurrentThreadId();
|
||||
VERIFY_IS_EQUAL(thread_id, i);
|
||||
while (phase < 2) {
|
||||
}
|
||||
++done;
|
||||
},
|
||||
i, i + 1);
|
||||
}
|
||||
running = 0;
|
||||
phase = 1;
|
||||
while (running != kThreads) {
|
||||
}
|
||||
running = 0;
|
||||
phase = 2;
|
||||
}
|
||||
|
||||
|
||||
EIGEN_DECLARE_TEST(cxx11_non_blocking_thread_pool)
|
||||
{
|
||||
CALL_SUBTEST(test_create_destroy_empty_pool());
|
||||
CALL_SUBTEST(test_parallelism(true));
|
||||
CALL_SUBTEST(test_parallelism(false));
|
||||
CALL_SUBTEST(test_cancel());
|
||||
CALL_SUBTEST(test_pool_partitions());
|
||||
}
|
||||
235
test/threads_runqueue.cpp
Normal file
235
test/threads_runqueue.cpp
Normal file
@@ -0,0 +1,235 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// Copyright (C) 2016 Dmitry Vyukov <dvyukov@google.com>
|
||||
// Copyright (C) 2016 Benoit Steiner <benoit.steiner.goog@gmail.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla
|
||||
// Public License v. 2.0. If a copy of the MPL was not distributed
|
||||
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#define EIGEN_USE_THREADS
|
||||
#include <cstdlib>
|
||||
#include "main.h"
|
||||
#include <Eigen/ThreadPool>
|
||||
|
||||
|
||||
// Visual studio doesn't implement a rand_r() function since its
|
||||
// implementation of rand() is already thread safe
|
||||
int rand_reentrant(unsigned int* s) {
|
||||
#if EIGEN_COMP_MSVC_STRICT
|
||||
EIGEN_UNUSED_VARIABLE(s);
|
||||
return rand();
|
||||
#else
|
||||
return rand_r(s);
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_basic_runqueue()
|
||||
{
|
||||
RunQueue<int, 4> q;
|
||||
// Check empty state.
|
||||
VERIFY(q.Empty());
|
||||
VERIFY_IS_EQUAL(0u, q.Size());
|
||||
VERIFY_IS_EQUAL(0, q.PopFront());
|
||||
std::vector<int> stolen;
|
||||
VERIFY_IS_EQUAL(0u, q.PopBackHalf(&stolen));
|
||||
VERIFY_IS_EQUAL(0u, stolen.size());
|
||||
// Push one front, pop one front.
|
||||
VERIFY_IS_EQUAL(0, q.PushFront(1));
|
||||
VERIFY_IS_EQUAL(1u, q.Size());
|
||||
VERIFY_IS_EQUAL(1, q.PopFront());
|
||||
VERIFY_IS_EQUAL(0u, q.Size());
|
||||
// Push front to overflow.
|
||||
VERIFY_IS_EQUAL(0, q.PushFront(2));
|
||||
VERIFY_IS_EQUAL(1u, q.Size());
|
||||
VERIFY_IS_EQUAL(0, q.PushFront(3));
|
||||
VERIFY_IS_EQUAL(2u, q.Size());
|
||||
VERIFY_IS_EQUAL(0, q.PushFront(4));
|
||||
VERIFY_IS_EQUAL(3u, q.Size());
|
||||
VERIFY_IS_EQUAL(0, q.PushFront(5));
|
||||
VERIFY_IS_EQUAL(4u, q.Size());
|
||||
VERIFY_IS_EQUAL(6, q.PushFront(6));
|
||||
VERIFY_IS_EQUAL(4u, q.Size());
|
||||
VERIFY_IS_EQUAL(5, q.PopFront());
|
||||
VERIFY_IS_EQUAL(3u, q.Size());
|
||||
VERIFY_IS_EQUAL(4, q.PopFront());
|
||||
VERIFY_IS_EQUAL(2u, q.Size());
|
||||
VERIFY_IS_EQUAL(3, q.PopFront());
|
||||
VERIFY_IS_EQUAL(1u, q.Size());
|
||||
VERIFY_IS_EQUAL(2, q.PopFront());
|
||||
VERIFY_IS_EQUAL(0u, q.Size());
|
||||
VERIFY_IS_EQUAL(0, q.PopFront());
|
||||
// Push one back, pop one back.
|
||||
VERIFY_IS_EQUAL(0, q.PushBack(7));
|
||||
VERIFY_IS_EQUAL(1u, q.Size());
|
||||
VERIFY_IS_EQUAL(1u, q.PopBackHalf(&stolen));
|
||||
VERIFY_IS_EQUAL(1u, stolen.size());
|
||||
VERIFY_IS_EQUAL(7, stolen[0]);
|
||||
VERIFY_IS_EQUAL(0u, q.Size());
|
||||
stolen.clear();
|
||||
// Push back to overflow.
|
||||
VERIFY_IS_EQUAL(0, q.PushBack(8));
|
||||
VERIFY_IS_EQUAL(1u, q.Size());
|
||||
VERIFY_IS_EQUAL(0, q.PushBack(9));
|
||||
VERIFY_IS_EQUAL(2u, q.Size());
|
||||
VERIFY_IS_EQUAL(0, q.PushBack(10));
|
||||
VERIFY_IS_EQUAL(3u, q.Size());
|
||||
VERIFY_IS_EQUAL(0, q.PushBack(11));
|
||||
VERIFY_IS_EQUAL(4u, q.Size());
|
||||
VERIFY_IS_EQUAL(12, q.PushBack(12));
|
||||
VERIFY_IS_EQUAL(4u, q.Size());
|
||||
// Pop back in halves.
|
||||
VERIFY_IS_EQUAL(2u, q.PopBackHalf(&stolen));
|
||||
VERIFY_IS_EQUAL(2u, stolen.size());
|
||||
VERIFY_IS_EQUAL(10, stolen[0]);
|
||||
VERIFY_IS_EQUAL(11, stolen[1]);
|
||||
VERIFY_IS_EQUAL(2u, q.Size());
|
||||
stolen.clear();
|
||||
VERIFY_IS_EQUAL(1u, q.PopBackHalf(&stolen));
|
||||
VERIFY_IS_EQUAL(1u, stolen.size());
|
||||
VERIFY_IS_EQUAL(9, stolen[0]);
|
||||
VERIFY_IS_EQUAL(1u, q.Size());
|
||||
stolen.clear();
|
||||
VERIFY_IS_EQUAL(1u, q.PopBackHalf(&stolen));
|
||||
VERIFY_IS_EQUAL(1u, stolen.size());
|
||||
VERIFY_IS_EQUAL(8, stolen[0]);
|
||||
stolen.clear();
|
||||
VERIFY_IS_EQUAL(0u, q.PopBackHalf(&stolen));
|
||||
VERIFY_IS_EQUAL(0u, stolen.size());
|
||||
// Empty again.
|
||||
VERIFY(q.Empty());
|
||||
VERIFY_IS_EQUAL(0u, q.Size());
|
||||
VERIFY_IS_EQUAL(0, q.PushFront(1));
|
||||
VERIFY_IS_EQUAL(0, q.PushFront(2));
|
||||
VERIFY_IS_EQUAL(0, q.PushFront(3));
|
||||
VERIFY_IS_EQUAL(1, q.PopBack());
|
||||
VERIFY_IS_EQUAL(2, q.PopBack());
|
||||
VERIFY_IS_EQUAL(3, q.PopBack());
|
||||
VERIFY(q.Empty());
|
||||
VERIFY_IS_EQUAL(0u, q.Size());
|
||||
}
|
||||
|
||||
// Empty tests that the queue is not claimed to be empty when is is in fact not.
|
||||
// Emptiness property is crucial part of thread pool blocking scheme,
|
||||
// so we go to great effort to ensure this property. We create a queue with
|
||||
// 1 element and then push 1 element (either front or back at random) and pop
|
||||
// 1 element (either front or back at random). So queue always contains at least
|
||||
// 1 element, but otherwise changes chaotically. Another thread constantly tests
|
||||
// that the queue is not claimed to be empty.
|
||||
void test_empty_runqueue()
|
||||
{
|
||||
RunQueue<int, 4> q;
|
||||
q.PushFront(1);
|
||||
std::atomic<bool> done(false);
|
||||
std::thread mutator([&q, &done]() {
|
||||
unsigned rnd = 0;
|
||||
std::vector<int> stolen;
|
||||
for (int i = 0; i < 1 << 18; i++) {
|
||||
if (rand_reentrant(&rnd) % 2)
|
||||
VERIFY_IS_EQUAL(0, q.PushFront(1));
|
||||
else
|
||||
VERIFY_IS_EQUAL(0, q.PushBack(1));
|
||||
if (rand_reentrant(&rnd) % 2)
|
||||
VERIFY_IS_EQUAL(1, q.PopFront());
|
||||
else {
|
||||
for (;;) {
|
||||
if (q.PopBackHalf(&stolen) == 1) {
|
||||
stolen.clear();
|
||||
break;
|
||||
}
|
||||
VERIFY_IS_EQUAL(0u, stolen.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
done = true;
|
||||
});
|
||||
while (!done) {
|
||||
VERIFY(!q.Empty());
|
||||
int size = q.Size();
|
||||
VERIFY_GE(size, 1);
|
||||
VERIFY_LE(size, 2);
|
||||
}
|
||||
VERIFY_IS_EQUAL(1, q.PopFront());
|
||||
mutator.join();
|
||||
}
|
||||
|
||||
// Stress is a chaotic random test.
|
||||
// One thread (owner) calls PushFront/PopFront, other threads call PushBack/
|
||||
// PopBack. Ensure that we don't crash, deadlock, and all sanity checks pass.
|
||||
void test_stress_runqueue()
|
||||
{
|
||||
static const int kEvents = 1 << 18;
|
||||
RunQueue<int, 8> q;
|
||||
std::atomic<int> total(0);
|
||||
std::vector<std::unique_ptr<std::thread>> threads;
|
||||
threads.emplace_back(new std::thread([&q, &total]() {
|
||||
int sum = 0;
|
||||
int pushed = 1;
|
||||
int popped = 1;
|
||||
while (pushed < kEvents || popped < kEvents) {
|
||||
if (pushed < kEvents) {
|
||||
if (q.PushFront(pushed) == 0) {
|
||||
sum += pushed;
|
||||
pushed++;
|
||||
}
|
||||
}
|
||||
if (popped < kEvents) {
|
||||
int v = q.PopFront();
|
||||
if (v != 0) {
|
||||
sum -= v;
|
||||
popped++;
|
||||
}
|
||||
}
|
||||
}
|
||||
total += sum;
|
||||
}));
|
||||
for (int i = 0; i < 2; i++) {
|
||||
threads.emplace_back(new std::thread([&q, &total]() {
|
||||
int sum = 0;
|
||||
for (int j = 1; j < kEvents; j++) {
|
||||
if (q.PushBack(j) == 0) {
|
||||
sum += j;
|
||||
continue;
|
||||
}
|
||||
EIGEN_THREAD_YIELD();
|
||||
j--;
|
||||
}
|
||||
total += sum;
|
||||
}));
|
||||
threads.emplace_back(new std::thread([&q, &total]() {
|
||||
int sum = 0;
|
||||
std::vector<int> stolen;
|
||||
for (int j = 1; j < kEvents;) {
|
||||
if (q.PopBackHalf(&stolen) == 0) {
|
||||
EIGEN_THREAD_YIELD();
|
||||
continue;
|
||||
}
|
||||
while (stolen.size() && j < kEvents) {
|
||||
int v = stolen.back();
|
||||
stolen.pop_back();
|
||||
VERIFY_IS_NOT_EQUAL(v, 0);
|
||||
sum += v;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
while (stolen.size()) {
|
||||
int v = stolen.back();
|
||||
stolen.pop_back();
|
||||
VERIFY_IS_NOT_EQUAL(v, 0);
|
||||
while ((v = q.PushBack(v)) != 0) EIGEN_THREAD_YIELD();
|
||||
}
|
||||
total -= sum;
|
||||
}));
|
||||
}
|
||||
for (size_t i = 0; i < threads.size(); i++) threads[i]->join();
|
||||
VERIFY(q.Empty());
|
||||
VERIFY(total.load() == 0);
|
||||
}
|
||||
|
||||
EIGEN_DECLARE_TEST(cxx11_runqueue)
|
||||
{
|
||||
CALL_SUBTEST_1(test_basic_runqueue());
|
||||
CALL_SUBTEST_2(test_empty_runqueue());
|
||||
CALL_SUBTEST_3(test_stress_runqueue());
|
||||
}
|
||||
Reference in New Issue
Block a user