已关闭
[Bug-Report|缺陷反馈]: Translate() 访问 kernelMap_ 时使用的锁与 Register/UnRegister 不一致 #285
MBpanzz创建于  6月24日关闭于  7月8日
MBpanzz
6月24日 创建

Thanks for sending an issue! Please fill in the following template to help quickly solve your problem.

Describe the current behavior / 问题描述 (Mandatory / 必填)

CcuKernelMgr::Translate() 在遍历 kernelMap_ 时持有 translateMutex_,而 Register()UnRegister()kernelMap_ 时持有的是另一把锁 kernelMapMutex_

由于两段逻辑使用不同的锁来保护同一份共享数据 kernelMap_,当同一设备上多个线程分别执行 Register() / UnRegister()Translate() 时,kernelMap_ 可能被一边写入(插入/删除节点)一边读取(find 遍历),在 unordered_map 的并发读/写上存在未定义行为的风险。

在单线程串行调用场景下(先完成所有 Register,再统一调用 Translate)不存在此问题;但在同一设备的多个线程分别操作不同 CcuInsHandle 时可能触发。

相关的代码路径:

Translate(读 kernelMap_,持 translateMutex_):

// ccu_kernel_mgr.cc:503-521
CcuResult CcuKernelMgr::Translate(const std::vector<CcuKernelHandle> &kernelHandles)
{
    std::unique_lock<std::mutex> lock(translateMutex_);   // 锁 A ← 与 Register/UnRegister 不同
    std::vector<CcuKernel *> kernels{};
    for (const auto kernelHandle : kernelHandles) {
        const auto &iter = kernelMap_.find(kernelHandle);  // ← 读 kernelMap_
        if (iter == kernelMap_.end()) {
            return CcuResult::CCU_E_NOT_FOUND;
        }
        kernels.push_back(iter->second.get());
    }
    // 后续翻译操作耗时较长
    CCU_CHK_RET(TransRepResToPhyRes(kernels, devLogicId_));
    CCU_CHK_RET(TransRepSequenceToMicrocode(kernels, isFuncBlock));
    ...
}

Register(写 kernelMap_,持 kernelMapMutex_):

// ccu_kernel_mgr.cc:106+133
std::unique_lock<std::mutex> lock(kernelMapMutex_);       // 锁 B
...
kernelMap_[kernelId_] = std::move(currKernel_);

UnRegister(删 kernelMap_,持 kernelMapMutex_):

// ccu_kernel_mgr.cc:550+561
std::unique_lock<std::mutex> lock(kernelMapMutex_);       // 锁 B
...
kernelMap_.erase(kernelHandle);

Environment / 环境信息 (Mandatory / 必填)

该问题与特定运行环境无关

Steps to reproduce the issue / 重现步骤 (Mandatory / 必填)

该问题出现的可能性较小,但在以下场景中可能有风险:

  1. 同一设备上不同线程分别持有不同的 CcuInsHandle,一个线程调用 HcommCcuKernelRegister(持 kernelMapMutex_ 写),另一个线程调用 HcommCcuKernelRegisterEnd(持 translateMutex_ 读)
  2. 由于 CcuKernelMgr 是 per-device 单例,两个线程不同的锁不能互斥,导致对 kernelMap_ 的读写并发

Describe the expected behavior / 预期结果 (Mandatory / 必填)

kernelMap_ 的所有访问路径应使用同一把锁保护

Special notes for this issue/备注 (Optional / 选填)

该问题可能不存在实际危害

likedislike
LLeewis成员
6月25日 关联了看板:HCCL
Leewis成员
6月25日 评论:

@MBpanzz 感谢反馈,当前问题已收到,待分析代码明确问题后与您反馈。

likedislike
LLeewis成员
6月27日 issue类型由 任务 改变为 缺陷
Leewis成员
7月8日 评论:

您好,相关问题已合入,当前issue将闭环,后续有相关问题欢迎提交issue交流讨论。
https://gitcode.com/cann/hcomm/pull/3119

likedislike
LLeewis成员
7月8日 issue状态由 待办的 改变为 已确认
LLeewis成员
7月8日 issue状态由 已确认 改变为 已完成
LLeewis成员
7月8日 关闭了 issue
LLeewis成员
20 天前 移除了看板:HCCL