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

Generated by: LCOV version 2.0-1