AI代码分析 - LocklessQueue

services\surfaceflinger\LocklessQueue.h

cpp 复制代码
// Single consumer multi producer queue. 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.
template <typename T>
class LocklessQueue {
public:
    bool isEmpty() { return (mPush.load() == nullptr) && (mPop.load() == nullptr); }

    void push(T value) {
        Entry* entry = new Entry(std::move(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 = std::move(popped->mValue);
            delete popped;
            return 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 = std::move(grabbedList->mValue);
            delete grabbedList;
            return value;
        }
    }

private:
    class Entry {
    public:
        T mValue;
        std::atomic<Entry*> mNext;
        Entry(T value) : mValue(value) {}
    };
    std::atomic<Entry*> mPush = nullptr;
    std::atomic<Entry*> mPop = nullptr;
};

LocklessQueue 类解析

源文件:services/surfaceflinger/LocklessQueue.h

概述

这是一个无锁队列 ,采用经典的 MPSC(Multi-Producer Single-Consumer) 模式,即多线程 push、单线程 pop。在 SurfaceFlinger 中主要用于从多个线程安全地收集输入事件/消息,然后在主线程统一消费处理。

核心数据结构

cpp 复制代码
class Entry {
public:
    T mValue;
    std::atomic<Entry*> mNext;
    Entry(T value) : mValue(value) {}
};

std::atomic<Entry*> mPush = nullptr;  // 多线程 push 进来的链表(栈结构,LIFO)
std::atomic<Entry*> mPop  = nullptr;  // 给消费者 pop 用的链表(FIFO 顺序)

示意图:

css 复制代码
mPush → [entry] → [entry] → ...    (多线程并发插入,栈式 LIFO)
mPop  → [entry] → [entry] → ...    (消费者 pop 使用,队列式 FIFO)

push ------ 多线程安全入队

cpp 复制代码
void push(T value) {
    Entry* entry = new Entry(std::move(value));
    Entry* previousHead = mPush.load();
    do {
        entry->mNext = previousHead;
    } while (!mPush.compare_exchange_weak(previousHead, entry));
}

原理:

  1. 创建新节点 entry
  2. 读取当前 mPush 头节点 previousHead
  3. entry->mNext 指向 previousHead
  4. CAS(compare_exchange_weak) 尝试将 mPush 更新为 entry
  5. 如果 CAS 失败(说明其他线程已经修改了 mPush),previousHead 会被自动更新为最新值,重试

这是标准的 Treiber Stack 模式。如果多个线程同时读到同一个 head,只有一个 CAS 会成功,失败的线程会拿到更新后的 head 值并重试,保证了无锁安全。

pop ------ 单消费者出队

cpp 复制代码
std::optional<T> pop() {
    Entry* popped = mPop.load();
    if (popped) {
        // 情况1:mPop 非空,直接从 mPop 链表头部取走
        mPop.store(popped->mNext);
        auto value = std::move(popped->mValue);
        delete popped;
        return value;
    } else {
        // 情况2:mPop 为空,从 mPush 链表批量转移
        Entry* grabbedList = mPush.exchange(nullptr);
        if (!grabbedList) return std::nullopt;
        // 反转链表(LIFO → FIFO)
        while (grabbedList->mNext) {
            Entry* next = grabbedList->mNext;
            grabbedList->mNext = popped;
            popped = grabbedList;
            grabbedList = next;
        }
        mPop.store(popped);
        auto value = std::move(grabbedList->mValue);
        delete grabbedList;
        return value;
    }
}

分两种情况:

情况 1:mPop 非空

直接从 mPop 链表头部取走一个节点,O(1) 操作。由于是单消费者,不需要同步。

情况 2:mPop 为空

  1. exchange 一次性 把整个 mPush 链表偷过来(原子操作,同时将 mPush 置为 nullptr)
  2. 反转链表:因为 push 端是栈式 LIFO 插入的,反转后恢复 FIFO 顺序
  3. 将反转后的链表挂到 mPop 上(最后一个节点直接返回其值)
  4. 返回第一个元素

关键设计点

特性 说明
无锁 push CAS 循环保证多线程安全,无 mutex
批量转移 pop 空时一次性将 push 链表整体搬到 pop 链表,减少争用
反转链表 push 端是 LIFO,反转后变 FIFO,保证正确的出队顺序
内存序 代码中注释掉了显式 memory_order,使用默认的 seq_cst,保证了正确性但性能略有牺牲
单消费者约束 pop 只能从一个线程调用,否则 mPop 的读写会有数据竞争

潜在问题

  1. isEmpty 不是线程安全的 :读取 mPushmPop 之间可能被其他线程修改,结果不一定准确,只能作为启发式判断。
  2. 内存泄漏风险:析构函数没有清理链表,如果队列销毁时还有未 pop 的元素,会泄漏。
  3. 无边界:没有容量限制,理论上可以无限 push,没有背压(back-pressure)机制。
  4. 逐个 new/delete :每次 push 都 new 一个节点,pop 时 delete,有内存分配开销。

总结

LocklessQueue 是一个轻量级的无锁 MPSC 队列,通过 CAS 实现多线程安全的 push,通过原子 exchange 实现高效的批量链表转移,适合"多生产者高频写入、单消费者批量消费"的场景,与 SurfaceFlinger 的主线程消息循环模型高度契合。

相关推荐
AD钙奶-lalala2 分钟前
Android Studio新建项目默认不使用Compose模版
android·ide·android studio
故渊at2 小时前
第一板块:Android 系统基石与运行原理 | 第二篇:Android 编译、打包与安装机制
android·系统架构·apk·打包·application·dalvik·android编译
故渊at2 小时前
第一板块:Android 系统基石与运行原理 | 第三篇:ART 与 Dalvik 运行时环境原理
android·对象模型·内存布局·运行原理·art·dalvik
私人珍藏库3 小时前
【Android】Wallcraft 3.62.0-最强4 K壁纸软件-解锁高级版
android·智能手机·app·工具·软件·多功能
GesLuck4 小时前
Node-RED企业微信发送—群文件
android·java·企业微信
whatever who cares4 小时前
android中fragment demo举例
android·java·开发语言
zhangphil4 小时前
Android将ImageView显示的图原样取出转换为Bitmap,Kotlin
android·kotlin
plainGeekDev4 小时前
CountDownTimer → Flow
android·java·kotlin
仙俊红5 小时前
如何优化 MySQL 深分页 SQL
android·sql·mysql
awu的Android笔记5 小时前
网络闪断 + DNS 故障:Android弱网模拟中最容易被忽视的两个场景
android·tcp/ip