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