git-svn-id: https://edk2.tianocore.org/svn/edk2/trunk@1931 de2fecce-e211-0410-80a6...
[people/mcb30/edk2.git] / edk2 / MdePkg / Library / BaseLib / Synchronization.c
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
17 #define SPIN_LOCK_RELEASED          ((SPIN_LOCK)1)\r
18 #define SPIN_LOCK_ACQUIRED          ((SPIN_LOCK)2)\r
19 \r
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
33 UINT32\r
34 EFIAPI\r
35 InternalSyncIncrement (\r
36   IN      volatile UINT32           *Value\r
37   );\r
38 \r
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
52 UINT32\r
53 EFIAPI\r
54 InternalSyncDecrement (\r
55   IN      volatile UINT32           *Value\r
56   );\r
57 \r
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
75 UINT32\r
76 EFIAPI\r
77 InternalSyncCompareExchange32 (\r
78   IN      volatile UINT32           *Value,\r
79   IN      UINT32                    CompareValue,\r
80   IN      UINT32                    ExchangeValue\r
81   );\r
82 \r
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
99 UINT64\r
100 EFIAPI\r
101 InternalSyncCompareExchange64 (\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
123 UINTN\r
124 EFIAPI\r
125 GetSpinLockProperties (\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
149 SPIN_LOCK *\r
150 EFIAPI\r
151 InitializeSpinLock (\r
152   OUT     SPIN_LOCK                 *SpinLock\r
153   )\r
154 {\r
155   ASSERT (SpinLock != NULL);\r
156   *SpinLock = SPIN_LOCK_RELEASED;\r
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
180 SPIN_LOCK *\r
181 EFIAPI\r
182 AcquireSpinLock (\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
193   if (PcdGet32 (PcdSpinLockTimeout) > 0) {\r
194     Tick = GetPerformanceCounter ();\r
195     Timeout = DivU64x32 (\r
196                 MultU64x32 (\r
197                   GetPerformanceCounterProperties (&Start, &End),\r
198                   PcdGet32 (PcdSpinLockTimeout)\r
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
233 BOOLEAN\r
234 EFIAPI\r
235 AcquireSpinLockOrFail (\r
236   IN OUT  SPIN_LOCK                 *SpinLock\r
237   )\r
238 {\r
239   SPIN_LOCK    LockValue;\r
240 \r
241   ASSERT (SpinLock != NULL);\r
242 \r
243   LockValue = *SpinLock;\r
244   ASSERT (LockValue == SPIN_LOCK_ACQUIRED || LockValue == SPIN_LOCK_RELEASED);\r
245 \r
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
269 SPIN_LOCK *\r
270 EFIAPI\r
271 ReleaseSpinLock (\r
272   IN OUT  SPIN_LOCK                 *SpinLock\r
273   )\r
274 {\r
275   SPIN_LOCK    LockValue;\r
276 \r
277   ASSERT (SpinLock != NULL);\r
278 \r
279   LockValue = *SpinLock;\r
280   ASSERT (LockValue == SPIN_LOCK_ACQUIRED || LockValue == SPIN_LOCK_RELEASED);\r
281 \r
282   *SpinLock = SPIN_LOCK_RELEASED;\r
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
301 UINT32\r
302 EFIAPI\r
303 InterlockedIncrement (\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
326 UINT32\r
327 EFIAPI\r
328 InterlockedDecrement (\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
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
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
355 UINT32\r
356 EFIAPI\r
357 InterlockedCompareExchange32 (\r
358   IN OUT  UINT32                    *Value,\r
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
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
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
385 UINT64\r
386 EFIAPI\r
387 InterlockedCompareExchange64 (\r
388   IN OUT  UINT64                    *Value,\r
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
414 VOID *\r
415 EFIAPI\r
416 InterlockedCompareExchangePointer (\r
417   IN OUT  VOID                      **Value,\r
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