diff options
author | Riley Andrews | 2014-06-05 21:34:40 -0500 |
---|---|---|
committer | JP Abgrall | 2014-10-10 21:10:23 -0500 |
commit | d0d7f6ac082184ee88bb34224f41537c140d98e5 (patch) | |
tree | 4df3219a7c5f63919da483a03f799c04daf4479e /libsync/tests | |
parent | 6b66025f6f3e48d182e8d6aeb989d0f39eb551e3 (diff) | |
download | platform-system-core-d0d7f6ac082184ee88bb34224f41537c140d98e5.tar.gz platform-system-core-d0d7f6ac082184ee88bb34224f41537c140d98e5.tar.xz platform-system-core-d0d7f6ac082184ee88bb34224f41537c140d98e5.zip |
Add gtest conformance tests for libsync.
Not complete yet, but substantially more comprehensive than the
interactive test that was there before.
(cherry-picked from internal master, same change-id).
Change-Id: I9019b0a8babbc91f78aa850e0e288bbf05f93500
Diffstat (limited to 'libsync/tests')
-rw-r--r-- | libsync/tests/Android.mk | 31 | ||||
-rw-r--r-- | libsync/tests/sync_test.cpp | 615 |
2 files changed, 646 insertions, 0 deletions
diff --git a/libsync/tests/Android.mk b/libsync/tests/Android.mk new file mode 100644 index 000000000..ad20e50a1 --- /dev/null +++ b/libsync/tests/Android.mk | |||
@@ -0,0 +1,31 @@ | |||
1 | # | ||
2 | # Copyright 2014 The Android Open Source Project | ||
3 | # | ||
4 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | # you may not use this file except in compliance with the License. | ||
6 | # You may obtain a copy of the License at | ||
7 | # | ||
8 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | # | ||
10 | # Unless required by applicable law or agreed to in writing, software | ||
11 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | # See the License for the specific language governing permissions and | ||
14 | # limitations under the License. | ||
15 | # | ||
16 | |||
17 | LOCAL_PATH:= $(call my-dir) | ||
18 | |||
19 | include $(CLEAR_VARS) | ||
20 | include external/libcxx/libcxx.mk | ||
21 | LOCAL_CLANG := true | ||
22 | LOCAL_MODULE := sync-unit-tests | ||
23 | LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk | ||
24 | LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers -Wno-sign-compare | ||
25 | LOCAL_SHARED_LIBRARIES += libsync | ||
26 | LOCAL_STATIC_LIBRARIES += libgtest_main | ||
27 | LOCAL_C_INCLUDES += $(LOCAL_PATH)/../include | ||
28 | LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. | ||
29 | LOCAL_SRC_FILES := \ | ||
30 | sync_test.cpp | ||
31 | include $(BUILD_NATIVE_TEST) | ||
diff --git a/libsync/tests/sync_test.cpp b/libsync/tests/sync_test.cpp new file mode 100644 index 000000000..55cd68773 --- /dev/null +++ b/libsync/tests/sync_test.cpp | |||
@@ -0,0 +1,615 @@ | |||
1 | #include <gtest/gtest.h> | ||
2 | #include <sync/sync.h> | ||
3 | #include <sw_sync.h> | ||
4 | #include <fcntl.h> | ||
5 | #include <vector> | ||
6 | #include <string> | ||
7 | #include <cassert> | ||
8 | #include <iostream> | ||
9 | #include <unistd.h> | ||
10 | #include <thread> | ||
11 | #include <poll.h> | ||
12 | #include <mutex> | ||
13 | #include <algorithm> | ||
14 | #include <tuple> | ||
15 | #include <random> | ||
16 | #include <unordered_map> | ||
17 | |||
18 | // TODO: better stress tests? | ||
19 | // Handle more than 64 fd's simultaneously, i.e. fix sync_fence_info's 4k limit. | ||
20 | // Handle wraparound in timelines like nvidia. | ||
21 | |||
22 | using namespace std; | ||
23 | |||
24 | namespace { | ||
25 | |||
26 | // C++ wrapper class for sync timeline. | ||
27 | class SyncTimeline { | ||
28 | int m_fd = -1; | ||
29 | bool m_fdInitialized = false; | ||
30 | public: | ||
31 | SyncTimeline(const SyncTimeline &) = delete; | ||
32 | SyncTimeline& operator=(SyncTimeline&) = delete; | ||
33 | SyncTimeline() noexcept { | ||
34 | int fd = sw_sync_timeline_create(); | ||
35 | if (fd == -1) | ||
36 | return; | ||
37 | m_fdInitialized = true; | ||
38 | m_fd = fd; | ||
39 | } | ||
40 | void destroy() { | ||
41 | if (m_fdInitialized) { | ||
42 | close(m_fd); | ||
43 | m_fd = -1; | ||
44 | m_fdInitialized = false; | ||
45 | } | ||
46 | } | ||
47 | ~SyncTimeline() { | ||
48 | destroy(); | ||
49 | } | ||
50 | bool isValid() const { | ||
51 | if (m_fdInitialized) { | ||
52 | int status = fcntl(m_fd, F_GETFD, 0); | ||
53 | if (status == 0) | ||
54 | return true; | ||
55 | else | ||
56 | return false; | ||
57 | } | ||
58 | else { | ||
59 | return false; | ||
60 | } | ||
61 | } | ||
62 | int getFd() const { | ||
63 | return m_fd; | ||
64 | } | ||
65 | int inc(int val = 1) { | ||
66 | return sw_sync_timeline_inc(m_fd, val); | ||
67 | } | ||
68 | }; | ||
69 | |||
70 | struct SyncPointInfo { | ||
71 | std::string driverName; | ||
72 | std::string objectName; | ||
73 | uint64_t timeStampNs; | ||
74 | int status; // 1 sig, 0 active, neg is err | ||
75 | }; | ||
76 | |||
77 | // Wrapper class for sync fence. | ||
78 | class SyncFence { | ||
79 | int m_fd = -1; | ||
80 | bool m_fdInitialized = false; | ||
81 | static int s_fenceCount; | ||
82 | |||
83 | void setFd(int fd) { | ||
84 | m_fd = fd; | ||
85 | m_fdInitialized = true; | ||
86 | } | ||
87 | void clearFd() { | ||
88 | m_fd = -1; | ||
89 | m_fdInitialized = false; | ||
90 | } | ||
91 | public: | ||
92 | bool isValid() const { | ||
93 | if (m_fdInitialized) { | ||
94 | int status = fcntl(m_fd, F_GETFD, 0); | ||
95 | if (status == 0) | ||
96 | return true; | ||
97 | else | ||
98 | return false; | ||
99 | } | ||
100 | else { | ||
101 | return false; | ||
102 | } | ||
103 | } | ||
104 | SyncFence& operator=(SyncFence &&rhs) noexcept { | ||
105 | destroy(); | ||
106 | if (rhs.isValid()) { | ||
107 | setFd(rhs.getFd()); | ||
108 | rhs.clearFd(); | ||
109 | } | ||
110 | return *this; | ||
111 | } | ||
112 | SyncFence(SyncFence &&fence) noexcept { | ||
113 | if (fence.isValid()) { | ||
114 | setFd(fence.getFd()); | ||
115 | fence.clearFd(); | ||
116 | } | ||
117 | } | ||
118 | SyncFence(const SyncFence &fence) noexcept { | ||
119 | // This is ok, as sync fences are immutable after construction, so a dup | ||
120 | // is basically the same thing as a copy. | ||
121 | if (fence.isValid()) { | ||
122 | int fd = dup(fence.getFd()); | ||
123 | if (fd == -1) | ||
124 | return; | ||
125 | setFd(fd); | ||
126 | } | ||
127 | } | ||
128 | SyncFence(const SyncTimeline &timeline, | ||
129 | int value, | ||
130 | const char *name = nullptr) noexcept { | ||
131 | std::string autoName = "allocFence"; | ||
132 | autoName += s_fenceCount; | ||
133 | s_fenceCount++; | ||
134 | int fd = sw_sync_fence_create(timeline.getFd(), name ? name : autoName.c_str(), value); | ||
135 | if (fd == -1) | ||
136 | return; | ||
137 | setFd(fd); | ||
138 | } | ||
139 | SyncFence(const SyncFence &a, const SyncFence &b, const char *name = nullptr) noexcept { | ||
140 | std::string autoName = "mergeFence"; | ||
141 | autoName += s_fenceCount; | ||
142 | s_fenceCount++; | ||
143 | int fd = sync_merge(name ? name : autoName.c_str(), a.getFd(), b.getFd()); | ||
144 | if (fd == -1) | ||
145 | return; | ||
146 | setFd(fd); | ||
147 | } | ||
148 | SyncFence(const vector<SyncFence> &sources) noexcept { | ||
149 | assert(sources.size()); | ||
150 | SyncFence temp(*begin(sources)); | ||
151 | for (auto itr = ++begin(sources); itr != end(sources); ++itr) { | ||
152 | temp = SyncFence(*itr, temp); | ||
153 | } | ||
154 | if (temp.isValid()) { | ||
155 | setFd(temp.getFd()); | ||
156 | temp.clearFd(); | ||
157 | } | ||
158 | } | ||
159 | void destroy() { | ||
160 | if (isValid()) { | ||
161 | close(m_fd); | ||
162 | clearFd(); | ||
163 | } | ||
164 | } | ||
165 | ~SyncFence() { | ||
166 | destroy(); | ||
167 | } | ||
168 | int getFd() const { | ||
169 | return m_fd; | ||
170 | } | ||
171 | int wait(int timeout = -1) { | ||
172 | return sync_wait(m_fd, timeout); | ||
173 | } | ||
174 | vector<SyncPointInfo> getInfo() const { | ||
175 | struct sync_pt_info *pointInfo = nullptr; | ||
176 | vector<SyncPointInfo> fenceInfo; | ||
177 | sync_fence_info_data *info = sync_fence_info(getFd()); | ||
178 | if (!info) { | ||
179 | return fenceInfo; | ||
180 | } | ||
181 | while ((pointInfo = sync_pt_info(info, pointInfo))) { | ||
182 | fenceInfo.push_back(SyncPointInfo{ | ||
183 | pointInfo->driver_name, | ||
184 | pointInfo->obj_name, | ||
185 | pointInfo->timestamp_ns, | ||
186 | pointInfo->status}); | ||
187 | } | ||
188 | sync_fence_info_free(info); | ||
189 | return fenceInfo; | ||
190 | } | ||
191 | int getSize() const { | ||
192 | return getInfo().size(); | ||
193 | } | ||
194 | int getSignaledCount() const { | ||
195 | return countWithStatus(1); | ||
196 | } | ||
197 | int getActiveCount() const { | ||
198 | return countWithStatus(0); | ||
199 | } | ||
200 | int getErrorCount() const { | ||
201 | return countWithStatus(-1); | ||
202 | } | ||
203 | private: | ||
204 | int countWithStatus(int status) const { | ||
205 | int count = 0; | ||
206 | for (auto &info : getInfo()) { | ||
207 | if (info.status == status) { | ||
208 | count++; | ||
209 | } | ||
210 | } | ||
211 | return count; | ||
212 | } | ||
213 | }; | ||
214 | |||
215 | int SyncFence::s_fenceCount = 0; | ||
216 | |||
217 | TEST(AllocTest, Timeline) { | ||
218 | SyncTimeline timeline; | ||
219 | ASSERT_TRUE(timeline.isValid()); | ||
220 | } | ||
221 | |||
222 | TEST(AllocTest, Fence) { | ||
223 | SyncTimeline timeline; | ||
224 | ASSERT_TRUE(timeline.isValid()); | ||
225 | |||
226 | SyncFence fence(timeline, 1); | ||
227 | ASSERT_TRUE(fence.isValid()); | ||
228 | } | ||
229 | |||
230 | TEST(AllocTest, FenceNegative) { | ||
231 | int timeline = sw_sync_timeline_create(); | ||
232 | ASSERT_GT(timeline, 0); | ||
233 | |||
234 | // bad fd. | ||
235 | ASSERT_LT(sw_sync_fence_create(-1, "fence", 1), 0); | ||
236 | |||
237 | // No name - segfaults in user space. | ||
238 | // Maybe we should be friendlier here? | ||
239 | /* | ||
240 | ASSERT_LT(sw_sync_fence_create(timeline, nullptr, 1), 0); | ||
241 | */ | ||
242 | close(timeline); | ||
243 | } | ||
244 | |||
245 | TEST(FenceTest, OneTimelineWait) { | ||
246 | SyncTimeline timeline; | ||
247 | ASSERT_TRUE(timeline.isValid()); | ||
248 | |||
249 | SyncFence fence(timeline, 5); | ||
250 | ASSERT_TRUE(fence.isValid()); | ||
251 | |||
252 | // Wait on fence until timeout. | ||
253 | ASSERT_EQ(fence.wait(0), -1); | ||
254 | ASSERT_EQ(errno, ETIME); | ||
255 | |||
256 | // Advance timeline from 0 -> 1 | ||
257 | ASSERT_EQ(timeline.inc(1), 0); | ||
258 | |||
259 | // Wait on fence until timeout. | ||
260 | ASSERT_EQ(fence.wait(0), -1); | ||
261 | ASSERT_EQ(errno, ETIME); | ||
262 | |||
263 | // Signal the fence. | ||
264 | ASSERT_EQ(timeline.inc(4), 0); | ||
265 | |||
266 | // Wait successfully. | ||
267 | ASSERT_EQ(fence.wait(0), 0); | ||
268 | |||
269 | // Go even futher, and confirm wait still succeeds. | ||
270 | ASSERT_EQ(timeline.inc(10), 0); | ||
271 | ASSERT_EQ(fence.wait(0), 0); | ||
272 | } | ||
273 | |||
274 | TEST(FenceTest, OneTimelinePoll) { | ||
275 | SyncTimeline timeline; | ||
276 | ASSERT_TRUE(timeline.isValid()); | ||
277 | |||
278 | SyncFence fence(timeline, 100); | ||
279 | ASSERT_TRUE(fence.isValid()); | ||
280 | |||
281 | fd_set set; | ||
282 | FD_ZERO(&set); | ||
283 | FD_SET(fence.getFd(), &set); | ||
284 | |||
285 | // Poll the fence, and wait till timeout. | ||
286 | timeval time = {0}; | ||
287 | ASSERT_EQ(select(fence.getFd() + 1, &set, nullptr, nullptr, &time), 0); | ||
288 | |||
289 | // Advance the timeline. | ||
290 | timeline.inc(100); | ||
291 | timeline.inc(100); | ||
292 | |||
293 | // Select should return that the fd is read for reading. | ||
294 | FD_ZERO(&set); | ||
295 | FD_SET(fence.getFd(), &set); | ||
296 | |||
297 | ASSERT_EQ(select(fence.getFd() + 1, &set, nullptr, nullptr, &time), 1); | ||
298 | ASSERT_TRUE(FD_ISSET(fence.getFd(), &set)); | ||
299 | } | ||
300 | |||
301 | TEST(FenceTest, OneTimelineMerge) { | ||
302 | SyncTimeline timeline; | ||
303 | ASSERT_TRUE(timeline.isValid()); | ||
304 | |||
305 | // create fence a,b,c and then merge them all into fence d. | ||
306 | SyncFence a(timeline, 1), b(timeline, 2), c(timeline, 3); | ||
307 | ASSERT_TRUE(a.isValid()); | ||
308 | ASSERT_TRUE(b.isValid()); | ||
309 | ASSERT_TRUE(c.isValid()); | ||
310 | |||
311 | SyncFence d({a,b,c}); | ||
312 | ASSERT_TRUE(d.isValid()); | ||
313 | |||
314 | // confirm all fences have one active point (even d). | ||
315 | ASSERT_EQ(a.getActiveCount(), 1); | ||
316 | ASSERT_EQ(b.getActiveCount(), 1); | ||
317 | ASSERT_EQ(c.getActiveCount(), 1); | ||
318 | ASSERT_EQ(d.getActiveCount(), 1); | ||
319 | |||
320 | // confirm that d is not signaled until the max of a,b,c | ||
321 | timeline.inc(1); | ||
322 | ASSERT_EQ(a.getSignaledCount(), 1); | ||
323 | ASSERT_EQ(d.getActiveCount(), 1); | ||
324 | |||
325 | timeline.inc(1); | ||
326 | ASSERT_EQ(b.getSignaledCount(), 1); | ||
327 | ASSERT_EQ(d.getActiveCount(), 1); | ||
328 | |||
329 | timeline.inc(1); | ||
330 | ASSERT_EQ(c.getSignaledCount(), 1); | ||
331 | ASSERT_EQ(d.getActiveCount(), 0); | ||
332 | ASSERT_EQ(d.getSignaledCount(), 1); | ||
333 | } | ||
334 | |||
335 | TEST(FenceTest, MergeSameFence) { | ||
336 | SyncTimeline timeline; | ||
337 | ASSERT_TRUE(timeline.isValid()); | ||
338 | |||
339 | SyncFence fence(timeline, 5); | ||
340 | ASSERT_TRUE(fence.isValid()); | ||
341 | |||
342 | SyncFence selfMergeFence(fence, fence); | ||
343 | ASSERT_TRUE(selfMergeFence.isValid()); | ||
344 | |||
345 | ASSERT_EQ(selfMergeFence.getSignaledCount(), 0); | ||
346 | |||
347 | timeline.inc(5); | ||
348 | ASSERT_EQ(selfMergeFence.getSignaledCount(), 1); | ||
349 | } | ||
350 | |||
351 | TEST(FenceTest, WaitOnDestroyedTimeline) { | ||
352 | SyncTimeline timeline; | ||
353 | ASSERT_TRUE(timeline.isValid()); | ||
354 | |||
355 | SyncFence fenceSig(timeline, 100); | ||
356 | SyncFence fenceKill(timeline, 200); | ||
357 | |||
358 | // Spawn a thread to wait on a fence when the timeline is killed. | ||
359 | thread waitThread{ | ||
360 | [&]() { | ||
361 | ASSERT_EQ(timeline.inc(100), 0); | ||
362 | |||
363 | ASSERT_EQ(fenceKill.wait(-1), -1); | ||
364 | ASSERT_EQ(errno, ENOENT); | ||
365 | } | ||
366 | }; | ||
367 | |||
368 | // Wait for the thread to spool up. | ||
369 | fenceSig.wait(); | ||
370 | |||
371 | // Kill the timeline. | ||
372 | timeline.destroy(); | ||
373 | |||
374 | // wait for the thread to clean up. | ||
375 | waitThread.join(); | ||
376 | } | ||
377 | |||
378 | TEST(FenceTest, PollOnDestroyedTimeline) { | ||
379 | SyncTimeline timeline; | ||
380 | ASSERT_TRUE(timeline.isValid()); | ||
381 | |||
382 | SyncFence fenceSig(timeline, 100); | ||
383 | SyncFence fenceKill(timeline, 200); | ||
384 | |||
385 | // Spawn a thread to wait on a fence when the timeline is killed. | ||
386 | thread waitThread{ | ||
387 | [&]() { | ||
388 | ASSERT_EQ(timeline.inc(100), 0); | ||
389 | |||
390 | // Wait on the fd. | ||
391 | struct pollfd fds; | ||
392 | fds.fd = fenceKill.getFd(); | ||
393 | fds.events = POLLIN | POLLERR; | ||
394 | ASSERT_EQ(poll(&fds, 1, -1), 1); | ||
395 | ASSERT_TRUE(fds.revents & POLLERR); | ||
396 | } | ||
397 | }; | ||
398 | |||
399 | // Wait for the thread to spool up. | ||
400 | fenceSig.wait(); | ||
401 | |||
402 | // Kill the timeline. | ||
403 | timeline.destroy(); | ||
404 | |||
405 | // wait for the thread to clean up. | ||
406 | waitThread.join(); | ||
407 | } | ||
408 | |||
409 | TEST(FenceTest, MultiTimelineWait) { | ||
410 | SyncTimeline timelineA, timelineB, timelineC; | ||
411 | |||
412 | SyncFence fenceA(timelineA, 5); | ||
413 | SyncFence fenceB(timelineB, 5); | ||
414 | SyncFence fenceC(timelineC, 5); | ||
415 | |||
416 | // Make a larger fence using 3 other fences from different timelines. | ||
417 | SyncFence mergedFence({fenceA, fenceB, fenceC}); | ||
418 | ASSERT_TRUE(mergedFence.isValid()); | ||
419 | |||
420 | // Confirm fence isn't signaled | ||
421 | ASSERT_EQ(mergedFence.getActiveCount(), 3); | ||
422 | ASSERT_EQ(mergedFence.wait(0), -1); | ||
423 | ASSERT_EQ(errno, ETIME); | ||
424 | |||
425 | timelineA.inc(5); | ||
426 | ASSERT_EQ(mergedFence.getActiveCount(), 2); | ||
427 | ASSERT_EQ(mergedFence.getSignaledCount(), 1); | ||
428 | |||
429 | timelineB.inc(5); | ||
430 | ASSERT_EQ(mergedFence.getActiveCount(), 1); | ||
431 | ASSERT_EQ(mergedFence.getSignaledCount(), 2); | ||
432 | |||
433 | timelineC.inc(5); | ||
434 | ASSERT_EQ(mergedFence.getActiveCount(), 0); | ||
435 | ASSERT_EQ(mergedFence.getSignaledCount(), 3); | ||
436 | |||
437 | // confirm you can successfully wait. | ||
438 | ASSERT_EQ(mergedFence.wait(100), 0); | ||
439 | } | ||
440 | |||
441 | TEST(StressTest, TwoThreadsSharedTimeline) { | ||
442 | const int iterations = 1 << 16; | ||
443 | int counter = 0; | ||
444 | SyncTimeline timeline; | ||
445 | ASSERT_TRUE(timeline.isValid()); | ||
446 | |||
447 | // Use a single timeline to synchronize two threads | ||
448 | // hammmering on the same counter. | ||
449 | auto threadMain = [&](int threadId) { | ||
450 | for (int i = 0; i < iterations; i++) { | ||
451 | SyncFence fence(timeline, i * 2 + threadId); | ||
452 | ASSERT_TRUE(fence.isValid()); | ||
453 | |||
454 | // Wait on the prior thread to complete. | ||
455 | ASSERT_EQ(fence.wait(), 0); | ||
456 | |||
457 | // Confirm the previous thread's writes are visible and then inc. | ||
458 | ASSERT_EQ(counter, i * 2 + threadId); | ||
459 | counter++; | ||
460 | |||
461 | // Kick off the other thread. | ||
462 | ASSERT_EQ(timeline.inc(), 0); | ||
463 | } | ||
464 | }; | ||
465 | |||
466 | thread a{threadMain, 0}; | ||
467 | thread b{threadMain, 1}; | ||
468 | a.join(); | ||
469 | b.join(); | ||
470 | |||
471 | // make sure the threads did not trample on one another. | ||
472 | ASSERT_EQ(counter, iterations * 2); | ||
473 | } | ||
474 | |||
475 | class ConsumerStressTest : public ::testing::TestWithParam<int> {}; | ||
476 | |||
477 | TEST_P(ConsumerStressTest, MultiProducerSingleConsumer) { | ||
478 | mutex lock; | ||
479 | int counter = 0; | ||
480 | int iterations = 1 << 12; | ||
481 | |||
482 | vector<SyncTimeline> producerTimelines(GetParam()); | ||
483 | vector<thread> threads; | ||
484 | SyncTimeline consumerTimeline; | ||
485 | |||
486 | // Producer threads run this lambda. | ||
487 | auto threadMain = [&](int threadId) { | ||
488 | for (int i = 0; i < iterations; i++) { | ||
489 | SyncFence fence(consumerTimeline, i); | ||
490 | ASSERT_TRUE(fence.isValid()); | ||
491 | |||
492 | // Wait for the consumer to finish. Use alternate | ||
493 | // means of waiting on the fence. | ||
494 | if ((iterations + threadId) % 8 != 0) { | ||
495 | ASSERT_EQ(fence.wait(), 0); | ||
496 | } | ||
497 | else { | ||
498 | while (fence.getSignaledCount() != 1) { | ||
499 | ASSERT_EQ(fence.getErrorCount(), 0); | ||
500 | } | ||
501 | } | ||
502 | |||
503 | // Every producer increments the counter, the consumer checks + erases it. | ||
504 | lock.lock(); | ||
505 | counter++; | ||
506 | lock.unlock(); | ||
507 | |||
508 | ASSERT_EQ(producerTimelines[threadId].inc(), 0); | ||
509 | } | ||
510 | }; | ||
511 | |||
512 | for (int i = 0; i < GetParam(); i++) { | ||
513 | threads.push_back(thread{threadMain, i}); | ||
514 | } | ||
515 | |||
516 | // Consumer thread runs this loop. | ||
517 | for (int i = 1; i <= iterations; i++) { | ||
518 | // Create a fence representing all producers final timelines. | ||
519 | vector<SyncFence> fences; | ||
520 | for (auto& timeline : producerTimelines) { | ||
521 | fences.push_back(SyncFence(timeline, i)); | ||
522 | } | ||
523 | SyncFence mergeFence(fences); | ||
524 | ASSERT_TRUE(mergeFence.isValid()); | ||
525 | |||
526 | // Make sure we see an increment from every producer thread. Vary | ||
527 | // the means by which we wait. | ||
528 | if (iterations % 8 != 0) { | ||
529 | ASSERT_EQ(mergeFence.wait(), 0); | ||
530 | } | ||
531 | else { | ||
532 | while (mergeFence.getSignaledCount() != mergeFence.getSize()) { | ||
533 | ASSERT_EQ(mergeFence.getErrorCount(), 0); | ||
534 | } | ||
535 | } | ||
536 | ASSERT_EQ(counter, GetParam()*i); | ||
537 | |||
538 | // Release the producer threads. | ||
539 | ASSERT_EQ(consumerTimeline.inc(), 0); | ||
540 | } | ||
541 | |||
542 | for_each(begin(threads), end(threads), [](thread& thread) { thread.join(); }); | ||
543 | } | ||
544 | INSTANTIATE_TEST_CASE_P( | ||
545 | ParameterizedStressTest, | ||
546 | ConsumerStressTest, | ||
547 | ::testing::Values(2,4,16)); | ||
548 | |||
549 | class MergeStressTest : public ::testing::TestWithParam<tuple<int, int>> {}; | ||
550 | |||
551 | template <typename K, typename V> using dict = unordered_map<K,V>; | ||
552 | |||
553 | TEST_P(MergeStressTest, RandomMerge) { | ||
554 | int timelineCount = get<0>(GetParam()); | ||
555 | int mergeCount = get<1>(GetParam()); | ||
556 | |||
557 | vector<SyncTimeline> timelines(timelineCount); | ||
558 | |||
559 | default_random_engine generator; | ||
560 | uniform_int_distribution<int> timelineDist(0, timelines.size()-1); | ||
561 | uniform_int_distribution<int> syncPointDist(0, numeric_limits<int>::max()); | ||
562 | |||
563 | SyncFence fence(timelines[0], 0); | ||
564 | ASSERT_TRUE(fence.isValid()); | ||
565 | |||
566 | unordered_map<int, int> fenceMap; | ||
567 | fenceMap.insert(make_tuple(0, 0)); | ||
568 | |||
569 | // Randomly create syncpoints out of a fixed set of timelines, and merge them together. | ||
570 | for (int i = 0; i < mergeCount; i++) { | ||
571 | |||
572 | // Generate syncpoint. | ||
573 | int timelineOffset = timelineDist(generator); | ||
574 | const SyncTimeline& timeline = timelines[timelineOffset]; | ||
575 | int syncPoint = syncPointDist(generator); | ||
576 | |||
577 | // Keep track of the latest syncpoint in each timeline. | ||
578 | auto itr = fenceMap.find(timelineOffset); | ||
579 | if (itr == end(fenceMap)) { | ||
580 | fenceMap.insert(tie(timelineOffset, syncPoint)); | ||
581 | } | ||
582 | else { | ||
583 | int oldSyncPoint = itr->second; | ||
584 | fenceMap.erase(itr); | ||
585 | fenceMap.insert(tie(timelineOffset, max(syncPoint, oldSyncPoint))); | ||
586 | } | ||
587 | |||
588 | // Merge. | ||
589 | fence = SyncFence(fence, SyncFence(timeline, syncPoint)); | ||
590 | ASSERT_TRUE(fence.isValid()); | ||
591 | } | ||
592 | |||
593 | // Confirm our map matches the fence. | ||
594 | ASSERT_EQ(fence.getSize(), fenceMap.size()); | ||
595 | |||
596 | // Trigger the merged fence. | ||
597 | for (auto& item: fenceMap) { | ||
598 | ASSERT_EQ(fence.wait(0), -1); | ||
599 | ASSERT_EQ(errno, ETIME); | ||
600 | |||
601 | // Increment the timeline to the last syncpoint. | ||
602 | timelines[item.first].inc(item.second); | ||
603 | } | ||
604 | |||
605 | // Check that the fence is triggered. | ||
606 | ASSERT_EQ(fence.wait(0), 0); | ||
607 | } | ||
608 | |||
609 | INSTANTIATE_TEST_CASE_P( | ||
610 | ParameterizedMergeStressTest, | ||
611 | MergeStressTest, | ||
612 | ::testing::Combine(::testing::Values(16,32), ::testing::Values(32, 1024, 1024*32))); | ||
613 | |||
614 | } | ||
615 | |||