Add the ReadWriteBarrier in the Acquire/ReleaseSpin to block the unexpected optimizat...
[mirror_edk2.git] / MdePkg / Library / BaseLib / SynchronizationMsc.c
1 /** @file
2 Implementation of synchronization functions.
3
4 Copyright (c) 2006 - 2007, Intel Corporation<BR>
5 All rights reserved. This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 Module Name: SynchronizationMsc.c
14
15 **/
16
17 #include "BaseLibInternals.h"
18
19 //
20 // Microsoft Visual Studio 7.1 Function Prototypes for read write barrier Intrinsics
21 //
22 void _ReadWriteBarrier (void);
23 #pragma intrinsic(_ReadWriteBarrier)
24
25
26 #define SPIN_LOCK_RELEASED ((UINTN) 1)
27 #define SPIN_LOCK_ACQUIRED ((UINTN) 2)
28
29 /**
30 Retrieves the architecture specific spin lock alignment requirements for
31 optimal spin lock performance.
32
33 This function retrieves the spin lock alignment requirements for optimal
34 performance on a given CPU architecture. The spin lock alignment must be a
35 power of two and is returned by this function. If there are no alignment
36 requirements, then 1 must be returned. The spin lock synchronization
37 functions must function correctly if the spin lock size and alignment values
38 returned by this function are not used at all. These values are hints to the
39 consumers of the spin lock synchronization functions to obtain optimal spin
40 lock performance.
41
42 @return The architecture specific spin lock alignment.
43
44 **/
45 UINTN
46 EFIAPI
47 GetSpinLockProperties (
48 VOID
49 )
50 {
51 // @bug May use a PCD entry to determine this alignment.
52 return 32;
53 }
54
55 /**
56 Initializes a spin lock to the released state and returns the spin lock.
57
58 This function initializes the spin lock specified by SpinLock to the released
59 state, and returns SpinLock. Optimal performance can be achieved by calling
60 GetSpinLockProperties() to determine the size and alignment requirements for
61 SpinLock.
62
63 If SpinLock is NULL, then ASSERT().
64
65 @param SpinLock A pointer to the spin lock to initialize to the released
66 state.
67
68 @return SpinLock
69
70 **/
71 SPIN_LOCK *
72 EFIAPI
73 InitializeSpinLock (
74 OUT SPIN_LOCK *SpinLock
75 )
76 {
77 ASSERT (SpinLock != NULL);
78
79 _ReadWriteBarrier();
80 *SpinLock = SPIN_LOCK_RELEASED;
81 _ReadWriteBarrier();
82
83 return SpinLock;
84 }
85
86 /**
87 Waits until a spin lock can be placed in the acquired state.
88
89 This function checks the state of the spin lock specified by SpinLock. If
90 SpinLock is in the released state, then this function places SpinLock in the
91 acquired state and returns SpinLock. Otherwise, this function waits
92 indefinitely for the spin lock to be released, and then places it in the
93 acquired state and returns SpinLock. All state transitions of SpinLock must
94 be performed using MP safe mechanisms.
95
96 If SpinLock is NULL, then ASSERT().
97 If SpinLock was not initialized with InitializeSpinLock(), then ASSERT().
98 If PcdSpinLockTimeout is not zero, and SpinLock is can not be acquired in
99 PcdSpinLockTimeout microseconds, then ASSERT().
100
101 @param SpinLock A pointer to the spin lock to place in the acquired state.
102
103 @return SpinLock
104
105 **/
106 SPIN_LOCK *
107 EFIAPI
108 AcquireSpinLock (
109 IN OUT SPIN_LOCK *SpinLock
110 )
111 {
112 UINT64 Tick;
113 UINT64 Start, End;
114 UINT64 Timeout;
115
116 Tick = 0;
117 Start = 0;
118 End = 0;
119 if (PcdGet32 (PcdSpinLockTimeout) > 0) {
120 Tick = GetPerformanceCounter ();
121 Timeout = DivU64x32 (
122 MultU64x32 (
123 GetPerformanceCounterProperties (&Start, &End),
124 PcdGet32 (PcdSpinLockTimeout)
125 ),
126 1000000
127 );
128 if (Start < End) {
129 Tick += Timeout;
130 } else {
131 Tick -= Timeout;
132 }
133 }
134
135 while (!AcquireSpinLockOrFail (SpinLock)) {
136 CpuPause ();
137 ASSERT ((Start < End) ^ (Tick <= GetPerformanceCounter ()));
138 }
139 return SpinLock;
140 }
141
142 /**
143 Attempts to place a spin lock in the acquired state.
144
145 This function checks the state of the spin lock specified by SpinLock. If
146 SpinLock is in the released state, then this function places SpinLock in the
147 acquired state and returns TRUE. Otherwise, FALSE is returned. All state
148 transitions of SpinLock must be performed using MP safe mechanisms.
149
150 If SpinLock is NULL, then ASSERT().
151 If SpinLock was not initialized with InitializeSpinLock(), then ASSERT().
152
153 @param SpinLock A pointer to the spin lock to place in the acquired state.
154
155 @retval TRUE SpinLock was placed in the acquired state.
156 @retval FALSE SpinLock could not be acquired.
157
158 **/
159 BOOLEAN
160 EFIAPI
161 AcquireSpinLockOrFail (
162 IN OUT SPIN_LOCK *SpinLock
163 )
164 {
165 VOID *Result;
166
167 ASSERT (SpinLock != NULL);
168 ASSERT (*SpinLock == SPIN_LOCK_ACQUIRED || *SpinLock == SPIN_LOCK_RELEASED);
169
170 _ReadWriteBarrier ();
171 Result = InterlockedCompareExchangePointer (
172 (VOID**)SpinLock,
173 (VOID*)SPIN_LOCK_RELEASED,
174 (VOID*)SPIN_LOCK_ACQUIRED
175 );
176
177 _ReadWriteBarrier ();
178 return (BOOLEAN) (Result == (VOID*) SPIN_LOCK_RELEASED);
179 }
180
181 /**
182 Releases a spin lock.
183
184 This function places the spin lock specified by SpinLock in the release state
185 and returns SpinLock.
186
187 If SpinLock is NULL, then ASSERT().
188 If SpinLock was not initialized with InitializeSpinLock(), then ASSERT().
189
190 @param SpinLock A pointer to the spin lock to release.
191
192 @return SpinLock
193
194 **/
195 SPIN_LOCK *
196 EFIAPI
197 ReleaseSpinLock (
198 IN OUT SPIN_LOCK *SpinLock
199 )
200 {
201 ASSERT (SpinLock != NULL);
202 ASSERT (*SpinLock == SPIN_LOCK_ACQUIRED || *SpinLock == SPIN_LOCK_RELEASED);
203
204 _ReadWriteBarrier ();
205 *SpinLock = SPIN_LOCK_RELEASED;
206 _ReadWriteBarrier ();
207
208 return SpinLock;
209 }
210
211 /**
212 Performs an atomic increment of an 32-bit unsigned integer.
213
214 Performs an atomic increment of the 32-bit unsigned integer specified by
215 Value and returns the incremented value. The increment operation must be
216 performed using MP safe mechanisms. The state of the return value is not
217 guaranteed to be MP safe.
218
219 If Value is NULL, then ASSERT().
220
221 @param Value A pointer to the 32-bit value to increment.
222
223 @return The incremented value.
224
225 **/
226 UINT32
227 EFIAPI
228 InterlockedIncrement (
229 IN UINT32 *Value
230 )
231 {
232 ASSERT (Value != NULL);
233 return InternalSyncIncrement (Value);
234 }
235
236 /**
237 Performs an atomic decrement of an 32-bit unsigned integer.
238
239 Performs an atomic decrement of the 32-bit unsigned integer specified by
240 Value and returns the decremented value. The decrement operation must be
241 performed using MP safe mechanisms. The state of the return value is not
242 guaranteed to be MP safe.
243
244 If Value is NULL, then ASSERT().
245
246 @param Value A pointer to the 32-bit value to decrement.
247
248 @return The decremented value.
249
250 **/
251 UINT32
252 EFIAPI
253 InterlockedDecrement (
254 IN UINT32 *Value
255 )
256 {
257 ASSERT (Value != NULL);
258 return InternalSyncDecrement (Value);
259 }
260
261 /**
262 Performs an atomic compare exchange operation on a 32-bit unsigned integer.
263
264 Performs an atomic compare exchange operation on the 32-bit unsigned integer
265 specified by Value. If Value is equal to CompareValue, then Value is set to
266 ExchangeValue and CompareValue is returned. If Value is not equal to CompareValue,
267 then Value is returned. The compare exchange operation must be performed using
268 MP safe mechanisms.
269
270 If Value is NULL, then ASSERT().
271
272 @param Value A pointer to the 32-bit value for the compare exchange
273 operation.
274 @param CompareValue 32-bit value used in compare operation.
275 @param ExchangeValue 32-bit value used in exchange operation.
276
277 @return The original *Value before exchange.
278
279 **/
280 UINT32
281 EFIAPI
282 InterlockedCompareExchange32 (
283 IN OUT UINT32 *Value,
284 IN UINT32 CompareValue,
285 IN UINT32 ExchangeValue
286 )
287 {
288 ASSERT (Value != NULL);
289 return InternalSyncCompareExchange32 (Value, CompareValue, ExchangeValue);
290 }
291
292 /**
293 Performs an atomic compare exchange operation on a 64-bit unsigned integer.
294
295 Performs an atomic compare exchange operation on the 64-bit unsigned integer specified
296 by Value. If Value is equal to CompareValue, then Value is set to ExchangeValue and
297 CompareValue is returned. If Value is not equal to CompareValue, then Value is returned.
298 The compare exchange operation must be performed using MP safe mechanisms.
299
300 If Value is NULL, then ASSERT().
301
302 @param Value A pointer to the 64-bit value for the compare exchange
303 operation.
304 @param CompareValue 64-bit value used in compare operation.
305 @param ExchangeValue 64-bit value used in exchange operation.
306
307 @return The original *Value before exchange.
308
309 **/
310 UINT64
311 EFIAPI
312 InterlockedCompareExchange64 (
313 IN OUT UINT64 *Value,
314 IN UINT64 CompareValue,
315 IN UINT64 ExchangeValue
316 )
317 {
318 ASSERT (Value != NULL);
319 return InternalSyncCompareExchange64 (Value, CompareValue, ExchangeValue);
320 }
321
322 /**
323 Performs an atomic compare exchange operation on a pointer value.
324
325 Performs an atomic compare exchange operation on the pointer value specified
326 by Value. If Value is equal to CompareValue, then Value is set to
327 ExchangeValue and CompareValue is returned. If Value is not equal to
328 CompareValue, then Value is returned. The compare exchange operation must be
329 performed using MP safe mechanisms.
330
331 If Value is NULL, then ASSERT().
332
333 @param Value A pointer to the pointer value for the compare exchange
334 operation.
335 @param CompareValue Pointer value used in compare operation.
336 @param ExchangeValue Pointer value used in exchange operation.
337
338 **/
339 VOID *
340 EFIAPI
341 InterlockedCompareExchangePointer (
342 IN OUT VOID **Value,
343 IN VOID *CompareValue,
344 IN VOID *ExchangeValue
345 )
346 {
347 UINT8 SizeOfValue;
348
349 SizeOfValue = sizeof (*Value);
350
351 switch (SizeOfValue) {
352 case sizeof (UINT32):
353 return (VOID*)(UINTN)InterlockedCompareExchange32 (
354 (UINT32*)Value,
355 (UINT32)(UINTN)CompareValue,
356 (UINT32)(UINTN)ExchangeValue
357 );
358 case sizeof (UINT64):
359 return (VOID*)(UINTN)InterlockedCompareExchange64 (
360 (UINT64*)Value,
361 (UINT64)(UINTN)CompareValue,
362 (UINT64)(UINTN)ExchangeValue
363 );
364 default:
365 ASSERT (FALSE);
366 return NULL;
367 }
368 }