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