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 
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;
SyncTimeline()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     }
destroy()40     void destroy() {
41         if (m_fdInitialized) {
42             close(m_fd);
43             m_fd = -1;
44             m_fdInitialized = false;
45         }
46     }
~SyncTimeline()47     ~SyncTimeline() {
48         destroy();
49     }
isValid() const50     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     }
getFd() const62     int getFd() const {
63         return m_fd;
64     }
inc(int val=1)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 
setFd(int fd)83     void setFd(int fd) {
84         m_fd = fd;
85         m_fdInitialized = true;
86     }
clearFd()87     void clearFd() {
88         m_fd = -1;
89         m_fdInitialized = false;
90     }
91 public:
isValid() const92     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     }
operator =(SyncFence && rhs)104     SyncFence& operator=(SyncFence &&rhs) noexcept {
105         destroy();
106         if (rhs.isValid()) {
107             setFd(rhs.getFd());
108             rhs.clearFd();
109         }
110         return *this;
111     }
SyncFence(SyncFence && fence)112     SyncFence(SyncFence &&fence) noexcept {
113         if (fence.isValid()) {
114             setFd(fence.getFd());
115             fence.clearFd();
116         }
117     }
SyncFence(const SyncFence & fence)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     }
SyncFence(const SyncTimeline & timeline,int value,const char * name=nullptr)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     }
SyncFence(const SyncFence & a,const SyncFence & b,const char * name=nullptr)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     }
SyncFence(const vector<SyncFence> & sources)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     }
destroy()159     void destroy() {
160         if (isValid()) {
161             close(m_fd);
162             clearFd();
163         }
164     }
~SyncFence()165     ~SyncFence() {
166         destroy();
167     }
getFd() const168     int getFd() const {
169         return m_fd;
170     }
wait(int timeout=-1)171     int wait(int timeout = -1) {
172         return sync_wait(m_fd, timeout);
173     }
getInfo() const174     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     }
getSize() const191     int getSize() const {
192         return getInfo().size();
193     }
getSignaledCount() const194     int getSignaledCount() const {
195         return countWithStatus(1);
196     }
getActiveCount() const197     int getActiveCount() const {
198         return countWithStatus(0);
199     }
getErrorCount() const200     int getErrorCount() const {
201         return countWithStatus(-1);
202     }
203 private:
countWithStatus(int status) const204     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 
CheckModernLegacyInfoMatch(const SyncFence & f)215 static 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 
241 int SyncFence::s_fenceCount = 0;
242 
TEST(AllocTest,Timeline)243 TEST(AllocTest, Timeline) {
244     SyncTimeline timeline;
245     ASSERT_TRUE(timeline.isValid());
246 }
247 
TEST(AllocTest,Fence)248 TEST(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 
TEST(AllocTest,FenceNegative)257 TEST(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 
TEST(FenceTest,OneTimelineWait)272 TEST(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 
TEST(FenceTest,OneTimelinePoll)301 TEST(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 
TEST(FenceTest,OneTimelineMerge)328 TEST(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 
TEST(FenceTest,MergeSameFence)368 TEST(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 
TEST(FenceTest,PollOnDestroyedTimeline)386 TEST(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 
TEST(FenceTest,MultiTimelineWait)416 TEST(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 
TEST(StressTest,TwoThreadsSharedTimeline)451 TEST(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 
485 class ConsumerStressTest : public ::testing::TestWithParam<int> {};
486 
TEST_P(ConsumerStressTest,MultiProducerSingleConsumer)487 TEST_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 }
554 INSTANTIATE_TEST_CASE_P(
555     ParameterizedStressTest,
556     ConsumerStressTest,
557     ::testing::Values(2,4,16));
558 
559 class MergeStressTest : public ::testing::TestWithParam<tuple<int, int>> {};
560 
561 template <typename K, typename V> using dict = unordered_map<K,V>;
562 
TEST_P(MergeStressTest,RandomMerge)563 TEST_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 
620 INSTANTIATE_TEST_CASE_P(
621     ParameterizedMergeStressTest,
622     MergeStressTest,
623     ::testing::Combine(::testing::Values(16,32), ::testing::Values(32, 1024, 1024*32)));
624 
625 }
626 
627