Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: test/AsyncExecutor.cpp

Issue 29706560: Issue 5179 - Implement asynchronous executor with a controllable lifetime (Closed) Base URL: https://github.com/adblockplus/libadblockplus.git
Patch Set: address comments Created March 1, 2018, 11:14 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/Platform.cpp ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-present eyeo GmbH
4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation.
8 *
9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <future>
19 #include <gtest/gtest.h>
20 #include <AdblockPlus/AsyncExecutor.h>
21
22 using namespace AdblockPlus;
23
24
25 // For present gtest does not provide public API to have value- and type-
26 // parameterized tests, so the approach is to accumalte the common code in
27 // BaseAsyncExecutorTest, then define it with different types and then for each
28 // type instantiate value-parameterized tests and call the common test body.
29
30 template<typename Executor>
31 struct BaseAsyncExecutorTestTraits;
32
33 typedef ::testing::tuple<uint16_t, // number of procucers
34 uint16_t // number of tasks issued by each producer
35 > BaseAsyncExecutorTestParams;
36
37 template<typename TExecutor>
38 class BaseAsyncExecutorTest : public ::testing::TestWithParam<BaseAsyncExecutorT estParams>
39 {
40 public:
41 typedef typename ::testing::tuple_element<0, ParamType>::type ProducersNumber;
42 typedef typename ::testing::tuple_element<1, ParamType>::type ProducerTasksNum ber;
43
44 typedef BaseAsyncExecutorTestTraits<TExecutor> Traits;
45 typedef typename Traits::Executor Executor;
46
47 protected:
48 void SetUp() override
49 {
50 testThreadID = std::this_thread::get_id();
51 }
52
53 void MultithreadedCallsTest();
54
55 std::thread::id testThreadID;
56 };
57
58 template<>
59 struct BaseAsyncExecutorTestTraits<ActiveObject>
60 {
61 typedef ActiveObject Executor;
62 typedef std::vector<uint32_t> PayloadResults;
63
64 static void Dispatch(typename BaseAsyncExecutorTest<Executor>::Executor& execu tor, std::function<void()> call)
65 {
66 executor.Post(std::move(call));
67 }
68 };
69
70 template<>
71 struct BaseAsyncExecutorTestTraits<AsyncExecutor>
72 {
73 typedef AsyncExecutor Executor;
74
75 // The API of this class is not thread-safe! It's merely a helper to
76 // generalize particular tests, don't use it anywhere else.
77 class PayloadResults : public SynchronizedCollection<std::vector<uint32_t>>
78 {
79 public:
80 typename Container::size_type size() const
81 {
82 return collection.size();
83 }
84 typename Container::iterator begin()
85 {
86 return collection.begin();
87 }
88 typename Container::iterator end()
89 {
90 return collection.end();
91 }
92 typename Container::const_reference operator[](typename Container::size_type pos) const
93 {
94 return collection[pos];
95 }
96 };
97
98 static void Dispatch(typename BaseAsyncExecutorTest<Executor>::Executor& execu tor, std::function<void()> call)
99 {
100 executor.Dispatch(std::move(call));
101 }
102 };
103
104 template<typename Executor>
105 void BaseAsyncExecutorTest<Executor>::MultithreadedCallsTest()
106 {
107 typename Traits::PayloadResults results;
108
109 std::promise<void> prepare;
110 std::shared_future<void> asyncPrepared = prepare.get_future();
111 const ProducersNumber producersNumber = ::testing::get<0>(GetParam());
112 const ProducerTasksNumber producerTasksNumber = ::testing::get<1>(GetParam());
113 const uint32_t totalTasksNumber = producersNumber * producerTasksNumber;
114 {
115 Executor executor;
116 {
117 // Defining AsyncExecutor (async) after tested Executor ensures that all
118 // producers finish before destroying the Executor. Otherwise there could
119 // be cases when the Executor is already destroyed but payload task is not
120 // dispatched yet, what would result in a data race.
121 // It seems at least strange to use AsyncExecutor in the test of
122 // AsyncExecutor, but perhaps it can be considered as addition testing.
123 AsyncExecutor async;
124 for (ProducersNumber producer = 0; producer < producersNumber; ++producer)
125 {
126 auto producerTask = [producerTasksNumber, this, &executor, producer, &re sults](std::thread::id producerThreadID)
127 {
128 for (ProducerTasksNumber task = 0; task < producerTasksNumber; ++task)
129 {
130 auto id = producer * producerTasksNumber + task;
131 Traits::Dispatch(executor, /*payload task*/[this, id, &results, prod ucerThreadID]
132 {
133 results.push_back(id);
134 EXPECT_NE(producerThreadID, std::this_thread::get_id());
135 EXPECT_NE(testThreadID, std::this_thread::get_id());
136 });
137 }
138 };
139 async.Dispatch([this, producerTask, asyncPrepared]
140 {
141 const auto producerThreadID = std::this_thread::get_id();
142 EXPECT_NE(testThreadID, producerThreadID);
143 asyncPrepared.wait();
144 producerTask(producerThreadID);
145 });
146 std::this_thread::yield();
147 }
148 // it's not ideal because some threads can still have not reached
149 // asyncPrepared.wait() but it's good enough, in particular yield should
150 // be helpful there.
151 prepare.set_value();
152 }
153 }
154
155 std::sort(results.begin(), results.end());
156 // ensure that all tasks are executed by finding their IDs
157 ASSERT_EQ(totalTasksNumber, results.size());
158 for (uint16_t id = 0; id < totalTasksNumber; ++id) {
159 EXPECT_EQ(id, results[id]);
160 }
161 }
162
163 namespace
164 {
165
166 std::string humanReadbleParams(const ::testing::TestParamInfo<BaseAsyncExecuto rTestParams> paramInfo)
167 {
168 std::stringstream ss;
169 ss << "producers_" << ::testing::get<0>(paramInfo.param) << "_tasks_" << ::t esting::get<1>(paramInfo.param);
170 return ss.str();
171 }
172
173 // The reason for such splitting for different implementations and different
174 // platforms is the limited number of threads and time to run tests on
175 // travis-ci.
176 // E.g. 100 x 1000 is already too much, so the generators are split.
177 // 0 - 10 1 - 100, 1000
178 const auto MultithreadedCallsGenerator1 = ::testing::Combine(::testing::Values (0, 1, 2, 3, 5, 10),
179 ::testing::Values (1, 2, 3, 5, 10, 100, 1000));
180 // 100, 1 - 100
181 const auto MultithreadedCallsGenerator2 = ::testing::Combine(::testing::Values (100),
182 ::testing::Values (1, 2, 3, 5, 10, 100));
183 // 0 - 10 2000|4000
184 const auto MultithreadedCallsGenerator3 = ::testing::Combine(::testing::Values (0, 1, 2, 3, 5, 10),
185 #ifdef WIN32
186 ::testing::Values(4000)
187 #else
188 ::testing::Values(2000)
189 #endif
190 );
191
192 typedef BaseAsyncExecutorTest<ActiveObject> ActiveObjectTest;
193
194 INSTANTIATE_TEST_CASE_P(DifferentProducersNumber1,
195 ActiveObjectTest,
196 MultithreadedCallsGenerator1,
197 humanReadbleParams);
198
199 INSTANTIATE_TEST_CASE_P(DifferentProducersNumber2,
200 ActiveObjectTest,
201 MultithreadedCallsGenerator2,
202 humanReadbleParams);
203
204 INSTANTIATE_TEST_CASE_P(DifferentProducersNumber3,
205 ActiveObjectTest,
206 MultithreadedCallsGenerator3,
207 humanReadbleParams);
208
209 TEST_P(ActiveObjectTest, MultithreadedCalls)
210 {
211 MultithreadedCallsTest();
212 }
213
214
215 typedef BaseAsyncExecutorTest<AsyncExecutor> AsyncExecutorTest;
216
217 INSTANTIATE_TEST_CASE_P(DifferentProducersNumber1,
218 AsyncExecutorTest,
219 MultithreadedCallsGenerator1,
220 humanReadbleParams);
221
222 INSTANTIATE_TEST_CASE_P(DifferentProducersNumber2,
223 AsyncExecutorTest,
224 MultithreadedCallsGenerator2,
225 humanReadbleParams);
226
227 INSTANTIATE_TEST_CASE_P(DifferentProducersNumber3,
228 AsyncExecutorTest,
229 MultithreadedCallsGenerator3,
230 humanReadbleParams);
231
232 TEST_P(AsyncExecutorTest, MultithreadedCalls)
233 {
234 MultithreadedCallsTest();
235 }
236 }
OLDNEW
« no previous file with comments | « src/Platform.cpp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld