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 : #ifndef PETERSON_LOCK_H
12 : #define PETERSON_LOCK_H
13 :
14 : #include <string>
15 : #include <thread>
16 : #include <atomic>
17 : #include <hccl/hccl_types.h>
18 : #include "hccl_common.h"
19 : #include "mem_device_pub.h"
20 :
21 : /**
22 : * 这里实现Peterson算法来实现host侧与device侧的互斥访问
23 : *
24 : * 背景:host侧与device可以通过H2D/D2H等API来进行共享内存访问,
25 : * 那么这两者之间无法对共享变量使用类似于CAS的机制,因此不能基于
26 : * 原子修改类API的方式实现锁,所以这里基于Peterson算法实现,
27 : * paper参考:https://zoo.cs.yale.edu/classes/cs323/doc/Peterson.pdf
28 : *
29 : * 依赖:Peterson算法虽然不依赖原子操作,但是依赖读写内存序,因此需要添加内存屏障
30 : * 来避免编译器与CPU的乱序执行
31 : *
32 : * 限制:目前实现只支持单个Host线程与单个Device线程互斥访问,不支持Host/Device侧多线程访问,
33 : * 如需单侧多线程访问请先使用std::mutex或者pthread_spinlock_t保证互斥,再使用该锁
34 : *
35 : * 内存布局:
36 : * [u32 turn] [u32 hostFlag] [u32 deviceFlag]
37 : */
38 : namespace hccl {
39 : class PetersonLock {
40 : public:
41 : static constexpr u64 DEFAULT_LOCK_TIMEOUT_SEC = 60; /* 默认的超时时间, 60s */
42 :
43 : /* Host侧对象构造函数,在Init()中会进行Device侧内存申请 */
44 : explicit PetersonLock(u64 timeoutSec);
45 : /* Device侧对象构造函数,devPtr是在Host申请的设备侧内存地址 */
46 : PetersonLock(void *devPtr, u64 timeoutSec);
47 : ~PetersonLock();
48 :
49 : HcclResult Init();
50 : HcclResult DeInit();
51 :
52 : /* 目前该接口只服务于传递地址给Device侧,所以直接返回u64而不是指针 */
53 : u64 GetDevMemAddr() const;
54 :
55 : HcclResult Lock();
56 : HcclResult Unlock();
57 :
58 : /* 显式禁用所有copy、move构造函数,因为锁不可复制,不可移动 */
59 : PetersonLock(const PetersonLock &) = delete;
60 : PetersonLock(PetersonLock &&) = delete;
61 : PetersonLock& operator=(const PetersonLock &) = delete;
62 : PetersonLock& operator=(PetersonLock &&) = delete;
63 : private:
64 : HcclResult AllocDeviceMem();
65 :
66 : HcclResult WriteSelfFlag(u32 selfFlag);
67 : HcclResult WriteTurn();
68 : HcclResult ReadPeerFlag(u32 &peerFlag);
69 : HcclResult ReadTurn(u32 &peerTurn);
70 :
71 : static constexpr size_t MIN_SHM_LEN = 32; /* 最小的共享内存大小 */
72 : enum class Type : int {
73 : HOST = 0,
74 : DEVICE = 1
75 : };
76 :
77 : static constexpr u32 TURN_FOR_HOST = 1;
78 : static constexpr u32 TURN_FOR_DEVICE = 0;
79 : static constexpr u32 FLAG_LOCK = 1; /* 获取锁 */
80 : static constexpr u32 FLAG_UNLOCK = 0; /* 释放锁 */
81 :
82 1198 : void MemFence() const
83 : {
84 : /* 内存屏障,即阻止编译器重排变量读写,也阻止CPU重排变量读写 */
85 : std::atomic_thread_fence(std::memory_order_seq_cst);
86 1198 : }
87 :
88 1320 : void Wait() const
89 : {
90 : /* 无事可做暂时释放CPU */
91 1320 : sched_yield();
92 1320 : }
93 :
94 : size_t size_ = 0;
95 : Type type_ = Type::HOST;
96 : std::string typeName_;
97 : u64 timeout_;
98 : DeviceMem devMem_;
99 : u32 myTurn_;
100 :
101 : /*
102 : * 这里变量使用volatile修饰,是为了Device直接读写内存数据,而不是CPU cache
103 : */
104 : volatile u32 *turn_ = nullptr;
105 : volatile u32 *hostFlag_ = nullptr;
106 : volatile u32 *deviceFlag_ = nullptr;
107 : };
108 : /**
109 : * 使用RAII特性使用PetersonLock,在该对象构造时加锁,析构时释放锁
110 : * 因为获取锁有可能失败,所以要调用IsLockFailed()去检查,只有加锁成功才能继续往下执行
111 : */
112 : class PetersonLockGuard {
113 : public:
114 : explicit PetersonLockGuard(PetersonLock *lock);
115 : ~PetersonLockGuard();
116 :
117 200 : bool IsLockFailed() const
118 : {
119 200 : return lockFailed_;
120 : }
121 :
122 : /* 显式禁用所有copy、move构造函数,因为锁不可复制,不可移动 */
123 : PetersonLockGuard(const PetersonLockGuard &) = delete;
124 : PetersonLockGuard(PetersonLockGuard &&) = delete;
125 : PetersonLockGuard& operator=(const PetersonLockGuard &) = delete;
126 : PetersonLockGuard& operator=(PetersonLockGuard &&) = delete;
127 : private:
128 : PetersonLock *lock_ = nullptr;
129 : bool lockFailed_ = false;
130 : };
131 : }
132 :
133 : #endif
|