LCOV - code coverage report
Current view: top level - legacy/ascend910/platform/common - peterson_lock.cc (source / functions) Coverage Total Hit
Test: coverage.info Lines: 78.5 % 121 95
Test Date: 2026-08-04 10:52:23 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              : #include <securec.h>
      11              : #include "adapter_rts_common.h"
      12              : #include "peterson_lock.h"
      13              : 
      14              : namespace hccl {
      15          522 : PetersonLock::PetersonLock(u64 timeoutSec)
      16         1044 :     : size_(MIN_SHM_LEN), type_(Type::HOST), typeName_("Host"), timeout_(timeoutSec),
      17          522 :     myTurn_(TURN_FOR_HOST)
      18              : {
      19          522 : }
      20              : 
      21           13 : PetersonLock::PetersonLock(void *devPtr, u64 timeoutSec)
      22           26 :     : size_(MIN_SHM_LEN), type_(Type::DEVICE), typeName_("Device"), timeout_(timeoutSec),
      23           13 :     devMem_(devPtr, size_, false), myTurn_(TURN_FOR_DEVICE)
      24              : {
      25           13 : }
      26              : 
      27              : 
      28          532 : PetersonLock::~PetersonLock()
      29              : {
      30          532 :     DeInit();
      31          534 : }
      32              : 
      33          535 : HcclResult PetersonLock::Init()
      34              : {
      35          535 :     if (type_ == Type::HOST) {
      36          522 :         if (AllocDeviceMem() != HCCL_SUCCESS) {
      37            0 :             return HCCL_E_INTERNAL;
      38              :         }
      39              :     }
      40              : 
      41          535 :     auto buffer = reinterpret_cast<u8 *>(devMem_.ptr());
      42          535 :     size_t offset = 0;
      43          535 :     turn_ = reinterpret_cast<volatile u32 *>(buffer + offset);
      44          535 :     offset += sizeof(u32);
      45              : 
      46          535 :     hostFlag_ = reinterpret_cast<volatile u32 *>(buffer + offset);
      47          535 :     offset += sizeof(u32);
      48              : 
      49          535 :     deviceFlag_ = reinterpret_cast<volatile u32 *>(buffer + offset);
      50          535 :     offset += sizeof(u32);
      51              : 
      52          535 :     HCCL_INFO("[PetersonLock][Init] type [%s] init success, memSize [%lu Byte] timeout[%lu s]", typeName_.c_str(),
      53              :         devMem_.size(), timeout_);
      54          535 :     return HCCL_SUCCESS;
      55              : }
      56              : 
      57          522 : HcclResult PetersonLock::AllocDeviceMem()
      58              : {
      59          522 :     if (devMem_.ptr() != nullptr || devMem_.size() != 0) {
      60            0 :         HCCL_ERROR("[PetersonLock][AllocDeviceMem] init failed, maybe it's already inited");
      61            0 :         return HCCL_E_INTERNAL;
      62              :     }
      63              : 
      64          522 :     CHK_RET(DeviceMem::alloc(devMem_, size_));
      65              : 
      66          522 :     if (hrtMemSet(devMem_.ptr(), devMem_.size(), devMem_.size()) != HCCL_SUCCESS) {
      67            0 :         HCCL_ERROR("[PetersonLock][AllocDeviceMem] memset device memory failed");
      68            0 :         return HCCL_E_INTERNAL;
      69              :     }
      70              : 
      71          522 :     HCCL_INFO("[PetersonLock][AllocDeviceMem] Type[%s] alloc memSize[%lu Byte]", typeName_.c_str(), size_);
      72          522 :     return HCCL_SUCCESS;
      73              : }
      74              : 
      75          534 : HcclResult PetersonLock::DeInit()
      76              : {
      77          534 :     size_ = 0;
      78              : 
      79          534 :     turn_ = nullptr;
      80          534 :     hostFlag_ = nullptr;
      81          534 :     deviceFlag_ = nullptr;
      82          534 :     return HCCL_SUCCESS;
      83              : }
      84              : 
      85            2 : u64 PetersonLock::GetDevMemAddr() const
      86              : {
      87            2 :     return reinterpret_cast<u64>(devMem_.ptr());
      88              : }
      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          398 :     auto startTime = std::chrono::steady_clock::now();
      99          400 :     auto timeout = std::chrono::seconds(timeout_);
     100              : 
     101              :     /* 更新flag表明自己准备获取锁 */
     102          398 :     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         1705 :         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         1696 :         CHK_RET(ReadPeerFlag(peerFlag));
     146         1705 :         CHK_RET(ReadTurn(turn));
     147              : 
     148         1705 :         if (peerFlag == FLAG_LOCK && turn == myTurn_) {
     149         1305 :             Wait();
     150              :         } else {
     151          400 :             HCCL_DEBUG("[PetersonLock][Lock] type [%s] got the lock", typeName_.c_str());
     152          400 :             break;
     153              :         }
     154         1305 :     }
     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          796 : HcclResult PetersonLock::WriteSelfFlag(u32 selfFlag)
     175              : {
     176          796 :     if (type_ == Type::DEVICE) {
     177          400 :         *deviceFlag_ = selfFlag;
     178              :     } else {
     179          396 :         u32 hostFlag = selfFlag;
     180          396 :         if (hrtMemSyncCopy(const_cast<u32 *>(hostFlag_), sizeof(u32), &hostFlag,
     181          400 :             sizeof(u32), HcclRtMemcpyKind::HCCL_RT_MEMCPY_KIND_HOST_TO_DEVICE) != HCCL_SUCCESS) {
     182            0 :             HCCL_INFO("[PetersonLock][WriteSelfFlag] H2D write hostFlag not correct");
     183            0 :             return HCCL_E_INTERNAL;
     184              :         }
     185              :     }
     186              : 
     187          800 :     MemFence();
     188          800 :     return HCCL_SUCCESS;
     189              : }
     190              : 
     191          400 : HcclResult PetersonLock::WriteTurn()
     192              : {
     193          400 :     if (type_ == Type::DEVICE) {
     194          200 :         *turn_ = TURN_FOR_DEVICE;
     195              :     } else {
     196          200 :         u32 turn = TURN_FOR_HOST;
     197          200 :         if (hrtMemSyncCopy(const_cast<u32 *>(turn_), sizeof(u32), &turn,
     198          200 :             sizeof(u32), HcclRtMemcpyKind::HCCL_RT_MEMCPY_KIND_HOST_TO_DEVICE) != HCCL_SUCCESS) {
     199            0 :             HCCL_INFO("[PetersonLock][WriteSelfFlag] H2D write turn not correct");
     200            0 :             return HCCL_E_INTERNAL;
     201              :         }
     202              :     }
     203              : 
     204          400 :     MemFence();
     205          400 :     return HCCL_SUCCESS;
     206              : }
     207              : 
     208         1696 : HcclResult PetersonLock::ReadPeerFlag(u32 &peerFlag)
     209              : {
     210         1696 :     if (type_ == Type::DEVICE) {
     211         1505 :         peerFlag = *hostFlag_;
     212              :     } else {
     213          191 :         if (hrtMemSyncCopy(&peerFlag, sizeof(u32), const_cast<u32 *>(deviceFlag_),
     214          200 :             sizeof(u32), HcclRtMemcpyKind::HCCL_RT_MEMCPY_KIND_DEVICE_TO_HOST) != HCCL_SUCCESS) {
     215            0 :             HCCL_INFO("[PetersonLock][ReadPeerFlag] D2H read device flag not correct");
     216            0 :             return HCCL_E_INTERNAL;
     217              :         }
     218              :     }
     219         1705 :     return HCCL_SUCCESS;
     220              : }
     221              : 
     222         1705 : HcclResult PetersonLock::ReadTurn(u32 &turn)
     223              : {
     224         1705 :     if (type_ == Type::DEVICE) {
     225         1505 :         turn = *turn_;
     226              :     } else {
     227          200 :         if (hrtMemSyncCopy(&turn, sizeof(u32), const_cast<u32 *>(turn_),
     228          200 :             sizeof(u32), HcclRtMemcpyKind::HCCL_RT_MEMCPY_KIND_DEVICE_TO_HOST) != HCCL_SUCCESS) {
     229            0 :             HCCL_INFO("[PetersonLock][ReadPeerFlag] D2H read turn not correct");
     230            0 :             return HCCL_E_INTERNAL;
     231              :         }
     232              :     }
     233         1705 :     return HCCL_SUCCESS;
     234              : }
     235              : 
     236          200 : PetersonLockGuard::PetersonLockGuard(PetersonLock *lock) : lock_(lock), lockFailed_(false)
     237              : {
     238          200 :     if (lock_ == nullptr) {
     239            0 :         HCCL_ERROR("[PetersonLockGuard] invalid lock");
     240            0 :         lockFailed_ = true;
     241            0 :         return;
     242              :     }
     243              : 
     244          200 :     if (lock_->Lock() != HCCL_SUCCESS) {
     245            0 :         HCCL_ERROR("[PetersonLockGuard] lock failed");
     246            0 :         lockFailed_ = true;
     247            0 :         lock_ = nullptr;
     248              :     }
     249              : }
     250              : 
     251          200 : PetersonLockGuard::~PetersonLockGuard()
     252              : {
     253          200 :     if (lock_) {
     254          200 :         lock_->Unlock();
     255              :     }
     256          200 : }
     257              : }
        

Generated by: LCOV version 2.0-1