1/*
2 * Copyright (C) 2017 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#include <gtest/gtest.h>
18
19#include "thread/ThreadBase.h"
20#include "utils/TimeUtils.h"
21
22#include <chrono>
23#include "unistd.h"
24
25using namespace android;
26using namespace android::uirenderer;
27
28static ThreadBase& thread() {
29    class TestThread : public ThreadBase, public virtual RefBase {};
30    static sp<TestThread> thread = []() -> auto {
31        sp<TestThread> ret{new TestThread};
32        ret->start("TestThread");
33        return ret;
34    }
35    ();
36    return *thread;
37}
38
39static WorkQueue& queue() {
40    return thread().queue();
41}
42
43TEST(ThreadBase, post) {
44    std::atomic_bool ran(false);
45    queue().post([&ran]() { ran = true; });
46    for (int i = 0; !ran && i < 1000; i++) {
47        usleep(1);
48    }
49    ASSERT_TRUE(ran) << "Failed to flip atomic after 1 second";
50}
51
52TEST(ThreadBase, postDelay) {
53    using clock = WorkQueue::clock;
54
55    std::promise<nsecs_t> ranAtPromise;
56    auto queuedAt = clock::now();
57    queue().postDelayed(100_us, [&]() { ranAtPromise.set_value(clock::now()); });
58    auto ranAt = ranAtPromise.get_future().get();
59    auto ranAfter = ranAt - queuedAt;
60    ASSERT_TRUE(ranAfter > 90_us) << "Ran after " << ns2us(ranAfter) << "us <= 90us";
61}
62
63TEST(ThreadBase, runSync) {
64    pid_t thisTid = gettid();
65    pid_t otherTid = thisTid;
66
67    auto result = queue().runSync([&otherTid]() -> auto {
68        otherTid = gettid();
69        return 42;
70    });
71
72    ASSERT_EQ(42, result);
73    ASSERT_NE(thisTid, otherTid);
74}
75
76TEST(ThreadBase, async) {
77    pid_t thisTid = gettid();
78    pid_t thisPid = getpid();
79
80    auto otherTid = queue().async([]() -> auto { return gettid(); });
81    auto otherPid = queue().async([]() -> auto { return getpid(); });
82    auto result = queue().async([]() -> auto { return 42; });
83
84    ASSERT_NE(thisTid, otherTid.get());
85    ASSERT_EQ(thisPid, otherPid.get());
86    ASSERT_EQ(42, result.get());
87}
88
89TEST(ThreadBase, lifecyclePerf) {
90    struct EventCount {
91        std::atomic_int construct{0};
92        std::atomic_int destruct{0};
93        std::atomic_int copy{0};
94        std::atomic_int move{0};
95    };
96
97    struct Counter {
98        Counter(EventCount* count) : mCount(count) { mCount->construct++; }
99
100        Counter(const Counter& other) : mCount(other.mCount) {
101            if (mCount) mCount->copy++;
102        }
103
104        Counter(Counter&& other) : mCount(other.mCount) {
105            other.mCount = nullptr;
106            if (mCount) mCount->move++;
107        }
108
109        Counter& operator=(const Counter& other) {
110            mCount = other.mCount;
111            if (mCount) mCount->copy++;
112            return *this;
113        }
114
115        Counter& operator=(Counter&& other) {
116            mCount = other.mCount;
117            other.mCount = nullptr;
118            if (mCount) mCount->move++;
119            return *this;
120        }
121
122        ~Counter() {
123            if (mCount) mCount->destruct++;
124        }
125
126        EventCount* mCount;
127    };
128
129    EventCount count;
130    {
131        Counter counter{&count};
132        queue().runSync([c = std::move(counter)](){});
133    }
134    ASSERT_EQ(1, count.construct.load());
135    ASSERT_EQ(1, count.destruct.load());
136    ASSERT_EQ(0, count.copy.load());
137    ASSERT_LE(1, count.move.load());
138}
139
140int lifecycleTestHelper(const sp<VirtualLightRefBase>& test) {
141    return queue().runSync([t = test]()->int { return t->getStrongCount(); });
142}
143
144TEST(ThreadBase, lifecycle) {
145    sp<VirtualLightRefBase> dummyObject{new VirtualLightRefBase};
146    ASSERT_EQ(1, dummyObject->getStrongCount());
147    ASSERT_EQ(2, queue().runSync([dummyObject]() -> int { return dummyObject->getStrongCount(); }));
148    ASSERT_EQ(1, dummyObject->getStrongCount());
149    ASSERT_EQ(2, lifecycleTestHelper(dummyObject));
150    ASSERT_EQ(1, dummyObject->getStrongCount());
151}