Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 129e7fc9 authored by Vishnu Nair's avatar Vishnu Nair Committed by Android (Google) Code Review
Browse files

Merge "SurfaceFlinger: LocklessQueue for transaction queue"

parents 2c9b0191 60d902e6
Loading
Loading
Loading
Loading
+82 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022 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.
 */

#pragma once
#include <atomic>
#include <optional>

template <typename T>
// Single consumer multi producer stack. We can understand the two operations independently to see
// why they are without race condition.
//
// push is responsible for maintaining a linked list stored in mPush, and called from multiple
// threads without lock. We can see that if two threads never observe the same value from
// mPush.load, it just functions as a normal linked list. In the case where two threads observe the
// same value, one of them has to execute the compare_exchange first. The one that doesn't execute
// the compare exchange first, will receive false from compare_exchange. previousHead is updated (by
// compare_exchange) to the most recent value of mPush, and we try again. It's relatively clear to
// see that the process can repeat with an arbitrary number of threads.
//
// Pop is much simpler. If mPop is empty (as it begins) it atomically exchanges
// the entire push list with null. This is safe, since the only other reader (push)
// of mPush will retry if it changes in between it's read and atomic compare. We
// then store the list and pop one element.
//
// If we already had something in the pop list we just pop directly.
class LocklessQueue {
public:
    class Entry {
    public:
        T mValue;
        std::atomic<Entry*> mNext;
        Entry(T value) : mValue(value) {}
    };
    std::atomic<Entry*> mPush = nullptr;
    std::atomic<Entry*> mPop = nullptr;
    bool isEmpty() { return (mPush.load() == nullptr) && (mPop.load() == nullptr); }

    void push(T value) {
        Entry* entry = new Entry(value);
        Entry* previousHead = mPush.load(/*std::memory_order_relaxed*/);
        do {
            entry->mNext = previousHead;
        } while (!mPush.compare_exchange_weak(previousHead, entry)); /*std::memory_order_release*/
    }
    std::optional<T> pop() {
        Entry* popped = mPop.load(/*std::memory_order_acquire*/);
        if (popped) {
            // Single consumer so this is fine
            mPop.store(popped->mNext /* , std::memory_order_release */);
            auto value = popped->mValue;
            delete popped;
            return std::move(value);
        } else {
            Entry* grabbedList = mPush.exchange(nullptr /* , std::memory_order_acquire */);
            if (!grabbedList) return std::nullopt;
            // Reverse the list
            while (grabbedList->mNext) {
                Entry* next = grabbedList->mNext;
                grabbedList->mNext = popped;
                popped = grabbedList;
                grabbedList = next;
            }
            mPop.store(popped /* , std::memory_order_release */);
            auto value = grabbedList->mValue;
            delete grabbedList;
            return std::move(value);
        }
    }
};
+14 −13
Original line number Diff line number Diff line
@@ -3701,6 +3701,8 @@ int SurfaceFlinger::flushPendingTransactionQueues(

            transactions.emplace_back(std::move(transaction));
            transactionQueue.pop();
            mPendingTransactionCount--;
            ATRACE_INT("TransactionQueue", mPendingTransactionCount.load());
        }

        if (transactionQueue.empty()) {
@@ -3724,8 +3726,6 @@ bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) {
    {
        Mutex::Autolock _l(mStateLock);
        {
            Mutex::Autolock _l(mQueueLock);

            int lastTransactionsPendingBarrier = 0;
            int transactionsPendingBarrier = 0;
            // First collect transactions from the pending transaction queues.
@@ -3738,8 +3738,12 @@ bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) {
            // Second, collect transactions from the transaction queue.
            // Here as well we are not allowing unsignaled buffers for the same
            // reason as above.
            while (!mTransactionQueue.empty()) {
                auto& transaction = mTransactionQueue.front();
            while (!mLocklessTransactionQueue.isEmpty()) {
                auto maybeTransaction = mLocklessTransactionQueue.pop();
                if (!maybeTransaction.has_value()) {
                    break;
                }
                auto transaction = maybeTransaction.value();
                const bool pendingTransactions =
                        mPendingTransactionQueues.find(transaction.applyToken) !=
                        mPendingTransactionQueues.end();
@@ -3777,9 +3781,9 @@ bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) {
                        }
                    });
                    transactions.emplace_back(std::move(transaction));
                    mPendingTransactionCount--;
                    ATRACE_INT("TransactionQueue", mPendingTransactionCount.load());
                }
                mTransactionQueue.pop_front();
                ATRACE_INT("TransactionQueue", mTransactionQueue.size());
            }

            // Transactions with a buffer pending on a barrier may be on a different applyToken
@@ -3840,8 +3844,7 @@ bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactio
}

bool SurfaceFlinger::transactionFlushNeeded() {
    Mutex::Autolock _l(mQueueLock);
    return !mPendingTransactionQueues.empty() || !mTransactionQueue.empty();
    return !mPendingTransactionQueues.empty() || !mLocklessTransactionQueue.isEmpty();
}

bool SurfaceFlinger::frameIsEarly(nsecs_t expectedPresentTime, int64_t vsyncId) const {
@@ -4008,17 +4011,15 @@ auto SurfaceFlinger::transactionIsReadyToBeApplied(TransactionState& transaction

void SurfaceFlinger::queueTransaction(TransactionState& state) {
    state.queueTime = systemTime();

    Mutex::Autolock lock(mQueueLock);

    // Generate a CountDownLatch pending state if this is a synchronous transaction.
    if (state.flags & eSynchronous) {
        state.transactionCommittedSignal =
                std::make_shared<CountDownLatch>(CountDownLatch::eSyncTransaction);
    }

    mTransactionQueue.emplace_back(state);
    ATRACE_INT("TransactionQueue", mTransactionQueue.size());
    mLocklessTransactionQueue.push(state);
    mPendingTransactionCount++;
    ATRACE_INT("TransactionQueue", mPendingTransactionCount.load());

    const auto schedule = [](uint32_t flags) {
        if (flags & eEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
+7 −6
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@
#include "FlagManager.h"
#include "FrameTracker.h"
#include "LayerVector.h"
#include "LocklessQueue.h"
#include "Scheduler/RefreshRateConfigs.h"
#include "Scheduler/RefreshRateStats.h"
#include "Scheduler/Scheduler.h"
@@ -754,13 +755,13 @@ private:
            std::vector<TransactionState>& transactions,
            std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
            std::unordered_set<sp<IBinder>, SpHash<IBinder>>& applyTokensWithUnsignaledTransactions,
            bool tryApplyUnsignaled) REQUIRES(mStateLock, mQueueLock);
            bool tryApplyUnsignaled) REQUIRES(mStateLock);

    int flushUnsignaledPendingTransactionQueues(
            std::vector<TransactionState>& transactions,
            std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
            std::unordered_set<sp<IBinder>, SpHash<IBinder>>& applyTokensWithUnsignaledTransactions)
            REQUIRES(mStateLock, mQueueLock);
            REQUIRES(mStateLock);

    uint32_t setClientStateLocked(const FrameTimelineInfo&, ComposerState&,
                                  int64_t desiredPresentTime, bool isAutoTimestamp,
@@ -1086,7 +1087,7 @@ private:
    status_t CheckTransactCodeCredentials(uint32_t code);

    // Add transaction to the Transaction Queue
    void queueTransaction(TransactionState& state) EXCLUDES(mQueueLock);
    void queueTransaction(TransactionState& state);
    void waitForSynchronousTransaction(const CountDownLatch& transactionCommittedSignal);
    void signalSynchronousTransactions(const uint32_t flag);

@@ -1244,11 +1245,11 @@ private:
    uint32_t mTexturePoolSize = 0;
    std::vector<uint32_t> mTexturePool;

    mutable Mutex mQueueLock;
    Condition mTransactionQueueCV;
    std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash>
            mPendingTransactionQueues GUARDED_BY(mQueueLock);
    std::deque<TransactionState> mTransactionQueue GUARDED_BY(mQueueLock);
            mPendingTransactionQueues;
    LocklessQueue<TransactionState> mLocklessTransactionQueue;
    std::atomic<size_t> mPendingTransactionCount = 0;
    /*
     * Feature prototyping
     */
+1 −1
Original line number Diff line number Diff line
@@ -724,7 +724,7 @@ public:
        return mFlinger->setPowerModeInternal(display, mode);
    }

    auto &getTransactionQueue() { return mFlinger->mTransactionQueue; }
    auto &getTransactionQueue() { return mFlinger->mLocklessTransactionQueue; }
    auto &getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; }

    auto setTransactionState(
+1 −1
Original line number Diff line number Diff line
@@ -421,7 +421,7 @@ public:
        return mFlinger->SurfaceFlinger::getDisplayNativePrimaries(displayToken, primaries);
    }

    auto& getTransactionQueue() { return mFlinger->mTransactionQueue; }
    auto& getTransactionQueue() { return mFlinger->mLocklessTransactionQueue; }
    auto& getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; }
    auto& getTransactionCommittedSignals() { return mFlinger->mTransactionCommittedSignals; }

Loading