| /* | |
| * Copyright (C) 2019 The Android Open Source Project | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| #include <gtest/gtest.h> | |
| #include "thread/CommonPool.h" | |
| #include <array> | |
| #include <condition_variable> | |
| #include <set> | |
| #include <thread> | |
| #include "unistd.h" | |
| using namespace android; | |
| using namespace android::uirenderer; | |
| TEST(CommonPool, post) { | |
| std::atomic_bool ran(false); | |
| CommonPool::post([&ran] { ran = true; }); | |
| for (int i = 0; !ran && i < 1000; i++) { | |
| usleep(1); | |
| } | |
| EXPECT_TRUE(ran) << "Failed to flip atomic after 1 second"; | |
| } | |
| // test currently relies on timings, which | |
| // makes it flaky. Disable for now | |
| TEST(DISABLED_CommonPool, threadCount) { | |
| std::set<pid_t> threads; | |
| std::array<std::future<pid_t>, 64> futures; | |
| for (int i = 0; i < futures.size(); i++) { | |
| futures[i] = CommonPool::async([] { | |
| usleep(10); | |
| return gettid(); | |
| }); | |
| } | |
| for (auto& f : futures) { | |
| threads.insert(f.get()); | |
| } | |
| EXPECT_EQ(threads.size(), CommonPool::THREAD_COUNT); | |
| EXPECT_EQ(0, threads.count(gettid())); | |
| } | |
| // Disabled since this is flaky. This isn't a necessarily useful functional test, so being | |
| // disabled isn't that significant. However it may be good to resurrect this somehow. | |
| TEST(CommonPool, DISABLED_singleThread) { | |
| std::mutex mutex; | |
| std::condition_variable fence; | |
| bool isProcessing = false; | |
| bool queuedSecond = false; | |
| auto f1 = CommonPool::async([&] { | |
| { | |
| std::unique_lock lock{mutex}; | |
| isProcessing = true; | |
| fence.notify_all(); | |
| while (!queuedSecond) { | |
| fence.wait(lock); | |
| } | |
| } | |
| return gettid(); | |
| }); | |
| { | |
| std::unique_lock lock{mutex}; | |
| while (!isProcessing) { | |
| fence.wait(lock); | |
| } | |
| } | |
| auto f2 = CommonPool::async([] { | |
| return gettid(); | |
| }); | |
| { | |
| std::unique_lock lock{mutex}; | |
| queuedSecond = true; | |
| fence.notify_all(); | |
| } | |
| auto tid1 = f1.get(); | |
| auto tid2 = f2.get(); | |
| EXPECT_EQ(tid1, tid2); | |
| EXPECT_NE(gettid(), tid1); | |
| } | |
| // Test currently relies on timings | |
| // which makes it flaky, disable for now | |
| TEST(DISABLED_CommonPool, fullQueue) { | |
| std::mutex lock; | |
| std::condition_variable fence; | |
| bool signaled = false; | |
| static constexpr auto QUEUE_COUNT = CommonPool::THREAD_COUNT + CommonPool::QUEUE_SIZE + 10; | |
| std::atomic_int queuedCount{0}; | |
| std::array<std::future<void>, QUEUE_COUNT> futures; | |
| std::thread queueThread{[&] { | |
| for (int i = 0; i < QUEUE_COUNT; i++) { | |
| futures[i] = CommonPool::async([&] { | |
| std::unique_lock _lock{lock}; | |
| while (!signaled) { | |
| fence.wait(_lock); | |
| } | |
| }); | |
| queuedCount++; | |
| } | |
| }}; | |
| int previous; | |
| do { | |
| previous = queuedCount.load(); | |
| usleep(10000); | |
| } while (previous != queuedCount.load()); | |
| EXPECT_GT(queuedCount.load(), CommonPool::QUEUE_SIZE); | |
| EXPECT_LT(queuedCount.load(), QUEUE_COUNT); | |
| { | |
| std::unique_lock _lock{lock}; | |
| signaled = true; | |
| fence.notify_all(); | |
| } | |
| queueThread.join(); | |
| EXPECT_EQ(queuedCount.load(), QUEUE_COUNT); | |
| // Ensure all our tasks are finished before return as they have references to the stack | |
| for (auto& f : futures) { | |
| f.get(); | |
| } | |
| } | |
| class ObjectTracker { | |
| static std::atomic_int sGlobalCount; | |
| public: | |
| ObjectTracker() { | |
| sGlobalCount++; | |
| } | |
| ObjectTracker(const ObjectTracker&) { | |
| sGlobalCount++; | |
| } | |
| ObjectTracker(ObjectTracker&&) { | |
| sGlobalCount++; | |
| } | |
| ~ObjectTracker() { | |
| sGlobalCount--; | |
| } | |
| static int count() { return sGlobalCount.load(); } | |
| }; | |
| std::atomic_int ObjectTracker::sGlobalCount{0}; | |
| TEST(CommonPool, asyncLifecycleCheck) { | |
| ASSERT_EQ(0, ObjectTracker::count()); | |
| { | |
| ObjectTracker obj; | |
| ASSERT_EQ(1, ObjectTracker::count()); | |
| EXPECT_LT(1, CommonPool::async([obj] { return ObjectTracker::count(); }).get()); | |
| } | |
| CommonPool::waitForIdle(); | |
| ASSERT_EQ(0, ObjectTracker::count()); | |
| } | |
| TEST(CommonPool, syncLifecycleCheck) { | |
| ASSERT_EQ(0, ObjectTracker::count()); | |
| { | |
| ObjectTracker obj; | |
| ASSERT_EQ(1, ObjectTracker::count()); | |
| EXPECT_LT(1, CommonPool::runSync([obj] { return ObjectTracker::count(); })); | |
| } | |
| CommonPool::waitForIdle(); | |
| ASSERT_EQ(0, ObjectTracker::count()); | |
| } |