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