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(StressTest, TwoThreadsSharedTimeline) { 452 const int iterations = 1 << 16; 453 int counter = 0; 454 SyncTimeline timeline; 455 ASSERT_TRUE(timeline.isValid()); 456 457 // Use a single timeline to synchronize two threads 458 // hammmering on the same counter. 459 auto threadMain = [&](int threadId) { 460 for (int i = 0; i < iterations; i++) { 461 SyncFence fence(timeline, i * 2 + threadId); 462 ASSERT_TRUE(fence.isValid()); 463 464 // Wait on the prior thread to complete. 465 ASSERT_EQ(fence.wait(), 0); 466 467 // Confirm the previous thread's writes are visible and then inc. 468 ASSERT_EQ(counter, i * 2 + threadId); 469 counter++; 470 471 // Kick off the other thread. 472 ASSERT_EQ(timeline.inc(), 0); 473 } 474 }; 475 476 thread a{threadMain, 0}; 477 thread b{threadMain, 1}; 478 a.join(); 479 b.join(); 480 481 // make sure the threads did not trample on one another. 482 ASSERT_EQ(counter, iterations * 2); 483} 484 485class ConsumerStressTest : public ::testing::TestWithParam<int> {}; 486 487TEST_P(ConsumerStressTest, MultiProducerSingleConsumer) { 488 mutex lock; 489 int counter = 0; 490 int iterations = 1 << 12; 491 492 vector<SyncTimeline> producerTimelines(GetParam()); 493 vector<thread> threads; 494 SyncTimeline consumerTimeline; 495 496 // Producer threads run this lambda. 497 auto threadMain = [&](int threadId) { 498 for (int i = 0; i < iterations; i++) { 499 SyncFence fence(consumerTimeline, i); 500 ASSERT_TRUE(fence.isValid()); 501 502 // Wait for the consumer to finish. Use alternate 503 // means of waiting on the fence. 504 if ((iterations + threadId) % 8 != 0) { 505 ASSERT_EQ(fence.wait(), 0); 506 } 507 else { 508 while (fence.getSignaledCount() != 1) { 509 ASSERT_EQ(fence.getErrorCount(), 0); 510 } 511 } 512 513 // Every producer increments the counter, the consumer checks + erases it. 514 lock.lock(); 515 counter++; 516 lock.unlock(); 517 518 ASSERT_EQ(producerTimelines[threadId].inc(), 0); 519 } 520 }; 521 522 for (int i = 0; i < GetParam(); i++) { 523 threads.push_back(thread{threadMain, i}); 524 } 525 526 // Consumer thread runs this loop. 527 for (int i = 1; i <= iterations; i++) { 528 // Create a fence representing all producers final timelines. 529 vector<SyncFence> fences; 530 for (auto& timeline : producerTimelines) { 531 fences.push_back(SyncFence(timeline, i)); 532 } 533 SyncFence mergeFence(fences); 534 ASSERT_TRUE(mergeFence.isValid()); 535 536 // Make sure we see an increment from every producer thread. Vary 537 // the means by which we wait. 538 if (iterations % 8 != 0) { 539 ASSERT_EQ(mergeFence.wait(), 0); 540 } 541 else { 542 while (mergeFence.getSignaledCount() != mergeFence.getSize()) { 543 ASSERT_EQ(mergeFence.getErrorCount(), 0); 544 } 545 } 546 ASSERT_EQ(counter, GetParam()*i); 547 548 // Release the producer threads. 549 ASSERT_EQ(consumerTimeline.inc(), 0); 550 } 551 552 for_each(begin(threads), end(threads), [](thread& thread) { thread.join(); }); 553} 554INSTANTIATE_TEST_CASE_P( 555 ParameterizedStressTest, 556 ConsumerStressTest, 557 ::testing::Values(2,4,16)); 558 559class MergeStressTest : public ::testing::TestWithParam<tuple<int, int>> {}; 560 561template <typename K, typename V> using dict = unordered_map<K,V>; 562 563TEST_P(MergeStressTest, RandomMerge) { 564 int timelineCount = get<0>(GetParam()); 565 int mergeCount = get<1>(GetParam()); 566 567 vector<SyncTimeline> timelines(timelineCount); 568 569 default_random_engine generator; 570 uniform_int_distribution<int> timelineDist(0, timelines.size()-1); 571 uniform_int_distribution<int> syncPointDist(0, numeric_limits<int>::max()); 572 573 SyncFence fence(timelines[0], 0); 574 ASSERT_TRUE(fence.isValid()); 575 576 unordered_map<int, int> fenceMap; 577 fenceMap.insert(make_pair(0, 0)); 578 579 // Randomly create syncpoints out of a fixed set of timelines, and merge them together. 580 for (int i = 0; i < mergeCount; i++) { 581 582 // Generate syncpoint. 583 int timelineOffset = timelineDist(generator); 584 const SyncTimeline& timeline = timelines[timelineOffset]; 585 int syncPoint = syncPointDist(generator); 586 587 // Keep track of the latest syncpoint in each timeline. 588 auto itr = fenceMap.find(timelineOffset); 589 if (itr == end(fenceMap)) { 590 fenceMap.insert(make_pair(timelineOffset, syncPoint)); 591 } 592 else { 593 int oldSyncPoint = itr->second; 594 fenceMap.erase(itr); 595 fenceMap.insert(make_pair(timelineOffset, max(syncPoint, oldSyncPoint))); 596 } 597 598 // Merge. 599 fence = SyncFence(fence, SyncFence(timeline, syncPoint)); 600 ASSERT_TRUE(fence.isValid()); 601 CheckModernLegacyInfoMatch(fence); 602 } 603 604 // Confirm our map matches the fence. 605 ASSERT_EQ(fence.getSize(), fenceMap.size()); 606 607 // Trigger the merged fence. 608 for (auto& item: fenceMap) { 609 ASSERT_EQ(fence.wait(0), -1); 610 ASSERT_EQ(errno, ETIME); 611 612 // Increment the timeline to the last syncpoint. 613 timelines[item.first].inc(item.second); 614 } 615 616 // Check that the fence is triggered. 617 ASSERT_EQ(fence.wait(0), 0); 618} 619 620INSTANTIATE_TEST_CASE_P( 621 ParameterizedMergeStressTest, 622 MergeStressTest, 623 ::testing::Combine(::testing::Values(16,32), ::testing::Values(32, 1024, 1024*32))); 624 625} 626 627