Thread pool

This commit is contained in:
Tobias Wood
2023-05-05 16:23:34 +00:00
committed by Rasmus Munk Larsen
parent 9eb8e2afba
commit 94f57867fe
25 changed files with 937 additions and 446 deletions

View File

@@ -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
View 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';
}
}

View File

@@ -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
View 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());
}

View 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
View 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());
}