/* * 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())); } TEST(CommonPool, 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()); }