Merge "Remove old TaskManager system"

This commit is contained in:
John Reck
2019-03-18 20:31:22 +00:00
committed by Android (Google) Code Review
27 changed files with 375 additions and 800 deletions

View File

@@ -31,6 +31,7 @@
#include <renderthread/CanvasContext.h>
#include <TreeInfo.h>
#include <hwui/Paint.h>
#include <utils/TraceUtils.h>
#include "core_jni_helpers.h"

View File

@@ -191,7 +191,7 @@ cc_defaults {
"surfacetexture/EGLConsumer.cpp",
"surfacetexture/ImageConsumer.cpp",
"surfacetexture/SurfaceTexture.cpp",
"thread/TaskManager.cpp",
"thread/CommonPool.cpp",
"utils/Blur.cpp",
"utils/Color.cpp",
"utils/GLUtils.cpp",
@@ -308,6 +308,7 @@ cc_test {
"tests/unit/main.cpp",
"tests/unit/CacheManagerTests.cpp",
"tests/unit/CanvasContextTests.cpp",
"tests/unit/CommonPoolTests.cpp",
"tests/unit/DamageAccumulatorTests.cpp",
"tests/unit/DeferredLayerUpdaterTests.cpp",
"tests/unit/FatVectorTests.cpp",
@@ -381,7 +382,6 @@ cc_benchmark {
"tests/microbench/LinearAllocatorBench.cpp",
"tests/microbench/PathParserBench.cpp",
"tests/microbench/RenderNodeBench.cpp",
"tests/microbench/TaskManagerBench.cpp",
],
}

View File

@@ -28,6 +28,7 @@
#include "hwui/Bitmap.h"
#include "utils/Color.h"
#include "utils/MathUtils.h"
#include "utils/TraceUtils.h"
using namespace android::uirenderer::renderthread;

View File

@@ -25,6 +25,7 @@
#include <SkPictureRecorder.h>
#include "TreeInfo.h"
#include "VectorDrawable.h"
#include "thread/CommonPool.h"
#include "utils/TraceUtils.h"
#include <unistd.h>
@@ -49,10 +50,6 @@ SkiaPipeline::~SkiaPipeline() {
unpinImages();
}
TaskManager* SkiaPipeline::getTaskManager() {
return mRenderThread.cacheManager().getTaskManager();
}
void SkiaPipeline::onDestroyHardwareResources() {
unpinImages();
mRenderThread.cacheManager().trimStaleResources();
@@ -225,42 +222,21 @@ void SkiaPipeline::renderVectorDrawableCache() {
}
}
class SkiaPipeline::SavePictureProcessor : public TaskProcessor<bool> {
public:
explicit SavePictureProcessor(TaskManager* taskManager) : TaskProcessor<bool>(taskManager) {}
struct SavePictureTask : public Task<bool> {
sk_sp<SkData> data;
std::string filename;
};
void savePicture(const sk_sp<SkData>& data, const std::string& filename) {
sp<SavePictureTask> task(new SavePictureTask());
task->data = data;
task->filename = filename;
TaskProcessor<bool>::add(task);
}
virtual void onProcess(const sp<Task<bool>>& task) override {
ATRACE_NAME("SavePictureTask");
SavePictureTask* t = static_cast<SavePictureTask*>(task.get());
if (0 == access(t->filename.c_str(), F_OK)) {
task->setResult(false);
static void savePictureAsync(const sk_sp<SkData>& data, const std::string& filename) {
CommonPool::post([data, filename] {
if (0 == access(filename.c_str(), F_OK)) {
return;
}
SkFILEWStream stream(t->filename.c_str());
SkFILEWStream stream(filename.c_str());
if (stream.isValid()) {
stream.write(t->data->data(), t->data->size());
stream.write(data->data(), data->size());
stream.flush();
SkDebugf("SKP Captured Drawing Output (%d bytes) for frame. %s", stream.bytesWritten(),
t->filename.c_str());
filename.c_str());
}
task->setResult(true);
}
};
});
}
SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface) {
if (CC_UNLIKELY(Properties::skpCaptureEnabled)) {
@@ -297,16 +273,10 @@ void SkiaPipeline::endCapture(SkSurface* surface) {
ATRACE_END();
// offload saving to file in a different thread
if (!mSavePictureProcessor.get()) {
TaskManager* taskManager = getTaskManager();
mSavePictureProcessor = new SavePictureProcessor(
taskManager->canRunTasks() ? taskManager : nullptr);
}
if (1 == mCaptureSequence) {
mSavePictureProcessor->savePicture(data, mCapturedFile);
savePictureAsync(data, mCapturedFile);
} else {
mSavePictureProcessor->savePicture(
data,
savePictureAsync(data,
mCapturedFile + "_" + std::to_string(mCaptureSequence));
}
mCaptureSequence--;

View File

@@ -33,8 +33,6 @@ public:
explicit SkiaPipeline(renderthread::RenderThread& thread);
virtual ~SkiaPipeline();
TaskManager* getTaskManager() override;
void onDestroyHardwareResources() override;
bool pinImages(std::vector<SkImage*>& mutableImages) override;
@@ -157,11 +155,7 @@ private:
* mCaptureSequence counts how many frames are left to take in the sequence.
*/
int mCaptureSequence = 0;
/**
* mSavePictureProcessor is used to run the file saving code in a separate thread.
*/
class SavePictureProcessor;
sp<SavePictureProcessor> mSavePictureProcessor;
/**
* mRecorder holds the current picture recorder. We could store it on the stack to support
* parallel tryCapture calls (not really needed).

View File

@@ -23,6 +23,7 @@
#include "pipeline/skia/SkiaMemoryTracer.h"
#include "Properties.h"
#include "renderstate/RenderState.h"
#include "thread/CommonPool.h"
#include <GrContextOptions.h>
#include <SkExecutor.h>
@@ -76,29 +77,15 @@ void CacheManager::updateContextCacheSizes() {
mGrContext->setResourceCacheLimits(mMaxResources, mMaxResourceBytes);
}
class CacheManager::SkiaTaskProcessor : public TaskProcessor<bool>, public SkExecutor {
class CommonPoolExecutor : public SkExecutor {
public:
explicit SkiaTaskProcessor(TaskManager* taskManager) : TaskProcessor<bool>(taskManager) {}
// This is really a Task<void> but that doesn't really work when Future<>
// expects to be able to get/set a value
struct SkiaTask : public Task<bool> {
std::function<void()> func;
};
virtual void add(std::function<void(void)> func) override {
sp<SkiaTask> task(new SkiaTask());
task->func = func;
TaskProcessor<bool>::add(task);
}
virtual void onProcess(const sp<Task<bool> >& task) override {
SkiaTask* t = static_cast<SkiaTask*>(task.get());
t->func();
task->setResult(true);
CommonPool::post(std::move(func));
}
};
static CommonPoolExecutor sDefaultExecutor;
void CacheManager::configureContext(GrContextOptions* contextOptions, const void* identity, ssize_t size) {
contextOptions->fAllowPathMaskCaching = true;
@@ -107,12 +94,7 @@ void CacheManager::configureContext(GrContextOptions* contextOptions, const void
// provided to Skia.
contextOptions->fGlyphCacheTextureMaximumBytes = GrNextSizePow2(mMaxSurfaceArea);
if (mTaskManager.canRunTasks()) {
if (!mTaskProcessor.get()) {
mTaskProcessor = new SkiaTaskProcessor(&mTaskManager);
}
contextOptions->fExecutor = mTaskProcessor.get();
}
contextOptions->fExecutor = &sDefaultExecutor;
auto& cache = skiapipeline::ShaderCache::get();
cache.initShaderDiskCache(identity, size);

View File

@@ -24,8 +24,6 @@
#include <vector>
#include "pipeline/skia/VectorDrawableAtlas.h"
#include "thread/TaskManager.h"
#include "thread/TaskProcessor.h"
namespace android {
@@ -54,8 +52,6 @@ public:
size_t getCacheSize() const { return mMaxResourceBytes; }
size_t getBackgroundCacheSize() const { return mBackgroundResourceBytes; }
TaskManager* getTaskManager() { return &mTaskManager; }
private:
friend class RenderThread;
@@ -78,10 +74,6 @@ private:
};
sp<skiapipeline::VectorDrawableAtlas> mVectorDrawableAtlas;
class SkiaTaskProcessor;
sp<SkiaTaskProcessor> mTaskProcessor;
TaskManager mTaskManager;
};
} /* namespace renderthread */

View File

@@ -27,8 +27,10 @@
#include "pipeline/skia/SkiaOpenGLPipeline.h"
#include "pipeline/skia/SkiaPipeline.h"
#include "pipeline/skia/SkiaVulkanPipeline.h"
#include "thread/CommonPool.h"
#include "utils/GLUtils.h"
#include "utils/TimeUtils.h"
#include "utils/TraceUtils.h"
#include "../Properties.h"
#include <cutils/properties.h>
@@ -603,31 +605,14 @@ void CanvasContext::waitOnFences() {
if (mFrameFences.size()) {
ATRACE_CALL();
for (auto& fence : mFrameFences) {
fence->getResult();
fence.get();
}
mFrameFences.clear();
}
}
class CanvasContext::FuncTaskProcessor : public TaskProcessor<bool> {
public:
explicit FuncTaskProcessor(TaskManager* taskManager) : TaskProcessor<bool>(taskManager) {}
virtual void onProcess(const sp<Task<bool> >& task) override {
FuncTask* t = static_cast<FuncTask*>(task.get());
t->func();
task->setResult(true);
}
};
void CanvasContext::enqueueFrameWork(std::function<void()>&& func) {
if (!mFrameWorkProcessor.get()) {
mFrameWorkProcessor = new FuncTaskProcessor(mRenderPipeline->getTaskManager());
}
sp<FuncTask> task(new FuncTask());
task->func = func;
mFrameFences.push_back(task);
mFrameWorkProcessor->add(task);
mFrameFences.push_back(CommonPool::async(std::move(func)));
}
int64_t CanvasContext::getFrameNumber() {

View File

@@ -28,8 +28,6 @@
#include "ReliableSurface.h"
#include "renderthread/RenderTask.h"
#include "renderthread/RenderThread.h"
#include "thread/Task.h"
#include "thread/TaskProcessor.h"
#include <EGL/egl.h>
#include <SkBitmap.h>
@@ -42,6 +40,7 @@
#include <set>
#include <string>
#include <vector>
#include <future>
namespace android {
namespace uirenderer {
@@ -274,15 +273,7 @@ private:
// Stores the bounds of the main content.
Rect mContentDrawBounds;
// TODO: This is really a Task<void> but that doesn't really work
// when Future<> expects to be able to get/set a value
struct FuncTask : public Task<bool> {
std::function<void()> func;
};
class FuncTaskProcessor;
std::vector<sp<FuncTask>> mFrameFences;
sp<TaskProcessor<bool>> mFrameWorkProcessor;
std::vector<std::future<void>> mFrameFences;
std::unique_ptr<IRenderPipeline> mRenderPipeline;
std::vector<std::function<void(int64_t)>> mFrameCompleteCallbacks;

View File

@@ -75,7 +75,6 @@ public:
virtual void renderLayers(const LightGeometry& lightGeometry,
LayerUpdateQueue* layerUpdateQueue, bool opaque,
const LightInfo& lightInfo) = 0;
virtual TaskManager* getTaskManager() = 0;
virtual bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
ErrorHandler* errorHandler) = 0;
virtual bool pinImages(std::vector<SkImage*>& mutableImages) = 0;

View File

@@ -31,6 +31,7 @@
#include "renderthread/RenderThread.h"
#include "utils/Macros.h"
#include "utils/TimeUtils.h"
#include "utils/TraceUtils.h"
#include <ui/GraphicBuffer.h>

View File

@@ -28,6 +28,7 @@
#include "renderstate/RenderState.h"
#include "utils/FatVector.h"
#include "utils/TimeUtils.h"
#include "utils/TraceUtils.h"
#ifdef HWUI_GLES_WRAP_ENABLED
#include "debug/GlesDriver.h"

View File

@@ -23,6 +23,7 @@
#include "RenderThread.h"
#include "renderstate/RenderState.h"
#include "utils/FatVector.h"
#include "utils/TraceUtils.h"
#include <GrBackendSemaphore.h>
#include <GrBackendSurface.h>

View File

@@ -21,6 +21,7 @@
#include "tests/common/TestContext.h"
#include "tests/common/TestScene.h"
#include "tests/common/scenes/TestSceneBase.h"
#include "utils/TraceUtils.h"
#include <benchmark/benchmark.h>
#include <gui/Surface.h>

View File

@@ -1,134 +0,0 @@
/*
* Copyright (C) 2016 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 <benchmark/benchmark.h>
#include "thread/Task.h"
#include "thread/TaskManager.h"
#include "thread/TaskProcessor.h"
#include "thread/ThreadBase.h"
#include <atomic>
#include <vector>
using namespace android;
using namespace android::uirenderer;
class TrivialTask : public Task<char> {};
class TrivialProcessor : public TaskProcessor<char> {
public:
explicit TrivialProcessor(TaskManager* manager) : TaskProcessor(manager) {}
virtual ~TrivialProcessor() {}
virtual void onProcess(const sp<Task<char>>& task) override {
TrivialTask* t = static_cast<TrivialTask*>(task.get());
t->setResult(reinterpret_cast<intptr_t>(t) % 16 == 0 ? 'a' : 'b');
}
};
class TestThread : public ThreadBase, public virtual RefBase {};
void BM_TaskManager_allocateTask(benchmark::State& state) {
std::vector<sp<TrivialTask>> tasks;
tasks.reserve(state.max_iterations);
while (state.KeepRunning()) {
tasks.emplace_back(new TrivialTask);
benchmark::DoNotOptimize(tasks.back());
}
}
BENCHMARK(BM_TaskManager_allocateTask);
void BM_TaskManager_enqueueTask(benchmark::State& state) {
TaskManager taskManager;
sp<TrivialProcessor> processor(new TrivialProcessor(&taskManager));
std::vector<sp<TrivialTask>> tasks;
tasks.reserve(state.max_iterations);
while (state.KeepRunning()) {
tasks.emplace_back(new TrivialTask);
benchmark::DoNotOptimize(tasks.back());
processor->add(tasks.back());
}
for (sp<TrivialTask>& task : tasks) {
task->getResult();
}
}
BENCHMARK(BM_TaskManager_enqueueTask);
void BM_TaskManager_enqueueRunDeleteTask(benchmark::State& state) {
TaskManager taskManager;
sp<TrivialProcessor> processor(new TrivialProcessor(&taskManager));
std::vector<sp<TrivialTask>> tasks;
tasks.reserve(state.max_iterations);
while (state.KeepRunning()) {
tasks.emplace_back(new TrivialTask);
benchmark::DoNotOptimize(tasks.back());
processor->add(tasks.back());
}
state.ResumeTiming();
for (sp<TrivialTask>& task : tasks) {
benchmark::DoNotOptimize(task->getResult());
}
tasks.clear();
state.PauseTiming();
}
BENCHMARK(BM_TaskManager_enqueueRunDeleteTask);
void BM_Thread_enqueueTask(benchmark::State& state) {
sp<TestThread> thread{new TestThread};
thread->start();
atomic_int counter(0);
int expected = 0;
while (state.KeepRunning()) {
expected++;
thread->queue().post([&counter]() { counter++; });
}
thread->queue().runSync([]() {});
thread->requestExit();
thread->join();
if (counter != expected) {
printf("Ran %d lambads, should have been %d\n", counter.load(), expected);
}
}
BENCHMARK(BM_Thread_enqueueTask);
void BM_Thread_enqueueRunDeleteTask(benchmark::State& state) {
sp<TestThread> thread{new TestThread};
thread->start();
std::vector<std::future<int>> tasks;
tasks.reserve(state.max_iterations);
int expected = 0;
while (state.KeepRunning()) {
tasks.emplace_back(thread->queue().async([expected]() -> int { return expected + 1; }));
expected++;
}
state.ResumeTiming();
expected = 0;
for (auto& future : tasks) {
if (future.get() != ++expected) {
printf("Mismatch expected %d vs. observed %d\n", expected, future.get());
}
}
tasks.clear();
state.PauseTiming();
}
BENCHMARK(BM_Thread_enqueueRunDeleteTask);

View File

@@ -0,0 +1,138 @@
/*
* 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(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(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();
}
}

View File

@@ -22,9 +22,6 @@
#include "debug/NullGlesDriver.h"
#include "hwui/Typeface.h"
#include "tests/common/LeakChecker.h"
#include "thread/Task.h"
#include "thread/TaskManager.h"
#include "thread/TaskProcessor.h"
#include <signal.h>

View File

@@ -1,54 +0,0 @@
/*
* Copyright (C) 2013 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.
*/
#ifndef ANDROID_HWUI_BARRIER_H
#define ANDROID_HWUI_BARRIER_H
#include <utils/Condition.h>
namespace android {
namespace uirenderer {
class Barrier {
public:
explicit Barrier(Condition::WakeUpType type = Condition::WAKE_UP_ALL)
: mType(type), mOpened(false) {}
~Barrier() {}
void open() {
Mutex::Autolock l(mLock);
mOpened = true;
mCondition.signal(mType);
}
void wait() const {
Mutex::Autolock l(mLock);
while (!mOpened) {
mCondition.wait(mLock);
}
}
private:
Condition::WakeUpType mType;
volatile bool mOpened;
mutable Mutex mLock;
mutable Condition mCondition;
};
} // namespace uirenderer
} // namespace android
#endif // ANDROID_HWUI_BARRIER_H

View File

@@ -0,0 +1,90 @@
/*
* 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 "CommonPool.h"
#include <sys/resource.h>
#include <utils/Trace.h>
#include "renderthread/RenderThread.h"
#include <array>
namespace android {
namespace uirenderer {
CommonPool::CommonPool() {
ATRACE_CALL();
CommonPool* pool = this;
// Create 2 workers
for (int i = 0; i < THREAD_COUNT; i++) {
std::thread worker([pool, i] {
{
std::array<char, 20> name{"hwuiTask"};
snprintf(name.data(), name.size(), "hwuiTask%d", i);
auto self = pthread_self();
pthread_setname_np(self, name.data());
setpriority(PRIO_PROCESS, 0, PRIORITY_FOREGROUND);
auto startHook = renderthread::RenderThread::getOnStartHook();
if (startHook) {
startHook(name.data());
}
}
pool->workerLoop();
});
worker.detach();
}
}
void CommonPool::post(Task&& task) {
static CommonPool pool;
pool.enqueue(std::move(task));
}
void CommonPool::enqueue(Task&& task) {
std::unique_lock lock(mLock);
while (!mWorkQueue.hasSpace()) {
lock.unlock();
usleep(100);
lock.lock();
}
mWorkQueue.push(std::move(task));
if (mWaitingThreads == THREAD_COUNT || (mWaitingThreads > 0 && mWorkQueue.size() > 1)) {
mCondition.notify_one();
}
}
void CommonPool::workerLoop() {
std::unique_lock lock(mLock);
while (true) {
if (!mWorkQueue.hasWork()) {
mWaitingThreads++;
mCondition.wait(lock);
mWaitingThreads--;
}
// Need to double-check that work is still available now that we have the lock
// It may have already been grabbed by a different thread
while (mWorkQueue.hasWork()) {
auto work = mWorkQueue.pop();
lock.unlock();
work();
lock.lock();
}
}
}
} // namespace uirenderer
} // namespace android

View File

@@ -0,0 +1,115 @@
/*
* 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.
*/
#ifndef FRAMEWORKS_BASE_COMMONPOOL_H
#define FRAMEWORKS_BASE_COMMONPOOL_H
#include "utils/Macros.h"
#include <log/log.h>
#include <condition_variable>
#include <functional>
#include <future>
#include <mutex>
namespace android {
namespace uirenderer {
template <class T, int SIZE>
class ArrayQueue {
PREVENT_COPY_AND_ASSIGN(ArrayQueue);
static_assert(SIZE > 0, "Size must be positive");
public:
ArrayQueue() = default;
~ArrayQueue() = default;
constexpr size_t capacity() const { return SIZE; }
constexpr bool hasWork() const { return mHead != mTail; }
constexpr bool hasSpace() const { return ((mHead + 1) % SIZE) != mTail; }
constexpr int size() const {
if (mHead > mTail) {
return mHead - mTail;
} else {
return mTail - mHead + SIZE;
}
}
constexpr void push(T&& t) {
int newHead = (mHead + 1) % SIZE;
LOG_ALWAYS_FATAL_IF(newHead == mTail, "no space");
mBuffer[mHead] = std::move(t);
mHead = newHead;
}
constexpr T&& pop() {
LOG_ALWAYS_FATAL_IF(mTail == mHead, "empty");
int index = mTail;
mTail = (mTail + 1) % SIZE;
return std::move(mBuffer[index]);
}
private:
T mBuffer[SIZE];
int mHead = 0;
int mTail = 0;
};
class CommonPool {
PREVENT_COPY_AND_ASSIGN(CommonPool);
public:
using Task = std::function<void()>;
static constexpr auto THREAD_COUNT = 2;
static constexpr auto QUEUE_SIZE = 128;
static void post(Task&& func);
template <class F>
static auto async(F&& func) -> std::future<decltype(func())> {
typedef std::packaged_task<decltype(func())()> task_t;
auto task = std::make_shared<task_t>(std::forward<F>(func));
post([task]() { std::invoke(*task); });
return task->get_future();
}
template <class F>
static auto runSync(F&& func) -> decltype(func()) {
std::packaged_task<decltype(func())()> task{std::forward<F>(func)};
post([&task]() { std::invoke(task); });
return task.get_future().get();
};
private:
CommonPool();
~CommonPool() {}
void enqueue(Task&&);
void workerLoop();
std::mutex mLock;
std::condition_variable mCondition;
int mWaitingThreads = 0;
ArrayQueue<Task, QUEUE_SIZE> mWorkQueue;
};
} // namespace uirenderer
} // namespace android
#endif // FRAMEWORKS_BASE_COMMONPOOL_H

View File

@@ -1,59 +0,0 @@
/*
* Copyright (C) 2013 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.
*/
#ifndef ANDROID_HWUI_FUTURE_H
#define ANDROID_HWUI_FUTURE_H
#include <utils/RefBase.h>
#include "Barrier.h"
namespace android {
namespace uirenderer {
template <typename T>
class Future : public LightRefBase<Future<T> > {
public:
explicit Future(Condition::WakeUpType type = Condition::WAKE_UP_ONE)
: mBarrier(type), mResult() {}
~Future() {}
/**
* Returns the result of this future, blocking if
* the result is not available yet.
*/
T get() const {
mBarrier.wait();
return mResult;
}
/**
* This method must be called only once.
*/
void produce(T result) {
mResult = result;
mBarrier.open();
}
private:
Barrier mBarrier;
T mResult;
};
} // namespace uirenderer
} // namespace android
#endif // ANDROID_HWUI_FUTURE_H

View File

@@ -1,59 +0,0 @@
/*
* Copyright (C) 2013 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.
*/
#ifndef ANDROID_HWUI_SIGNAL_H
#define ANDROID_HWUI_SIGNAL_H
#include <stdint.h>
#include <sys/types.h>
#include <utils/threads.h>
namespace android {
namespace uirenderer {
class Signal {
public:
explicit Signal(Condition::WakeUpType type = Condition::WAKE_UP_ALL)
: mType(type), mSignaled(false) {}
~Signal() {}
void signal() {
{
Mutex::Autolock l(mLock);
mSignaled = true;
}
mCondition.signal(mType);
}
void wait() {
Mutex::Autolock l(mLock);
while (!mSignaled) {
mCondition.wait(mLock);
}
mSignaled = false;
}
private:
Condition::WakeUpType mType;
volatile bool mSignaled;
mutable Mutex mLock;
mutable Condition mCondition;
};
} // namespace uirenderer
} // namespace android
#endif // ANDROID_HWUI_SIGNAL_H

View File

@@ -1,54 +0,0 @@
/*
* Copyright (C) 2013 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.
*/
#ifndef ANDROID_HWUI_TASK_H
#define ANDROID_HWUI_TASK_H
#include <utils/RefBase.h>
#include <utils/Trace.h>
#include "Future.h"
namespace android {
namespace uirenderer {
class TaskBase : public RefBase {
public:
TaskBase() {}
virtual ~TaskBase() {}
};
template <typename T>
class Task : public TaskBase {
public:
Task() : mFuture(new Future<T>()) {}
virtual ~Task() {}
T getResult() const { return mFuture->get(); }
void setResult(T result) { mFuture->produce(result); }
protected:
const sp<Future<T> >& future() const { return mFuture; }
private:
sp<Future<T> > mFuture;
};
} // namespace uirenderer
} // namespace android
#endif // ANDROID_HWUI_TASK_H

View File

@@ -1,139 +0,0 @@
/*
* Copyright (C) 2013 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 <sys/resource.h>
#include <sys/sysinfo.h>
#include "Task.h"
#include "TaskManager.h"
#include "TaskProcessor.h"
#include "utils/MathUtils.h"
#include "renderthread/RenderThread.h"
namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
// Manager
///////////////////////////////////////////////////////////////////////////////
TaskManager::TaskManager() {
// Get the number of available CPUs. This value does not change over time.
int cpuCount = sysconf(_SC_NPROCESSORS_CONF);
// Really no point in making more than 2 of these worker threads, but
// we do want to limit ourselves to 1 worker thread on dual-core devices.
int workerCount = cpuCount > 2 ? 2 : 1;
for (int i = 0; i < workerCount; i++) {
String8 name;
name.appendFormat("hwuiTask%d", i + 1);
mThreads.push_back(new WorkerThread(name));
}
}
TaskManager::~TaskManager() {
for (size_t i = 0; i < mThreads.size(); i++) {
mThreads[i]->exit();
}
}
bool TaskManager::canRunTasks() const {
return mThreads.size() > 0;
}
void TaskManager::stop() {
for (size_t i = 0; i < mThreads.size(); i++) {
mThreads[i]->exit();
}
}
bool TaskManager::addTaskBase(const sp<TaskBase>& task, const sp<TaskProcessorBase>& processor) {
if (mThreads.size() > 0) {
TaskWrapper wrapper(task, processor);
size_t minQueueSize = INT_MAX;
sp<WorkerThread> thread;
for (size_t i = 0; i < mThreads.size(); i++) {
if (mThreads[i]->getTaskCount() < minQueueSize) {
thread = mThreads[i];
minQueueSize = mThreads[i]->getTaskCount();
}
}
return thread->addTask(wrapper);
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
// Thread
///////////////////////////////////////////////////////////////////////////////
status_t TaskManager::WorkerThread::readyToRun() {
setpriority(PRIO_PROCESS, 0, PRIORITY_FOREGROUND);
auto onStartHook = renderthread::RenderThread::getOnStartHook();
if (onStartHook) {
onStartHook(mName.c_str());
}
return NO_ERROR;
}
bool TaskManager::WorkerThread::threadLoop() {
mSignal.wait();
std::vector<TaskWrapper> tasks;
{
Mutex::Autolock l(mLock);
tasks.swap(mTasks);
}
for (size_t i = 0; i < tasks.size(); i++) {
const TaskWrapper& task = tasks[i];
task.mProcessor->process(task.mTask);
}
return true;
}
bool TaskManager::WorkerThread::addTask(const TaskWrapper& task) {
if (!isRunning()) {
run(mName.string(), PRIORITY_DEFAULT);
} else if (exitPending()) {
return false;
}
{
Mutex::Autolock l(mLock);
mTasks.push_back(task);
}
mSignal.signal();
return true;
}
size_t TaskManager::WorkerThread::getTaskCount() const {
Mutex::Autolock l(mLock);
return mTasks.size();
}
void TaskManager::WorkerThread::exit() {
requestExit();
mSignal.signal();
}
} // namespace uirenderer
} // namespace android

View File

@@ -1,108 +0,0 @@
/*
* Copyright (C) 2013 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.
*/
#ifndef ANDROID_HWUI_TASK_MANAGER_H
#define ANDROID_HWUI_TASK_MANAGER_H
#include <utils/Mutex.h>
#include <utils/String8.h>
#include <utils/Thread.h>
#include "Signal.h"
#include <vector>
namespace android {
namespace uirenderer {
template <typename T>
class Task;
class TaskBase;
template <typename T>
class TaskProcessor;
class TaskProcessorBase;
class TaskManager {
public:
TaskManager();
~TaskManager();
/**
* Returns true if this task manager can run tasks,
* false otherwise. This method will typically return
* false on a single CPU core device.
*/
bool canRunTasks() const;
/**
* Stops all allocated threads. Adding tasks will start
* the threads again as necessary.
*/
void stop();
private:
template <typename T>
friend class TaskProcessor;
template <typename T>
bool addTask(const sp<Task<T> >& task, const sp<TaskProcessor<T> >& processor) {
return addTaskBase(sp<TaskBase>(task), sp<TaskProcessorBase>(processor));
}
bool addTaskBase(const sp<TaskBase>& task, const sp<TaskProcessorBase>& processor);
struct TaskWrapper {
TaskWrapper() : mTask(), mProcessor() {}
TaskWrapper(const sp<TaskBase>& task, const sp<TaskProcessorBase>& processor)
: mTask(task), mProcessor(processor) {}
sp<TaskBase> mTask;
sp<TaskProcessorBase> mProcessor;
};
class WorkerThread : public Thread {
public:
explicit WorkerThread(const String8& name)
: Thread(false), mSignal(Condition::WAKE_UP_ONE), mName(name) {}
bool addTask(const TaskWrapper& task);
size_t getTaskCount() const;
void exit();
private:
virtual status_t readyToRun() override;
virtual bool threadLoop() override;
// Lock for the list of tasks
mutable Mutex mLock;
std::vector<TaskWrapper> mTasks;
// Signal used to wake up the thread when a new
// task is available in the list
mutable Signal mSignal;
const String8 mName;
};
std::vector<sp<WorkerThread> > mThreads;
};
} // namespace uirenderer
} // namespace android
#endif // ANDROID_HWUI_TASK_MANAGER_H

View File

@@ -1,76 +0,0 @@
/*
* Copyright (C) 2013 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.
*/
#ifndef ANDROID_HWUI_TASK_PROCESSOR_H
#define ANDROID_HWUI_TASK_PROCESSOR_H
#include <utils/RefBase.h>
#include "Task.h"
#include "TaskManager.h"
namespace android {
namespace uirenderer {
class TaskProcessorBase : public RefBase {
public:
TaskProcessorBase() {}
virtual ~TaskProcessorBase(){};
virtual void process(const sp<TaskBase>& task) = 0;
};
template <typename T>
class TaskProcessor : public TaskProcessorBase {
public:
explicit TaskProcessor(TaskManager* manager) : mManager(manager) {}
virtual ~TaskProcessor() {}
void add(const sp<Task<T> >& task) {
if (!addImpl(task)) {
// fall back to immediate execution
process(task);
}
}
virtual void onProcess(const sp<Task<T> >& task) = 0;
private:
bool addImpl(const sp<Task<T> >& task);
virtual void process(const sp<TaskBase>& task) override {
sp<Task<T> > realTask = static_cast<Task<T>*>(task.get());
// This is the right way to do it but sp<> doesn't play nice
// sp<Task<T> > realTask = static_cast<sp<Task<T> > >(task);
onProcess(realTask);
}
TaskManager* mManager;
};
template <typename T>
bool TaskProcessor<T>::addImpl(const sp<Task<T> >& task) {
if (mManager) {
sp<TaskProcessor<T> > self(this);
return mManager->addTask(task, self);
}
return false;
}
}; // namespace uirenderer
}; // namespace android
#endif // ANDROID_HWUI_TASK_PROCESSOR_H

View File

@@ -26,7 +26,6 @@
#include <functional>
#include <future>
#include <mutex>
#include <variant>
#include <vector>
namespace android::uirenderer {