cf001ce4d155ebd66d832eef9ec520db6503faaa
[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   ASSERT (SpinLock != NULL);\r
240   ASSERT (*SpinLock == SPIN_LOCK_ACQUIRED || *SpinLock == SPIN_LOCK_RELEASED);\r
241   return (BOOLEAN)(\r
242            InterlockedCompareExchangePointer (\r
243              (VOID**)SpinLock,\r
244              (VOID*)SPIN_LOCK_RELEASED,\r
245              (VOID*)SPIN_LOCK_ACQUIRED\r
246              ) == (VOID*)SPIN_LOCK_RELEASED\r
247            );\r
248 }\r
249 \r
250 /**\r
251   Releases a spin lock.\r
252 \r
253   This function places the spin lock specified by SpinLock in the release state\r
254   and returns SpinLock.\r
255 \r
256   If SpinLock is NULL, then ASSERT().\r
257   If SpinLock was not initialized with InitializeSpinLock(), then ASSERT().\r
258 \r
259   @param  SpinLock  A pointer to the spin lock to release.\r
260 \r
261   @return SpinLock\r
262 \r
263 **/\r
264 SPIN_LOCK *\r
265 EFIAPI\r
266 ReleaseSpinLock (\r
267   IN OUT  SPIN_LOCK                 *SpinLock\r
268   )\r
269 {\r
270   ASSERT (SpinLock != NULL);\r
271   ASSERT (*SpinLock == SPIN_LOCK_ACQUIRED || *SpinLock == SPIN_LOCK_RELEASED);\r
272   *SpinLock = SPIN_LOCK_RELEASED;\r
273   return SpinLock;\r
274 }\r
275 \r
276 /**\r
277   Performs an atomic increment of an 32-bit unsigned integer.\r
278 \r
279   Performs an atomic increment of the 32-bit unsigned integer specified by\r
280   Value and returns the incremented value. The increment operation must be\r
281   performed using MP safe mechanisms. The state of the return value is not\r
282   guaranteed to be MP safe.\r
283 \r
284   If Value is NULL, then ASSERT().\r
285 \r
286   @param  Value A pointer to the 32-bit value to increment.\r
287 \r
288   @return The incremented value.\r
289 \r
290 **/\r
291 UINT32\r
292 EFIAPI\r
293 InterlockedIncrement (\r
294   IN      UINT32                    *Value\r
295   )\r
296 {\r
297   ASSERT (Value != NULL);\r
298   return InternalSyncIncrement (Value);\r
299 }\r
300 \r
301 /**\r
302   Performs an atomic decrement of an 32-bit unsigned integer.\r
303 \r
304   Performs an atomic decrement of the 32-bit unsigned integer specified by\r
305   Value and returns the decremented value. The decrement operation must be\r
306   performed using MP safe mechanisms. The state of the return value is not\r
307   guaranteed to be MP safe.\r
308 \r
309   If Value is NULL, then ASSERT().\r
310 \r
311   @param  Value A pointer to the 32-bit value to decrement.\r
312 \r
313   @return The decremented value.\r
314 \r
315 **/\r
316 UINT32\r
317 EFIAPI\r
318 InterlockedDecrement (\r
319   IN      UINT32                    *Value\r
320   )\r
321 {\r
322   ASSERT (Value != NULL);\r
323   return InternalSyncDecrement (Value);\r
324 }\r
325 \r
326 /**\r
327   Performs an atomic compare exchange operation on a 32-bit unsigned integer.\r
328 \r
329   Performs an atomic compare exchange operation on the 32-bit unsigned integer\r
330   specified by Value.  If Value is equal to CompareValue, then Value is set to \r
331   ExchangeValue and CompareValue is returned.  If Value is not equal to CompareValue,\r
332   then Value is returned.  The compare exchange operation must be performed using \r
333   MP safe mechanisms.\r
334 \r
335   If Value is NULL, then ASSERT().\r
336 \r
337   @param  Value         A pointer to the 32-bit value for the compare exchange\r
338                         operation.\r
339   @param  CompareValue  32-bit value used in compare operation.\r
340   @param  ExchangeValue 32-bit value used in exchange operation.\r
341 \r
342   @return The original *Value before exchange.\r
343 \r
344 **/\r
345 UINT32\r
346 EFIAPI\r
347 InterlockedCompareExchange32 (\r
348   IN OUT  UINT32                    *Value,\r
349   IN      UINT32                    CompareValue,\r
350   IN      UINT32                    ExchangeValue\r
351   )\r
352 {\r
353   ASSERT (Value != NULL);\r
354   return InternalSyncCompareExchange32 (Value, CompareValue, ExchangeValue);\r
355 }\r
356 \r
357 /**\r
358   Performs an atomic compare exchange operation on a 64-bit unsigned integer.\r
359 \r
360   Performs an atomic compare exchange operation on the 64-bit unsigned integer specified \r
361   by Value.  If Value is equal to CompareValue, then Value is set to ExchangeValue and \r
362   CompareValue is returned.  If Value is not equal to CompareValue, then Value is returned. \r
363   The compare exchange operation must be performed using MP safe mechanisms.\r
364 \r
365   If Value is NULL, then ASSERT().\r
366 \r
367   @param  Value         A pointer to the 64-bit value for the compare exchange\r
368                         operation.\r
369   @param  CompareValue  64-bit value used in compare operation.\r
370   @param  ExchangeValue 64-bit value used in exchange operation.\r
371 \r
372   @return The original *Value before exchange.\r
373 \r
374 **/\r
375 UINT64\r
376 EFIAPI\r
377 InterlockedCompareExchange64 (\r
378   IN OUT  UINT64                    *Value,\r
379   IN      UINT64                    CompareValue,\r
380   IN      UINT64                    ExchangeValue\r
381   )\r
382 {\r
383   ASSERT (Value != NULL);\r
384   return InternalSyncCompareExchange64 (Value, CompareValue, ExchangeValue);\r
385 }\r
386 \r
387 /**\r
388   Performs an atomic compare exchange operation on a pointer value.\r
389 \r
390   Performs an atomic compare exchange operation on the pointer value specified\r
391   by Value. If Value is equal to CompareValue, then Value is set to\r
392   ExchangeValue and CompareValue is returned. If Value is not equal to\r
393   CompareValue, then Value is returned. The compare exchange operation must be\r
394   performed using MP safe mechanisms.\r
395 \r
396   If Value is NULL, then ASSERT().\r
397 \r
398   @param  Value         A pointer to the pointer value for the compare exchange\r
399                         operation.\r
400   @param  CompareValue  Pointer value used in compare operation.\r
401   @param  ExchangeValue Pointer value used in exchange operation.\r
402 \r
403 **/\r
404 VOID *\r
405 EFIAPI\r
406 InterlockedCompareExchangePointer (\r
407   IN OUT  VOID                      **Value,\r
408   IN      VOID                      *CompareValue,\r
409   IN      VOID                      *ExchangeValue\r
410   )\r
411 {\r
412   switch (sizeof (*Value)) {\r
413     case sizeof (UINT32):\r
414       return (VOID*)(UINTN)InterlockedCompareExchange32 (\r
415                              (UINT32*)Value,\r
416                              (UINT32)(UINTN)CompareValue,\r
417                              (UINT32)(UINTN)ExchangeValue\r
418                              );\r
419     case sizeof (UINT64):\r
420       return (VOID*)(UINTN)InterlockedCompareExchange64 (\r
421                              (UINT64*)Value,\r
422                              (UINT64)(UINTN)CompareValue,\r
423                              (UINT64)(UINTN)ExchangeValue\r
424                              );\r
425     default:\r
426       ASSERT (FALSE);\r
427       return NULL;\r
428   }\r
429 }\r