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