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

总结

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

相关推荐
aqi008 小时前
鸿蒙版本的电子书阅读APP开放源码啦
android·华为·ai编程·harmonyos·鸿蒙
墨天梦8 小时前
B19_SunnyWeather到阅读客户端
android·kotlin
美狐美颜sdk9 小时前
直播APP源码可以直接接入视频美颜sdk吗?技术方案详解
android·人工智能·音视频·美颜sdk·直播美颜sdk
用户693717500138411 小时前
2026,程序员的时代拐点到了
android·前端·后端
浪潮IT馆13 小时前
Android Studio 非最新版本下载安装教程
android·ide·android studio
xiaozongt198914 小时前
MasterGo + Claude Code 生成 Android 布局:完整实践指南
android
Anhty15 小时前
2026最新免费手机音频处理工具 !!
android·功能测试·ios·智能手机·音视频
2501_9159090617 小时前
iOS应用从开发到上架App Store的完整发布流程与步骤指南
android·ios·小程序·https·uni-app·webview
狗凯之家源码网18 小时前
AI 步数修改提交系统源码应用场景与落地指南
android·数据库·人工智能·运动步数
ZHOUPUYU18 小时前
PHP 9.0 前瞻:下一代 PHP 会带来哪些新功能?
android·开发语言·php