1#include <gtest/gtest.h> 2#include <android/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 22using namespace std; 23 24namespace { 25 26// C++ wrapper class for sync timeline. 27class SyncTimeline { 28 int m_fd = -1; 29 bool m_fdInitialized = false; 30public: 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 70struct 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. 78class 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 } 91public: 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 vector<SyncPointInfo> fenceInfo; 176 struct sync_file_info *info = sync_file_info(getFd()); 177 if (!info) { 178 return fenceInfo; 179 } 180 const auto fences = sync_get_fence_info(info); 181 for (uint32_t i = 0; i < info->num_fences; i++) { 182 fenceInfo.push_back(SyncPointInfo{ 183 fences[i].driver_name, 184 fences[i].obj_name, 185 fences[i].timestamp_ns, 186 fences[i].status}); 187 } 188 sync_file_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 } 203private: 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 215static void CheckModernLegacyInfoMatch(const SyncFence& f) { 216 struct sync_file_info* modern = sync_file_info(f.getFd()); 217 struct sync_fence_info_data* legacy = sync_fence_info(f.getFd()); 218 219 ASSERT_TRUE(modern != NULL); 220 ASSERT_TRUE(legacy != NULL); 221 222 EXPECT_STREQ(modern->name, legacy->name); 223 EXPECT_EQ(modern->status, legacy->status); 224 225 uint32_t fenceIdx = 0; 226 struct sync_pt_info* pt = sync_pt_info(legacy, NULL); 227 const struct sync_fence_info* fences = sync_get_fence_info(modern); 228 while (fenceIdx < modern->num_fences && pt != NULL) { 229 EXPECT_STREQ(fences[fenceIdx].obj_name, pt->obj_name); 230 EXPECT_STREQ(fences[fenceIdx].driver_name, pt->driver_name); 231 EXPECT_EQ(fences[fenceIdx].status, pt->status); 232 EXPECT_EQ(fences[fenceIdx].timestamp_ns, pt->timestamp_ns); 233 234 fenceIdx++; 235 pt = sync_pt_info(legacy, pt); 236 } 237 EXPECT_EQ(fenceIdx, modern->num_fences); 238 EXPECT_EQ(NULL, pt); 239} 240 241int SyncFence::s_fenceCount = 0; 242 243TEST(AllocTest, Timeline) { 244 SyncTimeline timeline; 245 ASSERT_TRUE(timeline.isValid()); 246} 247 248TEST(AllocTest, Fence) { 249 SyncTimeline timeline; 250 ASSERT_TRUE(timeline.isValid()); 251 252 SyncFence fence(timeline, 1); 253 ASSERT_TRUE(fence.isValid()); 254 CheckModernLegacyInfoMatch(fence); 255} 256 257TEST(AllocTest, FenceNegative) { 258 int timeline = sw_sync_timeline_create(); 259 ASSERT_GT(timeline, 0); 260 261 // bad fd. 262 ASSERT_LT(sw_sync_fence_create(-1, "fence", 1), 0); 263 264 // No name - segfaults in user space. 265 // Maybe we should be friendlier here? 266 /* 267 ASSERT_LT(sw_sync_fence_create(timeline, nullptr, 1), 0); 268 */ 269 close(timeline); 270} 271 272TEST(FenceTest, OneTimelineWait) { 273 SyncTimeline timeline; 274 ASSERT_TRUE(timeline.isValid()); 275 276 SyncFence fence(timeline, 5); 277 ASSERT_TRUE(fence.isValid()); 278 279 // Wait on fence until timeout. 280 ASSERT_EQ(fence.wait(0), -1); 281 ASSERT_EQ(errno, ETIME); 282 283 // Advance timeline from 0 -> 1 284 ASSERT_EQ(timeline.inc(1), 0); 285 286 // Wait on fence until timeout. 287 ASSERT_EQ(fence.wait(0), -1); 288 ASSERT_EQ(errno, ETIME); 289 290 // Signal the fence. 291 ASSERT_EQ(timeline.inc(4), 0); 292 293 // Wait successfully. 294 ASSERT_EQ(fence.wait(0), 0); 295 296 // Go even futher, and confirm wait still succeeds. 297 ASSERT_EQ(timeline.inc(10), 0); 298 ASSERT_EQ(fence.wait(0), 0); 299} 300 301TEST(FenceTest, OneTimelinePoll) { 302 SyncTimeline timeline; 303 ASSERT_TRUE(timeline.isValid()); 304 305 SyncFence fence(timeline, 100); 306 ASSERT_TRUE(fence.isValid()); 307 308 fd_set set; 309 FD_ZERO(&set); 310 FD_SET(fence.getFd(), &set); 311 312 // Poll the fence, and wait till timeout. 313 timeval time = {0}; 314 ASSERT_EQ(select(fence.getFd() + 1, &set, nullptr, nullptr, &time), 0); 315 316 // Advance the timeline. 317 timeline.inc(100); 318 timeline.inc(100); 319 320 // Select should return that the fd is read for reading. 321 FD_ZERO(&set); 322 FD_SET(fence.getFd(), &set); 323 324 ASSERT_EQ(select(fence.getFd() + 1, &set, nullptr, nullptr, &time), 1); 325 ASSERT_TRUE(FD_ISSET(fence.getFd(), &set)); 326} 327 328TEST(FenceTest, OneTimelineMerge) { 329 SyncTimeline timeline; 330 ASSERT_TRUE(timeline.isValid()); 331 332 // create fence a,b,c and then merge them all into fence d. 333 SyncFence a(timeline, 1), b(timeline, 2), c(timeline, 3); 334 ASSERT_TRUE(a.isValid()); 335 ASSERT_TRUE(b.isValid()); 336 ASSERT_TRUE(c.isValid()); 337 338 SyncFence d({a,b,c}); 339 ASSERT_TRUE(d.isValid()); 340 341 // confirm all fences have one active point (even d). 342 ASSERT_EQ(a.getActiveCount(), 1); 343 ASSERT_EQ(b.getActiveCount(), 1); 344 ASSERT_EQ(c.getActiveCount(), 1); 345 ASSERT_EQ(d.getActiveCount(), 1); 346 347 // confirm that d is not signaled until the max of a,b,c 348 timeline.inc(1); 349 ASSERT_EQ(a.getSignaledCount(), 1); 350 ASSERT_EQ(d.getActiveCount(), 1); 351 CheckModernLegacyInfoMatch(a); 352 CheckModernLegacyInfoMatch(d); 353 354 timeline.inc(1); 355 ASSERT_EQ(b.getSignaledCount(), 1); 356 ASSERT_EQ(d.getActiveCount(), 1); 357 CheckModernLegacyInfoMatch(b); 358 CheckModernLegacyInfoMatch(d); 359 360 timeline.inc(1); 361 ASSERT_EQ(c.getSignaledCount(), 1); 362 ASSERT_EQ(d.getActiveCount(), 0); 363 ASSERT_EQ(d.getSignaledCount(), 1); 364 CheckModernLegacyInfoMatch(c); 365 CheckModernLegacyInfoMatch(d); 366} 367 368TEST(FenceTest, MergeSameFence) { 369 SyncTimeline timeline; 370 ASSERT_TRUE(timeline.isValid()); 371 372 SyncFence fence(timeline, 5); 373 ASSERT_TRUE(fence.isValid()); 374 375 SyncFence selfMergeFence(fence, fence); 376 ASSERT_TRUE(selfMergeFence.isValid()); 377 378 ASSERT_EQ(selfMergeFence.getSignaledCount(), 0); 379 CheckModernLegacyInfoMatch(selfMergeFence); 380 381 timeline.inc(5); 382 ASSERT_EQ(selfMergeFence.getSignaledCount(), 1); 383 CheckModernLegacyInfoMatch(selfMergeFence); 384} 385 386TEST(FenceTest, PollOnDestroyedTimeline) { 387 SyncTimeline timeline; 388 ASSERT_TRUE(timeline.isValid()); 389 390 SyncFence fenceSig(timeline, 100); 391 SyncFence fenceKill(timeline, 200); 392 393 // Spawn a thread to wait on a fence when the timeline is killed. 394 thread waitThread{ 395 [&]() { 396 ASSERT_EQ(timeline.inc(100), 0); 397 398 // Wait on the fd. 399 struct pollfd fds; 400 fds.fd = fenceKill.getFd(); 401 fds.events = POLLIN | POLLERR; 402 ASSERT_EQ(poll(&fds, 1, 0), 0); 403 } 404 }; 405 406 // Wait for the thread to spool up. 407 fenceSig.wait(); 408 409 // Kill the timeline. 410 timeline.destroy(); 411 412 // wait for the thread to clean up. 413 waitThread.join(); 414} 415 416TEST(FenceTest, MultiTimelineWait) { 417 SyncTimeline timelineA, timelineB, timelineC; 418 419 SyncFence fenceA(timelineA, 5); 420 SyncFence fenceB(timelineB, 5); 421 SyncFence fenceC(timelineC, 5); 422 423 // Make a larger fence using 3 other fences from different timelines. 424 SyncFence mergedFence({fenceA, fenceB, fenceC}); 425 ASSERT_TRUE(mergedFence.isValid()); 426 427 // Confirm fence isn't signaled 428 ASSERT_EQ(mergedFence.getActiveCount(), 3); 429 ASSERT_EQ(mergedFence.wait(0), -1); 430 ASSERT_EQ(errno, ETIME); 431 432 timelineA.inc(5); 433 ASSERT_EQ(mergedFence.getActiveCount(), 2); 434 ASSERT_EQ(mergedFence.getSignaledCount(), 1); 435 CheckModernLegacyInfoMatch(mergedFence); 436 437 timelineB.inc(5); 438 ASSERT_EQ(mergedFence.getActiveCount(), 1); 439 ASSERT_EQ(mergedFence.getSignaledCount(), 2); 440 CheckModernLegacyInfoMatch(mergedFence); 441 442 timelineC.inc(5); 443 ASSERT_EQ(mergedFence.getActiveCount(), 0); 444 ASSERT_EQ(mergedFence.getSignaledCount(), 3); 445 CheckModernLegacyInfoMatch(mergedFence); 446 447 // confirm you can successfully wait. 448 ASSERT_EQ(mergedFence.wait(100), 0); 449} 450 451TEST(FenceTest, GetInfoActive) { 452 SyncTimeline timeline; 453 ASSERT_TRUE(timeline.isValid()); 454 455 SyncFence fence(timeline, 1); 456 ASSERT_TRUE(fence.isValid()); 457 458 vector<SyncPointInfo> info = fence.getInfo(); 459 ASSERT_EQ(info.size(), 1); 460 461 ASSERT_FALSE(info[0].driverName.empty()); 462 ASSERT_FALSE(info[0].objectName.empty()); 463 ASSERT_EQ(info[0].timeStampNs, 0); 464 ASSERT_EQ(info[0].status, 0); 465} 466 467TEST(FenceTest, GetInfoSignaled) { 468 SyncTimeline timeline; 469 ASSERT_TRUE(timeline.isValid()); 470 471 SyncFence fence(timeline, 1); 472 ASSERT_TRUE(fence.isValid()); 473 474 ASSERT_EQ(timeline.inc(1), 0); 475 ASSERT_EQ(fence.wait(), 0); 476 477 vector<SyncPointInfo> info = fence.getInfo(); 478 ASSERT_EQ(info.size(), 1); 479 480 ASSERT_FALSE(info[0].driverName.empty()); 481 ASSERT_FALSE(info[0].objectName.empty()); 482 ASSERT_GT(info[0].timeStampNs, 0); 483 ASSERT_EQ(info[0].status, 1); 484} 485 486TEST(StressTest, TwoThreadsSharedTimeline) { 487 const int iterations = 1 << 16; 488 int counter = 0; 489 SyncTimeline timeline; 490 ASSERT_TRUE(timeline.isValid()); 491 492 // Use a single timeline to synchronize two threads 493 // hammmering on the same counter. 494 auto threadMain = [&](int threadId) { 495 for (int i = 0; i < iterations; i++) { 496 SyncFence fence(timeline, i * 2 + threadId); 497 ASSERT_TRUE(fence.isValid()); 498 499 // Wait on the prior thread to complete. 500 ASSERT_EQ(fence.wait(), 0); 501 502 // Confirm the previous thread's writes are visible and then inc. 503 ASSERT_EQ(counter, i * 2 + threadId); 504 counter++; 505 506 // Kick off the other thread. 507 ASSERT_EQ(timeline.inc(), 0); 508 } 509 }; 510 511 thread a{threadMain, 0}; 512 thread b{threadMain, 1}; 513 a.join(); 514 b.join(); 515 516 // make sure the threads did not trample on one another. 517 ASSERT_EQ(counter, iterations * 2); 518} 519 520class ConsumerStressTest : public ::testing::TestWithParam<int> {}; 521 522TEST_P(ConsumerStressTest, MultiProducerSingleConsumer) { 523 mutex lock; 524 int counter = 0; 525 int iterations = 1 << 12; 526 527 vector<SyncTimeline> producerTimelines(GetParam()); 528 vector<thread> threads; 529 SyncTimeline consumerTimeline; 530 531 // Producer threads run this lambda. 532 auto threadMain = [&](int threadId) { 533 for (int i = 0; i < iterations; i++) { 534 SyncFence fence(consumerTimeline, i); 535 ASSERT_TRUE(fence.isValid()); 536 537 // Wait for the consumer to finish. Use alternate 538 // means of waiting on the fence. 539 if ((iterations + threadId) % 8 != 0) { 540 ASSERT_EQ(fence.wait(), 0); 541 } 542 else { 543 while (fence.getSignaledCount() != 1) { 544 ASSERT_EQ(fence.getErrorCount(), 0); 545 } 546 } 547 548 // Every producer increments the counter, the consumer checks + erases it. 549 lock.lock(); 550 counter++; 551 lock.unlock(); 552 553 ASSERT_EQ(producerTimelines[threadId].inc(), 0); 554 } 555 }; 556 557 for (int i = 0; i < GetParam(); i++) { 558 threads.push_back(thread{threadMain, i}); 559 } 560 561 // Consumer thread runs this loop. 562 for (int i = 1; i <= iterations; i++) { 563 // Create a fence representing all producers final timelines. 564 vector<SyncFence> fences; 565 for (auto& timeline : producerTimelines) { 566 fences.push_back(SyncFence(timeline, i)); 567 } 568 SyncFence mergeFence(fences); 569 ASSERT_TRUE(mergeFence.isValid()); 570 571 // Make sure we see an increment from every producer thread. Vary 572 // the means by which we wait. 573 if (iterations % 8 != 0) { 574 ASSERT_EQ(mergeFence.wait(), 0); 575 } 576 else { 577 while (mergeFence.getSignaledCount() != mergeFence.getSize()) { 578 ASSERT_EQ(mergeFence.getErrorCount(), 0); 579 } 580 } 581 ASSERT_EQ(counter, GetParam()*i); 582 583 // Release the producer threads. 584 ASSERT_EQ(consumerTimeline.inc(), 0); 585 } 586 587 for_each(begin(threads), end(threads), [](thread& thread) { thread.join(); }); 588} 589INSTANTIATE_TEST_CASE_P( 590 ParameterizedStressTest, 591 ConsumerStressTest, 592 ::testing::Values(2,4,16)); 593 594class MergeStressTest : public ::testing::TestWithParam<tuple<int, int>> {}; 595 596template <typename K, typename V> using dict = unordered_map<K,V>; 597 598TEST_P(MergeStressTest, RandomMerge) { 599 int timelineCount = get<0>(GetParam()); 600 int mergeCount = get<1>(GetParam()); 601 602 vector<SyncTimeline> timelines(timelineCount); 603 604 default_random_engine generator; 605 uniform_int_distribution<int> timelineDist(0, timelines.size()-1); 606 uniform_int_distribution<int> syncPointDist(0, numeric_limits<int>::max()); 607 608 SyncFence fence(timelines[0], 0); 609 ASSERT_TRUE(fence.isValid()); 610 611 unordered_map<int, int> fenceMap; 612 fenceMap.insert(make_pair(0, 0)); 613 614 // Randomly create syncpoints out of a fixed set of timelines, and merge them together. 615 for (int i = 0; i < mergeCount; i++) { 616 617 // Generate syncpoint. 618 int timelineOffset = timelineDist(generator); 619 const SyncTimeline& timeline = timelines[timelineOffset]; 620 int syncPoint = syncPointDist(generator); 621 622 // Keep track of the latest syncpoint in each timeline. 623 auto itr = fenceMap.find(timelineOffset); 624 if (itr == end(fenceMap)) { 625 fenceMap.insert(make_pair(timelineOffset, syncPoint)); 626 } 627 else { 628 int oldSyncPoint = itr->second; 629 fenceMap.erase(itr); 630 fenceMap.insert(make_pair(timelineOffset, max(syncPoint, oldSyncPoint))); 631 } 632 633 // Merge. 634 fence = SyncFence(fence, SyncFence(timeline, syncPoint)); 635 ASSERT_TRUE(fence.isValid()); 636 CheckModernLegacyInfoMatch(fence); 637 } 638 639 // Confirm our map matches the fence. 640 ASSERT_EQ(fence.getSize(), fenceMap.size()); 641 642 // Trigger the merged fence. 643 for (auto& item: fenceMap) { 644 ASSERT_EQ(fence.wait(0), -1); 645 ASSERT_EQ(errno, ETIME); 646 647 // Increment the timeline to the last syncpoint. 648 timelines[item.first].inc(item.second); 649 } 650 651 // Check that the fence is triggered. 652 ASSERT_EQ(fence.wait(0), 0); 653} 654 655INSTANTIATE_TEST_CASE_P( 656 ParameterizedMergeStressTest, 657 MergeStressTest, 658 ::testing::Combine(::testing::Values(16,32), ::testing::Values(32, 1024, 1024*32))); 659 660} 661 662