hwuiTask 线程

渲染线程带了两个工作线程,名称是 hwuiTask0 和 hwuiTask1

下面介绍下它的原理和为什么说是渲染线程的工作线程。

一、创建

这两个线程都是在 CommonPool 构造函数中启动的

CommonPool::CommonPool() {
    ATRACE_CALL();

    CommonPool* pool = this;
    std::mutex mLock;
    std::vector<int> tids(THREAD_COUNT);
    std::vector<std::condition_variable> tidConditionVars(THREAD_COUNT);

    // Create 2 workers
    for (int i = 0; i < THREAD_COUNT; i++) {
        std::thread worker([pool, i, &mLock, &tids, &tidConditionVars] {
            {
                std::array<char, 20> name{"hwuiTask"};
                snprintf(name.data(), name.size(), "hwuiTask%d", i);
                auto self = pthread_self();
                pthread_setname_np(self, name.data());
                {
                    std::unique_lock lock(mLock);
                    tids[i] = pthread_gettid_np(self);
                    tidConditionVars[i].notify_one();
                }
                setpriority(PRIO_PROCESS, 0, PRIORITY_FOREGROUND);
                auto startHook = renderthread::RenderThread::getOnStartHook();
                if (startHook) {
                    startHook(name.data());
                }
            }
            pool->workerLoop();
        });
        worker.detach();
    }
    {
        std::unique_lock lock(mLock);
        for (int i = 0; i < THREAD_COUNT; i++) {
            while (!tids[i]) {
                tidConditionVars[i].wait(lock);
            }
        }
    }
    mWorkerThreadIds = std::move(tids);
}

启动两个线程,优先级设置为 PRIORITY_FOREGROUND,worker.detach() 和创建线程断开联系。

工作函数是 CommonPool::workerLoop,这个函数是

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();
        }
    }
}

工作函数主要工作:

在锁 mLock 的保护下从 mWorkQueue 中去一个 work 去执行。

这里需要理解两个工作线程的工作函数是同一个,所以都是从 mWorkQueue 中获取 work 去执行。

二、任务来源 mWorkQueue

从上面知道工作线程的工作来源于 mWorkQueue ,它是个什么呢?

它是 CommonPool 类的成员

ArrayQueue<Task, QUEUE_SIZE> mWorkQueue;

ArrayQueue 类是 CommonPool.h 文件中定义的类。

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; 
            // 这里有问题,应该是 mHead - mTail + 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;
        T ret = std::move(mBuffer[index]);
        mBuffer[index] = nullptr;
        return ret;
    }

private:
    T mBuffer[SIZE];
    int mHead = 0;
    int mTail = 0;
};

这样就知道了 mWorkQueue 是一个维护128个数据成员的数组的类对象,数组的数据成员是 Task,

using Task = std::function<void()>;

Task 就是一个函数。一般是 lamda 函数

三、mWorkQueue 的数据来源

3.1 直接填充的地方

mWorkQueue 是工作线程的数据来源,那谁又在给 mWorkQueue 填充数据呢?发现只有一个函数。

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();
    }
}

调用 CommonPool::enqueue 的是 CommonPool::post 函数

void CommonPool::post(Task&& task) {
    instance().enqueue(std::move(task));
}

post 是静态函数,enqueue 确是普通类函数

static void post(Task&& func);
void enqueue(Task&&);

所以这里使用了单例去调用 enqueue,这也是为啥工作线程放在 CommonPool 构造函数中的,这样两个工作线程使用的 mWorkQueue 就是这个单例的 mWorkQueue 。

private:
    static CommonPool& instance();

CommonPool& CommonPool::instance() {
    static CommonPool pool;
    return pool;
}

从上知道是通过调用 CommonPool::post 函数填充的 mWorkQueue 。

3.2 谁调用了 CommonPool::post 静态函数

总共有五处

3.2.1 hwui/renderthread/CacheManager.cpp

这里有一次

class CommonPoolExecutor : public SkExecutor {
public:
    virtual void add(std::function<void(void)> func) override { CommonPool::post(std::move(func)); }
};

构建了一个 CommonPoolExecutor 静态对象:

static CommonPoolExecutor sDefaultExecutor;

sDefaultExecutor 赋值给了 GrContextOptions 对象的 fExecutor,之后在skia里有多处调用 add 函数的地方,可以在opengrok上搜索 fExecutor.add

3.2.2 hwui/pipeline/skia/SkiaPipeline.cpp

这里调用了两次

void SkiaPipeline::endCapture(SkSurface* surface) {
    if (CC_LIKELY(mCaptureMode == CaptureMode::None)) { return; }
    mNwayCanvas.reset();
    ATRACE_CALL();
    if (mCaptureSequence > 0 && mCaptureMode == CaptureMode::MultiFrameSKP) {
        mMultiPic->endPage();
        mCaptureSequence--;
        if (mCaptureSequence == 0) {
            mCaptureMode = CaptureMode::None;
            // Pass mMultiPic and mOpenMultiPicStream to a background thread, which will handle
            // the heavyweight serialization work and destroy them. mOpenMultiPicStream is released
            // to a bare pointer because keeping it in a smart pointer makes the lambda
            // non-copyable. The lambda is only called once, so this is safe.
            SkFILEWStream* stream = mOpenMultiPicStream.release();
            CommonPool::post([doc = std::move(mMultiPic), stream]{
                ALOGD("Finalizing multi frame SKP");
                doc->close();
                delete stream;
                ALOGD("Multi frame SKP complete.");
            });
        }
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(filename.c_str());
        if (stream.isValid()) {
            stream.write(data->data(), data->size());
            stream.flush();
            ALOGD("SKP Captured Drawing Output (%zu bytes) for frame. %s", stream.bytesWritten(),
                     filename.c_str());
        }
    });
}

都是 lamda 函数

3.2.3 CommonPool.cpp

这里有两处

    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();
    };

这两个静态函数都使用了 std::packaged_task,不过一个是异步,一个是同步,且异步时还使用了 std::make_shared,为啥异步需要而同步不需要呢?其实是为了保证在推出函数体后,task_t 在异步时不被删除,因为它需要在之后被调用执行。

调用async的地方

void CanvasContext::enqueueFrameWork(std::function<void()>&& func) {
    mFrameFences.push_back(CommonPool::async(std::move(func)));
}

void DrawFrameTask::run() {
...
    if (CC_UNLIKELY(frameCallback)) {
        // 这里 DrawFrameTask::run 每次渲染都会执行,但不是每次 frameCallback 都存在
        context->enqueueFrameWork([frameCallback, context, syncResult = mSyncResult,
                                   frameNr = context->getFrameNumber()]() {
            auto frameCommitCallback = std::move(frameCallback(syncResult, frameNr));
            if (frameCommitCallback) {
                context->addFrameCommitListener(std::move(frameCommitCallback));
            }
        });
    }

runSync 目前只有在tests里有过调用。

四、用法

调用下面的静态函数即可将一些操作放在这两个工作线程

不关心任务什么时候执行完

        CommonPool::post

同步:等待任务执行完

        CommonPool::runSync

异步:不需要立刻等任务执行完,但执行到某些过程点时需要等任务执行完

        CommonPool::async

传入的参数是 std::function<void(void)>,使用 std::move 传入。

五、总结

工作线程是在 CommonPool 的构造函数中创建,CommonPool 构造函数是在第一次调用 CommonPool::instance 静态函数构造单例时调用,CommonPool::instance 又是在 三个静态函数:getThreadIds、waitForIdle 和 post 中调用,所以是第一次调用这三个函数中的一个时创建的这两个工作线程。

waitForIdle 目前在hwui中没有使用;

getThreadIds只在 DrawFrameTask::HintSessionWrapper 的构造函数中使用一次;

所以主要的还是从 post 中调用的。

它主要是协助渲染线程处理一些后台工作,如 skia 执行gl命令画图操作、保证 framework callback 被执行,所以说它是渲染线程的工作线程。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值