File: trecursivefor.cpp

package info (click to toggle)
wsclean 3.7-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 10,968 kB
  • sloc: cpp: 85,742; python: 3,526; sh: 245; makefile: 21
file content (143 lines) | stat: -rw-r--r-- 4,182 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#include <atomic>
#include <iostream>
#include <cmath>

#include <unistd.h>  // for usleep

#include <aocommon/recursivefor.h>

#include <boost/test/unit_test.hpp>

using namespace aocommon;

namespace {
void RunSingleLoop(bool nested = false) {
  std::mutex mutex;
  std::vector<size_t> counts(10, 0);
  auto function = [&](size_t iter, size_t) {
    std::unique_lock<std::mutex> lock(mutex);
    counts[iter]++;
  };
  if (nested) {
    RecursiveFor::NestedRun(0, 10, function);
  } else {
    RecursiveFor loop;
    loop.Run(0, 10, function);
  }
  std::vector<size_t> ref(10, 1);
  BOOST_CHECK_EQUAL_COLLECTIONS(counts.begin(), counts.end(), ref.begin(),
                                ref.end());
}
}  // namespace

BOOST_AUTO_TEST_SUITE(recursive_for)

BOOST_AUTO_TEST_CASE(construction_single_threaded) {
  ThreadPool::GetInstance().SetNThreads(1);
  BOOST_CHECK(RecursiveFor::GetInstance() == nullptr);
  RecursiveFor loop;
  BOOST_CHECK(RecursiveFor::GetInstance() == &loop);
}

BOOST_AUTO_TEST_CASE(construction_multi_threaded) {
  ThreadPool::GetInstance().SetNThreads(4);
  BOOST_CHECK(RecursiveFor::GetInstance() == nullptr);
  RecursiveFor loop;
  BOOST_CHECK(RecursiveFor::GetInstance() == &loop);
}

BOOST_AUTO_TEST_CASE(serial_single_loop) {
  ThreadPool::GetInstance().SetNThreads(1);
  RunSingleLoop();
}

BOOST_AUTO_TEST_CASE(parallel_single_loop) {
  ThreadPool::GetInstance().SetNThreads(4);
  RunSingleLoop();
}

BOOST_AUTO_TEST_CASE(recursive) {
  ThreadPool::GetInstance().SetNThreads(4);
  RecursiveFor loop;
  std::mutex mutex;
  std::vector<size_t> counts(800, 0);
  loop.Run(0, 100, [&](size_t iter1, size_t) {
    loop.Run(0, 8, [&](size_t iter2, size_t) {
      std::unique_lock<std::mutex> lock(mutex);
      counts[iter1 + iter2 * 100]++;
    });
  });
  std::vector<size_t> ref(800, 1);
  BOOST_CHECK_EQUAL_COLLECTIONS(counts.begin(), counts.end(), ref.begin(),
                                ref.end());
}

BOOST_AUTO_TEST_CASE(slow_task) {
  constexpr size_t kNThread = 4;
  ThreadPool::GetInstance().SetNThreads(kNThread);
  std::mutex mutex;
  RecursiveFor loop;
  std::vector<size_t> counts(5 * kNThread, 0);
  loop.Run(0, 5 * kNThread, [&](size_t iter, size_t) {
    usleep(1000);
    std::unique_lock<std::mutex> lock(mutex);
    counts[iter]++;
  });
  std::vector<size_t> ref(5 * kNThread, 1);
  BOOST_CHECK_EQUAL_COLLECTIONS(counts.begin(), counts.end(), ref.begin(),
                                ref.end());
}

BOOST_AUTO_TEST_CASE(nested_run) {
  ThreadPool::GetInstance().SetNThreads(4);
  // Call NestedRun() without an active recursive for:
  RunSingleLoop(true);
  // Same, but now with an active recursive for:
  const RecursiveFor outer_recursive_for;
  RunSingleLoop(true);
}

BOOST_AUTO_TEST_CASE(reuse) {
  ThreadPool::GetInstance().SetNThreads(4);
  RecursiveFor recursive_for;
  for (size_t i = 0; i != 100; ++i) {
    volatile double x = 0.1;
    recursive_for.Run(0, 100, [&](size_t, size_t) { (void)std::sin(x); });
  }
}

BOOST_AUTO_TEST_CASE(exception_single_threaded) {
  ThreadPool::GetInstance().SetNThreads(1);
  RecursiveFor loop;
  auto test_call = [&loop]() {
    loop.Run(0, 1000, [](size_t) { throw std::exception(); });
  };
  BOOST_CHECK_THROW(test_call(), std::exception);
}

BOOST_AUTO_TEST_CASE(exception_multi_threaded) {
  ThreadPool::GetInstance().SetNThreads(4);
  RecursiveFor loop;
  auto test_call = [&loop](size_t tested_thread) {
    std::atomic<bool> found(false);
    loop.Run(0, 1000, [tested_thread, &found](size_t, size_t thread_index) {
      if (thread_index == tested_thread) {
        found = true;
        throw std::exception();
      } else {
        // Non-throwing threads are delayed to make sure that the
        // requested thread gets to run an iteration. Otherwise, other
        // threads might process all iterations before the requested
        // thread starts, causing it to never throw.
        while (!found) {
        }
      }
    });
  };
  // Exceptions in thread 0 and other threads are handled differently,
  // so test both
  BOOST_CHECK_THROW(test_call(0), std::exception);
  BOOST_CHECK_THROW(test_call(3), std::exception);
}

BOOST_AUTO_TEST_SUITE_END()