版本

menu_open
Wwise SDK 2024.1.0
AkAtomicSpinFuncs.h
浏览该文件的文档.
1 /*******************************************************************************
2 The content of this file includes portions of the AUDIOKINETIC Wwise Technology
3 released in source code form as part of the SDK installer package.
4 
5 Commercial License Usage
6 
7 Licensees holding valid commercial licenses to the AUDIOKINETIC Wwise Technology
8 may use this file in accordance with the end user license agreement provided
9 with the software or, alternatively, in accordance with the terms contained in a
10 written agreement between you and Audiokinetic Inc.
11 
12 Apache License Usage
13 
14 Alternatively, this file may be used under the Apache License, Version 2.0 (the
15 "Apache License"); you may not use this file except in compliance with the
16 Apache License. You may obtain a copy of the Apache License at
17 http://www.apache.org/licenses/LICENSE-2.0.
18 
19 Unless required by applicable law or agreed to in writing, software distributed
20 under the Apache License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
21 OR CONDITIONS OF ANY KIND, either express or implied. See the Apache License for
22 the specific language governing permissions and limitations under the License.
23 
24  Copyright (c) 2024 Audiokinetic Inc.
25 *******************************************************************************/
26 
27 // AkAtomicSpinFuncs.h
28 
29 // Helper functions for some simple spin-locks using atomics.
30 
31 #pragma once
32 
35 
36 #if defined(AK_MONITORX_SUPPORTED)
37 #if defined(_MSC_VER)
38 #include <intrin.h>
39 #elif (defined __clang__ || defined __GNUC__)
40 #include <x86intrin.h>
41 #endif
42 #endif // defined(AK_MONITORX_SUPPORTED)
43 
44 #if !defined(AK_NULL_PLATFORM)
45 /// Platform-dependent helpers
46 namespace AKPLATFORM
47 {
48 #if defined(AK_MONITORX_SUPPORTED)
49  // monitorx and waitx are available on certain AMD CPUs,
50  // so we can have a custom impl of AkLimitedSpinForZero, and AkLimitedSpinToAcquire,
51  // which can slightly improve performance when spinlock is tripped
52 
53  // Waits for a limited amount of time for in_pVal to hit zero (without yielding the thread)
54  inline void AkLimitedSpinForZero(AkAtomic32* in_pVal)
55  {
56  AkInt64 endSpinTime = 0;
57  AkInt64 currentTime = 0;
58  PerformanceCounter(&endSpinTime);
59  endSpinTime += AkInt64(AK::g_fFreqRatio * 0.01); // only spin for about 10us
60  while (true)
61  {
62  // set up monitorx on pVal
63  _mm_monitorx((void*)in_pVal, 0U, 0U);
64  // if pval is zero, skip out
65  if (AkAtomicLoad32(in_pVal) == 0)
66  {
67  break;
68  }
69  // wait until a store to pVal occurs (or ~1us passes)
70  _mm_mwaitx(2U, 0U, 1000U);
71 
72  // Check if we've hit the deadline for the timeout
73  PerformanceCounter(&currentTime);
74  if (currentTime > endSpinTime)
75  {
76  break;
77  }
78  }
79  }
80 
81  // Waits for a limited amount of time for in_pVal to get atomically shift from the expected value to the proposed one
82  // returns true if acquisition succeeded
83  inline bool AkLimitedSpinToAcquire(AkAtomic32* in_pVal, AkInt32 in_proposed, AkInt32 in_expected)
84  {
85  if (AkAtomicCas32(in_pVal, in_proposed, in_expected))
86  {
87  return true;
88  }
89 
90  // Cas failed, start the slower evaluation
91 
92  AkInt64 endSpinTime = 0;
93  AkInt64 currentTime = 0;
94  PerformanceCounter(&endSpinTime);
95  endSpinTime += AkInt64(AK::g_fFreqRatio * 0.01); // only spin for about 10us
96  while (true)
97  {
98  // set up monitorx on pVal
99  _mm_monitorx((void*)in_pVal, 0U, 0U);
100  // attempt cas to acquire and if successful, skip out
101  if (AkAtomicCas32(in_pVal, in_proposed, in_expected))
102  {
103  return true;
104  }
105  // wait until a store to pVal occurs (or ~1us passes)
106  _mm_mwaitx(2U, 0U, 1000U);
107 
108  // Check if we've hit the deadline for the timeout
109  PerformanceCounter(&currentTime);
110  if (currentTime > endSpinTime)
111  {
112  return false;
113  }
114  }
115  }
116 #else
117  // Waits for a limited amount of time for in_pVal to hit zero (without yielding the thread)
118  inline void AkLimitedSpinForZero(AkAtomic32* in_pVal)
119  {
120  AkInt64 endSpinTime = 0;
121  AkInt64 currentTime = 0;
122  PerformanceCounter(&endSpinTime);
123  endSpinTime += AkInt64(AK::g_fFreqRatio * 0.01); // only spin for about 10us
124  while (true)
125  {
126  // if pval is zero, skip out
127  if (AkAtomicLoad32(in_pVal) == 0)
128  {
129  break;
130  }
131  AkSpinHint();
132 
133  // Check if we've hit the deadline for the timeout
134  PerformanceCounter(&currentTime);
135  if (currentTime > endSpinTime)
136  {
137  break;
138  }
139  }
140  }
141 
142  // Waits for a limited amount of time for in_pVal to get atomically shift from the expected value to the proposed one
143  // returns true if acquisition succeeded
144  inline bool AkLimitedSpinToAcquire(AkAtomic32* in_pVal, AkInt32 in_proposed, AkInt32 in_expected)
145  {
146  if (AkAtomicCas32(in_pVal, in_proposed, in_expected))
147  {
148  return true;
149  }
150 
151  // Cas failed, start the slower evaluation
152  AkInt64 endSpinTime = 0;
153  AkInt64 currentTime = 0;
154  PerformanceCounter(&endSpinTime);
155  endSpinTime += AkInt64(AK::g_fFreqRatio * 0.01); // only spin for about 10us
156  while (true)
157  {
158  // attempt cas to acquire and if successful, skip out
159  if (AkAtomicCas32(in_pVal, in_proposed, in_expected))
160  {
161  return true;
162  }
163  AkSpinHint();
164 
165  // Check if we've hit the deadline for the timeout
166  PerformanceCounter(&currentTime);
167  if (currentTime > endSpinTime)
168  {
169  return false;
170  }
171  }
172  }
173 #endif // !defined(AK_MONITORX_SUPPORTED)
174 
175  inline void AkSpinWaitForZero(AkAtomic32* in_pVal)
176  {
177  if (AkAtomicLoad32(in_pVal) == 0)
178  {
179  return;
180  }
181 
182  // do a limited spin on-the-spot until in_pVal hits zero
183  AkLimitedSpinForZero(in_pVal);
184 
185  // if in_pVal is still non-zero, then the other thread is either blocked or waiting for us. Yield for real.
186  while (AkAtomicLoad32(in_pVal))
187  AkThreadYield();
188  }
189 
190  // Waits for a limited amount of time for in_pVal to get atomically shift from 0 to 1
191  inline void AkSpinToAcquire(AkAtomic32* in_pVal, AkInt32 in_proposed, AkInt32 in_expected)
192  {
193  // do a limited spin on-the-spot until in_pVal can successfully hit 1
194  // or if it fails, then the other thread is either blocked or waiting for us. Yield for real.
195  while (!AkLimitedSpinToAcquire(in_pVal, in_proposed, in_expected))
196  {
197  AkThreadYield();
198  }
199  }
200 }
201 #endif
void AkSpinHint()
Definition: AkAtomic.h:37
Platform-dependent helpers
__forceinline int AkAtomicCas32(AkAtomic32 *pDest, long proposed, long expected)
Definition: AkAtomic.h:65
volatile int32_t AkAtomic32
Definition: AkAtomicTypes.h:33
int32_t AkInt32
Signed 32-bit integer
void PerformanceCounter(AkInt64 *out_piLastTime)
Platform Independent Helper
void AkLimitedSpinForZero(AkAtomic32 *in_pVal)
bool AkLimitedSpinToAcquire(AkAtomic32 *in_pVal, AkInt32 in_proposed, AkInt32 in_expected)
AkReal32 g_fFreqRatio
int64_t AkInt64
Signed 64-bit integer
void AkSpinToAcquire(AkAtomic32 *in_pVal, AkInt32 in_proposed, AkInt32 in_expected)
#define AkThreadYield()
Definition: AkAtomic.h:35
void AkSpinWaitForZero(AkAtomic32 *in_pVal)
__forceinline long AkAtomicLoad32(AkAtomic32 *pSrc)
Definition: AkAtomic.h:55

此页面对您是否有帮助?

需要技术支持?

仍有疑问?或者问题?需要更多信息?欢迎联系我们,我们可以提供帮助!

查看我们的“技术支持”页面

介绍一下自己的项目。我们会竭力为您提供帮助。

来注册自己的项目,我们帮您快速入门,不带任何附加条件!

开始 Wwise 之旅