LCOV - code coverage report
Current view: top level - legacy/ascend910/platform/common - peterson_lock.cc (source / functions) Coverage Total Hit
Test: coverage.info Lines: 79.8 % 129 103
Test Date: 2026-08-18 17:47:01 Functions: 100.0 % 15 15

            Line data    Source code
       1              : /**
       2              :  * Copyright (c) 2025 Huawei Technologies Co., Ltd.
       3              :  * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
       4              :  * CANN Open Software License Agreement Version 2.0 (the "License").
       5              :  * Please refer to the License for details. You may not use this file except in compliance with the License.
       6              :  * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
       7              :  * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
       8              :  * See LICENSE in the root of the software repository for the full text of the License.
       9              :  */
      10              : 
      11              : #include <securec.h>
      12              : #include "adapter_rts_common.h"
      13              : #include "peterson_lock.h"
      14              : 
      15              : namespace hccl {
      16          525 : PetersonLock::PetersonLock(u64 timeoutSec)
      17          525 :     : size_(MIN_SHM_LEN),
      18          525 :       type_(Type::HOST),
      19         1050 :       typeName_("Host"),
      20          525 :       timeout_(timeoutSec),
      21          525 :       myTurn_(TURN_FOR_HOST)
      22          525 : {}
      23              : 
      24           13 : PetersonLock::PetersonLock(void* devPtr, u64 timeoutSec)
      25           13 :     : size_(MIN_SHM_LEN),
      26           13 :       type_(Type::DEVICE),
      27           26 :       typeName_("Device"),
      28           13 :       timeout_(timeoutSec),
      29           13 :       devMem_(devPtr, size_, false),
      30           13 :       myTurn_(TURN_FOR_DEVICE)
      31           13 : {}
      32              : 
      33          538 : PetersonLock::~PetersonLock() { DeInit(); }
      34              : 
      35          538 : HcclResult PetersonLock::Init()
      36              : {
      37          538 :     if (type_ == Type::HOST) {
      38          525 :         if (AllocDeviceMem() != HCCL_SUCCESS) {
      39            0 :             return HCCL_E_INTERNAL;
      40              :         }
      41              :     }
      42              : 
      43          538 :     auto buffer = reinterpret_cast<u8*>(devMem_.ptr());
      44          538 :     size_t offset = 0;
      45          538 :     turn_ = reinterpret_cast<volatile u32*>(buffer + offset);
      46          538 :     offset += sizeof(u32);
      47              : 
      48          538 :     hostFlag_ = reinterpret_cast<volatile u32*>(buffer + offset);
      49          538 :     offset += sizeof(u32);
      50              : 
      51          538 :     deviceFlag_ = reinterpret_cast<volatile u32*>(buffer + offset);
      52          538 :     offset += sizeof(u32);
      53              : 
      54          538 :     HCCL_INFO(
      55              :         "[PetersonLock][Init] type [%s] init success, memSize [%lu Byte] timeout[%lu s]", typeName_.c_str(),
      56              :         devMem_.size(), timeout_);
      57          538 :     return HCCL_SUCCESS;
      58              : }
      59              : 
      60          525 : HcclResult PetersonLock::AllocDeviceMem()
      61              : {
      62          525 :     if (devMem_.ptr() != nullptr || devMem_.size() != 0) {
      63            0 :         HCCL_ERROR("[PetersonLock][AllocDeviceMem] init failed, maybe it's already inited");
      64            0 :         return HCCL_E_INTERNAL;
      65              :     }
      66              : 
      67          525 :     CHK_RET(DeviceMem::alloc(devMem_, size_));
      68              : 
      69          525 :     if (hrtMemSet(devMem_.ptr(), devMem_.size(), devMem_.size()) != HCCL_SUCCESS) {
      70            0 :         HCCL_ERROR("[PetersonLock][AllocDeviceMem] memset device memory failed");
      71            0 :         return HCCL_E_INTERNAL;
      72              :     }
      73              : 
      74          525 :     HCCL_INFO("[PetersonLock][AllocDeviceMem] Type[%s] alloc memSize[%lu Byte]", typeName_.c_str(), size_);
      75          525 :     return HCCL_SUCCESS;
      76              : }
      77              : 
      78          538 : HcclResult PetersonLock::DeInit()
      79              : {
      80          538 :     size_ = 0;
      81              : 
      82          538 :     turn_ = nullptr;
      83          538 :     hostFlag_ = nullptr;
      84          538 :     deviceFlag_ = nullptr;
      85          538 :     return HCCL_SUCCESS;
      86              : }
      87              : 
      88            2 : u64 PetersonLock::GetDevMemAddr() const { return reinterpret_cast<u64>(devMem_.ptr()); }
      89              : 
      90          400 : HcclResult PetersonLock::Lock()
      91              : {
      92          400 :     if (turn_ == nullptr || hostFlag_ == nullptr || deviceFlag_ == nullptr) {
      93            0 :         HCCL_ERROR("[PetersonLock][lock] ptr is nullptr, maybe not call Init()");
      94            0 :         return HCCL_E_INTERNAL;
      95              :     }
      96              : 
      97          400 :     HCCL_DEBUG("[PetersonLock][Lock] type [%s] before require the lock", typeName_.c_str());
      98          400 :     auto startTime = std::chrono::steady_clock::now();
      99          400 :     auto timeout = std::chrono::seconds(timeout_);
     100              : 
     101              :     /* 更新flag表明自己准备获取锁 */
     102          400 :     CHK_RET(WriteSelfFlag(FLAG_LOCK));
     103              : 
     104              :     /* 更新TURN值 */
     105          400 :     CHK_RET(WriteTurn());
     106              : 
     107              :     /*
     108              :      * 这里判断为真只有一种场景会进入等待,需满足以下两个条件
     109              :      *    - 条件1:peer也想要获取锁
     110              :      *    - 条件2:self更新的turn等于自己设置的值
     111              :      * 通常条件2意味着peer执行的更快,使得自己是覆盖写turn,那么自己就需要等待
     112              :      *
     113              :      *   (PS:当然这里存在并发场景,即self刚写完turn就又读turn,peer也是刚写完turn就又读turn,
     114              :      *   此时两端都满足上述为真,就都进入等待,但是因为turn最终只会有一个值,
     115              :      *   所以再循环一遍后最后turn的值就只能是一个了,此时谁最后更新谁就去等待)
     116              :      *
     117              :      * Q:为什么读取peer为FLAG_UNLOCK时本端一定可以安全获取锁?
     118              :      * A:当自己读到peer是释放锁状态时,peer侧有三种场景:
     119              :      *     - 场景1:peer不准备获取锁,此时self可以安全获取锁
     120              :      *     - 场景2:peer已经释放了锁,此时self可以安全获取锁
     121              :      *     - 场景3:peer侧也想获取锁,但是由于并发(self读flag peer写flag)导致peer更新的flag未被self读到
     122              :      *  对于场景3,由于严格内存序那么写self与读peer操作一定是串行,此时self虽未及时读到peer的flag,
     123              :      *  但是因为自己的flag已经被更新,不会与peer的读有并发,因此一定能被peer读到,那么peer就会获取到
     124              :      *  self要获取锁,就会进入前面描述的状态,即等待self释放锁
     125              :      *  时序图描述大致如下:
     126              :      *             self                          peer
     127              :      *             write flag (LOCK)
     128              :      *             write turn (HOST)
     129              :      *             read peer flag (UNLOCK)       write flag (LOCK)     # 这里并发导致未及时读到最新值
     130              :      *             成功获取锁                     write turn (DEVICE)      # 这里自己一定是最后更新的turn
     131              :      *                                           read peer flag (LOCK) # 读到的一定是获取锁,因为没有并发问题
     132              :      *                                           peer==LOCK && turn==DEVICE 为真,所以等待
     133              :      */
     134              :     u32 peerFlag;
     135              :     u32 turn;
     136              :     while (true) {
     137         1684 :         if ((std::chrono::steady_clock::now() - startTime) > timeout) {
     138            0 :             HCCL_ERROR("[PetersonLock][Lock] type [%s] get lock timeout [%lu s]", typeName_.c_str(), timeout_);
     139              : 
     140              :             /* 重置flag */
     141            0 :             CHK_RET(WriteSelfFlag(FLAG_UNLOCK));
     142            0 :             return HCCL_E_TIMEOUT;
     143              :         }
     144              : 
     145         1678 :         CHK_RET(ReadPeerFlag(peerFlag));
     146         1685 :         CHK_RET(ReadTurn(turn));
     147              : 
     148         1685 :         if (peerFlag == FLAG_LOCK && turn == myTurn_) {
     149         1285 :             Wait();
     150              :         } else {
     151          400 :             HCCL_DEBUG("[PetersonLock][Lock] type [%s] got the lock", typeName_.c_str());
     152          400 :             break;
     153              :         }
     154         1285 :     }
     155              : 
     156          400 :     HCCL_DEBUG("[PetersonLock][Lock] type [%s] after require the lock", typeName_.c_str());
     157          400 :     return HCCL_SUCCESS;
     158              : }
     159              : 
     160          400 : HcclResult PetersonLock::Unlock()
     161              : {
     162          400 :     if (deviceFlag_ == nullptr) {
     163            0 :         HCCL_ERROR("[PetersonLock][Unlock] ptr is nullptr, maybe not call Init()");
     164            0 :         return HCCL_E_INTERNAL;
     165              :     }
     166              : 
     167              :     /* 释放锁 */
     168          400 :     WriteSelfFlag(FLAG_UNLOCK);
     169              : 
     170          400 :     HCCL_DEBUG("[PetersonLock][Unlock] type [%s] release the lock", typeName_.c_str());
     171          400 :     return HCCL_SUCCESS;
     172              : }
     173              : 
     174          799 : HcclResult PetersonLock::WriteSelfFlag(u32 selfFlag)
     175              : {
     176          799 :     if (type_ == Type::DEVICE) {
     177          400 :         *deviceFlag_ = selfFlag;
     178              :     } else {
     179          399 :         u32 hostFlag = selfFlag;
     180          799 :         if (hrtMemSyncCopy(
     181          399 :                 const_cast<u32*>(hostFlag_), sizeof(u32), &hostFlag, sizeof(u32),
     182              :                 HcclRtMemcpyKind::HCCL_RT_MEMCPY_KIND_HOST_TO_DEVICE)
     183          400 :             != HCCL_SUCCESS) {
     184            0 :             HCCL_INFO("[PetersonLock][WriteSelfFlag] H2D write hostFlag not correct");
     185            0 :             return HCCL_E_INTERNAL;
     186              :         }
     187              :     }
     188              : 
     189          800 :     MemFence();
     190          800 :     return HCCL_SUCCESS;
     191              : }
     192              : 
     193          400 : HcclResult PetersonLock::WriteTurn()
     194              : {
     195          400 :     if (type_ == Type::DEVICE) {
     196          200 :         *turn_ = TURN_FOR_DEVICE;
     197              :     } else {
     198          200 :         u32 turn = TURN_FOR_HOST;
     199          400 :         if (hrtMemSyncCopy(
     200          200 :                 const_cast<u32*>(turn_), sizeof(u32), &turn, sizeof(u32),
     201              :                 HcclRtMemcpyKind::HCCL_RT_MEMCPY_KIND_HOST_TO_DEVICE)
     202          200 :             != HCCL_SUCCESS) {
     203            0 :             HCCL_INFO("[PetersonLock][WriteSelfFlag] H2D write turn not correct");
     204            0 :             return HCCL_E_INTERNAL;
     205              :         }
     206              :     }
     207              : 
     208          400 :     MemFence();
     209          399 :     return HCCL_SUCCESS;
     210              : }
     211              : 
     212         1678 : HcclResult PetersonLock::ReadPeerFlag(u32& peerFlag)
     213              : {
     214         1678 :     if (type_ == Type::DEVICE) {
     215         1485 :         peerFlag = *hostFlag_;
     216              :     } else {
     217          393 :         if (hrtMemSyncCopy(
     218          193 :                 &peerFlag, sizeof(u32), const_cast<u32*>(deviceFlag_), sizeof(u32),
     219              :                 HcclRtMemcpyKind::HCCL_RT_MEMCPY_KIND_DEVICE_TO_HOST)
     220          200 :             != HCCL_SUCCESS) {
     221            0 :             HCCL_INFO("[PetersonLock][ReadPeerFlag] D2H read device flag not correct");
     222            0 :             return HCCL_E_INTERNAL;
     223              :         }
     224              :     }
     225         1685 :     return HCCL_SUCCESS;
     226              : }
     227              : 
     228         1685 : HcclResult PetersonLock::ReadTurn(u32& turn)
     229              : {
     230         1685 :     if (type_ == Type::DEVICE) {
     231         1485 :         turn = *turn_;
     232              :     } else {
     233          400 :         if (hrtMemSyncCopy(
     234          200 :                 &turn, sizeof(u32), const_cast<u32*>(turn_), sizeof(u32),
     235              :                 HcclRtMemcpyKind::HCCL_RT_MEMCPY_KIND_DEVICE_TO_HOST)
     236          200 :             != HCCL_SUCCESS) {
     237            0 :             HCCL_INFO("[PetersonLock][ReadPeerFlag] D2H read turn not correct");
     238            0 :             return HCCL_E_INTERNAL;
     239              :         }
     240              :     }
     241         1685 :     return HCCL_SUCCESS;
     242              : }
     243              : 
     244          200 : PetersonLockGuard::PetersonLockGuard(PetersonLock* lock) : lock_(lock), lockFailed_(false)
     245              : {
     246          200 :     if (lock_ == nullptr) {
     247            0 :         HCCL_ERROR("[PetersonLockGuard] invalid lock");
     248            0 :         lockFailed_ = true;
     249            0 :         return;
     250              :     }
     251              : 
     252          200 :     if (lock_->Lock() != HCCL_SUCCESS) {
     253            0 :         HCCL_ERROR("[PetersonLockGuard] lock failed");
     254            0 :         lockFailed_ = true;
     255            0 :         lock_ = nullptr;
     256              :     }
     257              : }
     258              : 
     259          200 : PetersonLockGuard::~PetersonLockGuard()
     260              : {
     261          200 :     if (lock_) {
     262          200 :         lock_->Unlock();
     263              :     }
     264          200 : }
     265              : } // namespace hccl
        

Generated by: LCOV version 2.0-1