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