LCOV - code coverage report
Current view: top level - src/include/storage - s_lock.h (source / functions) Coverage Total Hit
Test: Code coverage Lines: 100.0 % 13 13
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 3 3
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * s_lock.h
       4              :  *         Implementation of spinlocks.
       5              :  *
       6              :  *      NOTE: none of the macros in this file are intended to be called directly.
       7              :  *      Call them through the macros in spin.h.
       8              :  *
       9              :  *      The following hardware-dependent macros must be provided for each
      10              :  *      supported platform:
      11              :  *
      12              :  *      void S_INIT_LOCK(slock_t *lock)
      13              :  *              Initialize a spinlock (to the unlocked state).
      14              :  *
      15              :  *      int S_LOCK(slock_t *lock)
      16              :  *              Acquire a spinlock, waiting if necessary.
      17              :  *              Time out and abort() if unable to acquire the lock in a
      18              :  *              "reasonable" amount of time --- typically ~ 1 minute.
      19              :  *              Should return number of "delays"; see s_lock.c
      20              :  *
      21              :  *      void S_UNLOCK(slock_t *lock)
      22              :  *              Unlock a previously acquired lock.
      23              :  *
      24              :  *      bool S_LOCK_FREE(slock_t *lock)
      25              :  *              Tests if the lock is free. Returns true if free, false if locked.
      26              :  *              This does *not* change the state of the lock.
      27              :  *
      28              :  *      void SPIN_DELAY(void)
      29              :  *              Delay operation to occur inside spinlock wait loop.
      30              :  *
      31              :  *      Note to implementors: there are default implementations for all these
      32              :  *      macros at the bottom of the file.  Check if your platform can use
      33              :  *      these or needs to override them.
      34              :  *
      35              :  *  Usually, S_LOCK() is implemented in terms of even lower-level macros
      36              :  *      TAS() and TAS_SPIN():
      37              :  *
      38              :  *      int TAS(slock_t *lock)
      39              :  *              Atomic test-and-set instruction.  Attempt to acquire the lock,
      40              :  *              but do *not* wait.      Returns 0 if successful, nonzero if unable
      41              :  *              to acquire the lock.
      42              :  *
      43              :  *      int TAS_SPIN(slock_t *lock)
      44              :  *              Like TAS(), but this version is used when waiting for a lock
      45              :  *              previously found to be contended.  By default, this is the
      46              :  *              same as TAS(), but on some architectures it's better to poll a
      47              :  *              contended lock using an unlocked instruction and retry the
      48              :  *              atomic test-and-set only when it appears free.
      49              :  *
      50              :  *      TAS() and TAS_SPIN() are NOT part of the API, and should never be called
      51              :  *      directly.
      52              :  *
      53              :  *      CAUTION: on some platforms TAS() and/or TAS_SPIN() may sometimes report
      54              :  *      failure to acquire a lock even when the lock is not locked.  For example,
      55              :  *      on Alpha TAS() will "fail" if interrupted.  Therefore a retry loop must
      56              :  *      always be used, even if you are certain the lock is free.
      57              :  *
      58              :  *      It is the responsibility of these macros to make sure that the compiler
      59              :  *      does not re-order accesses to shared memory to precede the actual lock
      60              :  *      acquisition, or follow the lock release.  Prior to PostgreSQL 9.5, this
      61              :  *      was the caller's responsibility, which meant that callers had to use
      62              :  *      volatile-qualified pointers to refer to both the spinlock itself and the
      63              :  *      shared data being accessed within the spinlocked critical section.  This
      64              :  *      was notationally awkward, easy to forget (and thus error-prone), and
      65              :  *      prevented some useful compiler optimizations.  For these reasons, we
      66              :  *      now require that the macros themselves prevent compiler re-ordering,
      67              :  *      so that the caller doesn't need to take special precautions.
      68              :  *
      69              :  *      On platforms with weak memory ordering, the TAS(), TAS_SPIN(), and
      70              :  *      S_UNLOCK() macros must further include hardware-level memory fence
      71              :  *      instructions to prevent similar re-ordering at the hardware level.
      72              :  *      TAS() and TAS_SPIN() must guarantee that loads and stores issued after
      73              :  *      the macro are not executed until the lock has been obtained.  Conversely,
      74              :  *      S_UNLOCK() must guarantee that loads and stores issued before the macro
      75              :  *      have been executed before the lock is released.
      76              :  *
      77              :  *      On most supported platforms, TAS() uses a tas() function written
      78              :  *      in assembly language to execute a hardware atomic-test-and-set
      79              :  *      instruction.  Equivalent OS-supplied mutex routines could be used too.
      80              :  *
      81              :  *
      82              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      83              :  * Portions Copyright (c) 1994, Regents of the University of California
      84              :  *
      85              :  *        src/include/storage/s_lock.h
      86              :  *
      87              :  *-------------------------------------------------------------------------
      88              :  */
      89              : #ifndef S_LOCK_H
      90              : #define S_LOCK_H
      91              : 
      92              : #ifdef FRONTEND
      93              : #error "s_lock.h may not be included from frontend code"
      94              : #endif
      95              : 
      96              : #if defined(__GNUC__) || defined(__INTEL_COMPILER)
      97              : /*************************************************************************
      98              :  * All the gcc inlines
      99              :  * Gcc consistently defines the CPU as __cpu__.
     100              :  * Other compilers use __cpu or __cpu__ so we test for both in those cases.
     101              :  */
     102              : 
     103              : /*----------
     104              :  * Standard gcc asm format (assuming "volatile slock_t *lock"):
     105              : 
     106              :         __asm__ __volatile__(
     107              :                 "  instruction     \n"
     108              :                 "  instruction     \n"
     109              :                 "  instruction     \n"
     110              : :               "=r"(_res), "+m"(*lock)             // return register, in/out lock value
     111              : :               "r"(lock)                                     // lock pointer, in input register
     112              : :               "memory", "cc");                    // show clobbered registers here
     113              : 
     114              :  * The output-operands list (after first colon) should always include
     115              :  * "+m"(*lock), whether or not the asm code actually refers to this
     116              :  * operand directly.  This ensures that gcc believes the value in the
     117              :  * lock variable is used and set by the asm code.  Also, the clobbers
     118              :  * list (after third colon) should always include "memory"; this prevents
     119              :  * gcc from thinking it can cache the values of shared-memory fields
     120              :  * across the asm code.  Add "cc" if your asm code changes the condition
     121              :  * code register, and also list any temp registers the code uses.
     122              :  *----------
     123              :  */
     124              : 
     125              : 
     126              : #ifdef __i386__         /* 32-bit i386 */
     127              : #define HAS_TEST_AND_SET
     128              : 
     129              : typedef unsigned char slock_t;
     130              : 
     131              : #define TAS(lock) tas(lock)
     132              : 
     133              : static __inline__ int
     134              : tas(volatile slock_t *lock)
     135              : {
     136              :         slock_t         _res = 1;
     137              : 
     138              :         /*
     139              :          * Use a non-locking test before asserting the bus lock.  Note that the
     140              :          * extra test appears to be a small loss on some x86 platforms and a small
     141              :          * win on others; it's by no means clear that we should keep it.
     142              :          *
     143              :          * When this was last tested, we didn't have separate TAS() and TAS_SPIN()
     144              :          * macros.  Nowadays it probably would be better to do a non-locking test
     145              :          * in TAS_SPIN() but not in TAS(), like on x86_64, but no-one's done the
     146              :          * testing to verify that.  Without some empirical evidence, better to
     147              :          * leave it alone.
     148              :          */
     149              :         __asm__ __volatile__(
     150              :                 "  cmpb    $0,%1   \n"
     151              :                 "  jne             1f              \n"
     152              :                 "  lock                    \n"
     153              :                 "  xchgb   %0,%1   \n"
     154              :                 "1: \n"
     155              : :               "+q"(_res), "+m"(*lock)
     156              : :               /* no inputs */
     157              : :               "memory", "cc");
     158              :         return (int) _res;
     159              : }
     160              : 
     161              : #define SPIN_DELAY() spin_delay()
     162              : 
     163              : static __inline__ void
     164              : spin_delay(void)
     165              : {
     166              :         /*
     167              :          * This sequence is equivalent to the PAUSE instruction ("rep" is
     168              :          * ignored by old IA32 processors if the following instruction is
     169              :          * not a string operation); the IA-32 Architecture Software
     170              :          * Developer's Manual, Vol. 3, Section 7.7.2 describes why using
     171              :          * PAUSE in the inner loop of a spin lock is necessary for good
     172              :          * performance:
     173              :          *
     174              :          *     The PAUSE instruction improves the performance of IA-32
     175              :          *     processors supporting Hyper-Threading Technology when
     176              :          *     executing spin-wait loops and other routines where one
     177              :          *     thread is accessing a shared lock or semaphore in a tight
     178              :          *     polling loop. When executing a spin-wait loop, the
     179              :          *     processor can suffer a severe performance penalty when
     180              :          *     exiting the loop because it detects a possible memory order
     181              :          *     violation and flushes the core processor's pipeline. The
     182              :          *     PAUSE instruction provides a hint to the processor that the
     183              :          *     code sequence is a spin-wait loop. The processor uses this
     184              :          *     hint to avoid the memory order violation and prevent the
     185              :          *     pipeline flush. In addition, the PAUSE instruction
     186              :          *     de-pipelines the spin-wait loop to prevent it from
     187              :          *     consuming execution resources excessively.
     188              :          */
     189              :         __asm__ __volatile__(
     190              :                 " rep; nop                 \n");
     191              : }
     192              : 
     193              : #endif   /* __i386__ */
     194              : 
     195              : 
     196              : #ifdef __x86_64__               /* AMD Opteron, Intel EM64T */
     197              : #define HAS_TEST_AND_SET
     198              : 
     199              : typedef unsigned char slock_t;
     200              : 
     201              : #define TAS(lock) tas(lock)
     202              : 
     203              : /*
     204              :  * On Intel EM64T, it's a win to use a non-locking test before the xchg proper,
     205              :  * but only when spinning.
     206              :  *
     207              :  * See also Implementing Scalable Atomic Locks for Multi-Core Intel(tm) EM64T
     208              :  * and IA32, by Michael Chynoweth and Mary R. Lee. As of this writing, it is
     209              :  * available at:
     210              :  * http://software.intel.com/en-us/articles/implementing-scalable-atomic-locks-for-multi-core-intel-em64t-and-ia32-architectures
     211              :  */
     212              : #define TAS_SPIN(lock)    (*(lock) ? 1 : TAS(lock))
     213              : 
     214              : static __inline__ int
     215              : tas(volatile slock_t *lock)
     216              : {
     217              :         slock_t         _res = 1;
     218              : 
     219              :         __asm__ __volatile__(
     220              :                 "  lock                    \n"
     221              :                 "  xchgb   %0,%1   \n"
     222              : :               "+q"(_res), "+m"(*lock)
     223              : :               /* no inputs */
     224              : :               "memory", "cc");
     225              :         return (int) _res;
     226              : }
     227              : 
     228              : #define SPIN_DELAY() spin_delay()
     229              : 
     230              : static __inline__ void
     231              : spin_delay(void)
     232              : {
     233              :         /*
     234              :          * Adding a PAUSE in the spin delay loop is demonstrably a no-op on
     235              :          * Opteron, but it may be of some use on EM64T, so we keep it.
     236              :          */
     237              :         __asm__ __volatile__(
     238              :                 " rep; nop                 \n");
     239              : }
     240              : 
     241              : #endif   /* __x86_64__ */
     242              : 
     243              : 
     244              : /*
     245              :  * On ARM and ARM64, we use __sync_lock_test_and_set(int *, int) if available.
     246              :  *
     247              :  * We use the int-width variant of the builtin because it works on more chips
     248              :  * than other widths.
     249              :  */
     250              : #if defined(__arm__) || defined(__arm) || defined(__aarch64__)
     251              : #ifdef HAVE_GCC__SYNC_INT32_TAS
     252              : #define HAS_TEST_AND_SET
     253              : 
     254              : #define TAS(lock) tas(lock)
     255              : 
     256              : typedef int slock_t;
     257              : 
     258              : static __inline__ int
     259      5572787 : tas(volatile slock_t *lock)
     260              : {
     261      5572787 :         return __sync_lock_test_and_set(lock, 1);
     262              : }
     263              : 
     264              : #define S_UNLOCK(lock) __sync_lock_release(lock)
     265              : 
     266              : #if defined(__aarch64__)
     267              : 
     268              : /*
     269              :  * On ARM64, it's a win to use a non-locking test before the TAS proper.  It
     270              :  * may be a win on 32-bit ARM, too, but nobody's tested it yet.
     271              :  */
     272              : #define TAS_SPIN(lock)  (*(lock) ? 1 : TAS(lock))
     273              : 
     274              : #define SPIN_DELAY() spin_delay()
     275              : 
     276              : static __inline__ void
     277        24410 : spin_delay(void)
     278              : {
     279              :         /*
     280              :          * Using an ISB instruction to delay in spinlock loops appears beneficial
     281              :          * on high-core-count ARM64 processors.  It seems mostly a wash for smaller
     282              :          * gear, and ISB doesn't exist at all on pre-v7 ARM chips.
     283              :          */
     284        24410 :         __asm__ __volatile__(
     285              :                 " isb;                             \n");
     286        24410 : }
     287              : 
     288              : #endif   /* __aarch64__ */
     289              : #endif   /* HAVE_GCC__SYNC_INT32_TAS */
     290              : #endif   /* __arm__ || __arm || __aarch64__ */
     291              : 
     292              : 
     293              : /* S/390 and S/390x Linux (32- and 64-bit zSeries) */
     294              : #if defined(__s390__) || defined(__s390x__)
     295              : #define HAS_TEST_AND_SET
     296              : 
     297              : typedef unsigned int slock_t;
     298              : 
     299              : #define TAS(lock)          tas(lock)
     300              : 
     301              : static __inline__ int
     302              : tas(volatile slock_t *lock)
     303              : {
     304              :         int                     _res = 0;
     305              : 
     306              :         __asm__ __volatile__(
     307              :                 "  cs      %0,%3,0(%2)             \n"
     308              : :               "+d"(_res), "+m"(*lock)
     309              : :               "a"(lock), "d"(1)
     310              : :               "memory", "cc");
     311              :         return _res;
     312              : }
     313              : 
     314              : #endif   /* __s390__ || __s390x__ */
     315              : 
     316              : 
     317              : #if defined(__sparc__)          /* Sparc */
     318              : /*
     319              :  * Solaris has always run sparc processors in TSO (total store) mode, but
     320              :  * linux didn't use to and the *BSDs still don't. So, be careful about
     321              :  * acquire/release semantics. The CPU will treat superfluous members as
     322              :  * NOPs, so it's just code space.
     323              :  */
     324              : #define HAS_TEST_AND_SET
     325              : 
     326              : typedef unsigned char slock_t;
     327              : 
     328              : #define TAS(lock) tas(lock)
     329              : 
     330              : static __inline__ int
     331              : tas(volatile slock_t *lock)
     332              : {
     333              :         slock_t         _res;
     334              : 
     335              :         /*
     336              :          * "cas" would be better than "ldstub", but it is only present on
     337              :          * sparcv8plus and later, while some platforms still support sparcv7 or
     338              :          * sparcv8.  Also, "cas" requires that the system be running in TSO mode.
     339              :          */
     340              :         __asm__ __volatile__(
     341              :                 "  ldstub  [%2], %0        \n"
     342              : :               "=r"(_res), "+m"(*lock)
     343              : :               "r"(lock)
     344              : :               "memory");
     345              : #if defined(__sparcv7) || defined(__sparc_v7__)
     346              :         /*
     347              :          * No stbar or membar available, luckily no actually produced hardware
     348              :          * requires a barrier.
     349              :          */
     350              : #elif defined(__sparcv8) || defined(__sparc_v8__)
     351              :         /* stbar is available (and required for both PSO, RMO), membar isn't */
     352              :         __asm__ __volatile__ ("stbar        \n":::"memory");
     353              : #else
     354              :         /*
     355              :          * #LoadStore (RMO) | #LoadLoad (RMO) together are the appropriate acquire
     356              :          * barrier for sparcv8+ upwards.
     357              :          */
     358              :         __asm__ __volatile__ ("membar #LoadStore | #LoadLoad \n":::"memory");
     359              : #endif
     360              :         return (int) _res;
     361              : }
     362              : 
     363              : #if defined(__sparcv7) || defined(__sparc_v7__)
     364              : /*
     365              :  * No stbar or membar available, luckily no actually produced hardware
     366              :  * requires a barrier.  We fall through to the default gcc definition of
     367              :  * S_UNLOCK in this case.
     368              :  */
     369              : #elif defined(__sparcv8) || defined(__sparc_v8__)
     370              : /* stbar is available (and required for both PSO, RMO), membar isn't */
     371              : #define S_UNLOCK(lock)  \
     372              : do \
     373              : { \
     374              :         __asm__ __volatile__ ("stbar        \n":::"memory"); \
     375              :         *((volatile slock_t *) (lock)) = 0; \
     376              : } while (0)
     377              : #else
     378              : /*
     379              :  * #LoadStore (RMO) | #StoreStore (RMO, PSO) together are the appropriate
     380              :  * release barrier for sparcv8+ upwards.
     381              :  */
     382              : #define S_UNLOCK(lock)  \
     383              : do \
     384              : { \
     385              :         __asm__ __volatile__ ("membar #LoadStore | #StoreStore \n":::"memory"); \
     386              :         *((volatile slock_t *) (lock)) = 0; \
     387              : } while (0)
     388              : #endif
     389              : 
     390              : #endif   /* __sparc__ */
     391              : 
     392              : 
     393              : /* PowerPC */
     394              : #if defined(__ppc__) || defined(__powerpc__) || defined(__ppc64__) || defined(__powerpc64__)
     395              : #define HAS_TEST_AND_SET
     396              : 
     397              : typedef unsigned int slock_t;
     398              : 
     399              : #define TAS(lock) tas(lock)
     400              : 
     401              : /* On PPC, it's a win to use a non-locking test before the lwarx */
     402              : #define TAS_SPIN(lock)  (*(lock) ? 1 : TAS(lock))
     403              : 
     404              : /*
     405              :  * The second operand of addi can hold a constant zero or a register number,
     406              :  * hence constraint "=&b" to avoid allocating r0.  "b" stands for "address
     407              :  * base register"; most operands having this register-or-zero property are
     408              :  * address bases, e.g. the second operand of lwax.
     409              :  *
     410              :  * NOTE: per the Enhanced PowerPC Architecture manual, v1.0 dated 7-May-2002,
     411              :  * an isync is a sufficient synchronization barrier after a lwarx/stwcx loop.
     412              :  * But if the spinlock is in ordinary memory, we can use lwsync instead for
     413              :  * better performance.
     414              :  */
     415              : static __inline__ int
     416              : tas(volatile slock_t *lock)
     417              : {
     418              :         slock_t _t;
     419              :         int _res;
     420              : 
     421              :         __asm__ __volatile__(
     422              : "  lwarx   %0,0,%3,1       \n"
     423              : "  cmpwi   %0,0            \n"
     424              : "  bne     1f                      \n"
     425              : "  addi    %0,%0,1         \n"
     426              : "  stwcx.  %0,0,%3         \n"
     427              : "  beq     2f                      \n"
     428              : "1: \n"
     429              : "  li      %1,1            \n"
     430              : "  b       3f                      \n"
     431              : "2: \n"
     432              : "  lwsync                          \n"
     433              : "  li      %1,0            \n"
     434              : "3: \n"
     435              : :       "=&b"(_t), "=r"(_res), "+m"(*lock)
     436              : :       "r"(lock)
     437              : :       "memory", "cc");
     438              :         return _res;
     439              : }
     440              : 
     441              : /*
     442              :  * PowerPC S_UNLOCK is almost standard but requires a "sync" instruction.
     443              :  * But we can use lwsync instead for better performance.
     444              :  */
     445              : #define S_UNLOCK(lock)  \
     446              : do \
     447              : { \
     448              :         __asm__ __volatile__ ("    lwsync \n" ::: "memory"); \
     449              :         *((volatile slock_t *) (lock)) = 0; \
     450              : } while (0)
     451              : 
     452              : #endif /* powerpc */
     453              : 
     454              : 
     455              : #if defined(__mips__) && !defined(__sgi)        /* non-SGI MIPS */
     456              : #define HAS_TEST_AND_SET
     457              : 
     458              : typedef unsigned int slock_t;
     459              : 
     460              : #define TAS(lock) tas(lock)
     461              : 
     462              : /*
     463              :  * Original MIPS-I processors lacked the LL/SC instructions, but if we are
     464              :  * so unfortunate as to be running on one of those, we expect that the kernel
     465              :  * will handle the illegal-instruction traps and emulate them for us.  On
     466              :  * anything newer (and really, MIPS-I is extinct) LL/SC is the only sane
     467              :  * choice because any other synchronization method must involve a kernel
     468              :  * call.  Unfortunately, many toolchains still default to MIPS-I as the
     469              :  * codegen target; if the symbol __mips shows that that's the case, we
     470              :  * have to force the assembler to accept LL/SC.
     471              :  *
     472              :  * R10000 and up processors require a separate SYNC, which has the same
     473              :  * issues as LL/SC.
     474              :  */
     475              : #if __mips < 2
     476              : #define MIPS_SET_MIPS2  "       .set mips2          \n"
     477              : #else
     478              : #define MIPS_SET_MIPS2
     479              : #endif
     480              : 
     481              : static __inline__ int
     482              : tas(volatile slock_t *lock)
     483              : {
     484              :         volatile slock_t *_l = lock;
     485              :         int                     _res;
     486              :         int                     _tmp;
     487              : 
     488              :         __asm__ __volatile__(
     489              :                 "       .set push           \n"
     490              :                 MIPS_SET_MIPS2
     491              :                 "       .set noreorder      \n"
     492              :                 "       .set nomacro        \n"
     493              :                 "       ll      %0, %2      \n"
     494              :                 "       or      %1, %0, 1   \n"
     495              :                 "       sc      %1, %2      \n"
     496              :                 "       xori    %1, 1       \n"
     497              :                 "       or      %0, %0, %1  \n"
     498              :                 "       sync                \n"
     499              :                 "       .set pop              "
     500              : :               "=&r" (_res), "=&r" (_tmp), "+R" (*_l)
     501              : :               /* no inputs */
     502              : :               "memory");
     503              :         return _res;
     504              : }
     505              : 
     506              : /* MIPS S_UNLOCK is almost standard but requires a "sync" instruction */
     507              : #define S_UNLOCK(lock)  \
     508              : do \
     509              : { \
     510              :         __asm__ __volatile__( \
     511              :                 "       .set push           \n" \
     512              :                 MIPS_SET_MIPS2 \
     513              :                 "       .set noreorder      \n" \
     514              :                 "       .set nomacro        \n" \
     515              :                 "       sync                \n" \
     516              :                 "       .set pop              " \
     517              : :               /* no outputs */ \
     518              : :               /* no inputs */ \
     519              : :               "memory"); \
     520              :         *((volatile slock_t *) (lock)) = 0; \
     521              : } while (0)
     522              : 
     523              : #endif /* __mips__ && !__sgi */
     524              : 
     525              : 
     526              : 
     527              : /*
     528              :  * If we have no platform-specific knowledge, but we found that the compiler
     529              :  * provides __sync_lock_test_and_set(), use that.  Prefer the int-width
     530              :  * version over the char-width version if we have both, on the rather dubious
     531              :  * grounds that that's known to be more likely to work in the ARM ecosystem.
     532              :  * (But we dealt with ARM above.)
     533              :  */
     534              : #if !defined(HAS_TEST_AND_SET)
     535              : 
     536              : #if defined(HAVE_GCC__SYNC_INT32_TAS)
     537              : #define HAS_TEST_AND_SET
     538              : 
     539              : #define TAS(lock) tas(lock)
     540              : 
     541              : typedef int slock_t;
     542              : 
     543              : static __inline__ int
     544              : tas(volatile slock_t *lock)
     545              : {
     546              :         return __sync_lock_test_and_set(lock, 1);
     547              : }
     548              : 
     549              : #define S_UNLOCK(lock) __sync_lock_release(lock)
     550              : 
     551              : #elif defined(HAVE_GCC__SYNC_CHAR_TAS)
     552              : #define HAS_TEST_AND_SET
     553              : 
     554              : #define TAS(lock) tas(lock)
     555              : 
     556              : typedef char slock_t;
     557              : 
     558              : static __inline__ int
     559              : tas(volatile slock_t *lock)
     560              : {
     561              :         return __sync_lock_test_and_set(lock, 1);
     562              : }
     563              : 
     564              : #define S_UNLOCK(lock) __sync_lock_release(lock)
     565              : 
     566              : #endif   /* HAVE_GCC__SYNC_INT32_TAS */
     567              : 
     568              : #endif  /* !defined(HAS_TEST_AND_SET) */
     569              : 
     570              : 
     571              : /*
     572              :  * Default implementation of S_UNLOCK() for gcc/icc.
     573              :  *
     574              :  * Note that this implementation is unsafe for any platform that can reorder
     575              :  * a memory access (either load or store) after a following store.  That
     576              :  * happens not to be possible on x86 and most legacy architectures (some are
     577              :  * single-processor!), but many modern systems have weaker memory ordering.
     578              :  * Those that do must define their own version of S_UNLOCK() rather than
     579              :  * relying on this one.
     580              :  */
     581              : #if !defined(S_UNLOCK)
     582              : #define S_UNLOCK(lock)  \
     583              :         do { __asm__ __volatile__("" : : : "memory");  *(lock) = 0; } while (0)
     584              : #endif
     585              : 
     586              : #endif  /* defined(__GNUC__) || defined(__INTEL_COMPILER) */
     587              : 
     588              : 
     589              : /*
     590              :  * ---------------------------------------------------------------------
     591              :  * Platforms that use non-gcc inline assembly:
     592              :  * ---------------------------------------------------------------------
     593              :  */
     594              : 
     595              : #if !defined(HAS_TEST_AND_SET)  /* We didn't trigger above, let's try here */
     596              : 
     597              : #ifdef _MSC_VER
     598              : typedef LONG slock_t;
     599              : 
     600              : #define HAS_TEST_AND_SET
     601              : #define TAS(lock) (InterlockedCompareExchange(lock, 1, 0))
     602              : 
     603              : #define SPIN_DELAY() spin_delay()
     604              : 
     605              : #ifdef _M_ARM64
     606              : static __forceinline void
     607              : spin_delay(void)
     608              : {
     609              :         /*
     610              :          * Research indicates ISB is better than __yield() on AArch64.  See
     611              :          * https://postgr.es/m/1c2a29b8-5b1e-44f7-a871-71ec5fefc120%40app.fastmail.com.
     612              :          */
     613              :         __isb(_ARM64_BARRIER_SY);
     614              : }
     615              : #elif defined(_WIN64)
     616              : static __forceinline void
     617              : spin_delay(void)
     618              : {
     619              :         /*
     620              :          * If using Visual C++ on Win64, inline assembly is unavailable.
     621              :          * Use a _mm_pause intrinsic instead of rep nop.
     622              :          */
     623              :         _mm_pause();
     624              : }
     625              : #else
     626              : static __forceinline void
     627              : spin_delay(void)
     628              : {
     629              :         /* See comment for gcc code. Same code, MASM syntax */
     630              :         __asm rep nop;
     631              : }
     632              : #endif
     633              : 
     634              : #include <intrin.h>
     635              : 
     636              : #ifdef _M_ARM64
     637              : 
     638              : /* _ReadWriteBarrier() is insufficient on non-TSO architectures. */
     639              : #pragma intrinsic(_InterlockedExchange)
     640              : #define S_UNLOCK(lock) _InterlockedExchange(lock, 0)
     641              : 
     642              : #else
     643              : 
     644              : #pragma intrinsic(_ReadWriteBarrier)
     645              : #define S_UNLOCK(lock)  \
     646              :         do { _ReadWriteBarrier(); (*(lock)) = 0; } while (0)
     647              : 
     648              : #endif
     649              : #endif
     650              : 
     651              : 
     652              : #endif  /* !defined(HAS_TEST_AND_SET) */
     653              : 
     654              : 
     655              : /* Blow up if we didn't have any way to do spinlocks */
     656              : #ifndef HAS_TEST_AND_SET
     657              : #error PostgreSQL does not have spinlock support on this platform.  Please report this to pgsql-bugs@lists.postgresql.org.
     658              : #endif
     659              : 
     660              : 
     661              : /*
     662              :  * Default Definitions - override these above as needed.
     663              :  */
     664              : 
     665              : #if !defined(S_LOCK)
     666              : #define S_LOCK(lock) \
     667              :         (TAS(lock) ? s_lock((lock), __FILE__, __LINE__, __func__) : 0)
     668              : #endif   /* S_LOCK */
     669              : 
     670              : #if !defined(S_LOCK_FREE)
     671              : #define S_LOCK_FREE(lock)       (*(lock) == 0)
     672              : #endif   /* S_LOCK_FREE */
     673              : 
     674              : #if !defined(S_UNLOCK)
     675              : /*
     676              :  * Our default implementation of S_UNLOCK is essentially *(lock) = 0.  This
     677              :  * is unsafe if the platform can reorder a memory access (either load or
     678              :  * store) after a following store; platforms where this is possible must
     679              :  * define their own S_UNLOCK.  But CPU reordering is not the only concern:
     680              :  * if we simply defined S_UNLOCK() as an inline macro, the compiler might
     681              :  * reorder instructions from inside the critical section to occur after the
     682              :  * lock release.  Since the compiler probably can't know what the external
     683              :  * function s_unlock is doing, putting the same logic there should be adequate.
     684              :  * A sufficiently-smart globally optimizing compiler could break that
     685              :  * assumption, though, and the cost of a function call for every spinlock
     686              :  * release may hurt performance significantly, so we use this implementation
     687              :  * only for platforms where we don't know of a suitable intrinsic.  For the
     688              :  * most part, those are relatively obscure platform/compiler combinations to
     689              :  * which the PostgreSQL project does not have access.
     690              :  */
     691              : #define USE_DEFAULT_S_UNLOCK
     692              : extern void s_unlock(volatile slock_t *lock);
     693              : #define S_UNLOCK(lock)          s_unlock(lock)
     694              : #endif   /* S_UNLOCK */
     695              : 
     696              : #if !defined(S_INIT_LOCK)
     697              : #define S_INIT_LOCK(lock)       S_UNLOCK(lock)
     698              : #endif   /* S_INIT_LOCK */
     699              : 
     700              : #if !defined(SPIN_DELAY)
     701              : #define SPIN_DELAY()    ((void) 0)
     702              : #endif   /* SPIN_DELAY */
     703              : 
     704              : #if !defined(TAS)
     705              : extern int      tas(volatile slock_t *lock);            /* in port/.../tas.s, or
     706              :                                                                                                  * s_lock.c */
     707              : 
     708              : #define TAS(lock)               tas(lock)
     709              : #endif   /* TAS */
     710              : 
     711              : #if !defined(TAS_SPIN)
     712              : #define TAS_SPIN(lock)  TAS(lock)
     713              : #endif   /* TAS_SPIN */
     714              : 
     715              : 
     716              : /*
     717              :  * Platform-independent out-of-line support routines
     718              :  */
     719              : extern int s_lock(volatile slock_t *lock, const char *file, int line, const char *func);
     720              : 
     721              : /* Support for dynamic adjustment of spins_per_delay */
     722              : #define DEFAULT_SPINS_PER_DELAY  100
     723              : 
     724              : extern void set_spins_per_delay(int shared_spins_per_delay);
     725              : extern int      update_spins_per_delay(int shared_spins_per_delay);
     726              : 
     727              : /*
     728              :  * Support for spin delay which is useful in various places where
     729              :  * spinlock-like procedures take place.
     730              :  */
     731              : typedef struct
     732              : {
     733              :         int                     spins;
     734              :         int                     delays;
     735              :         int                     cur_delay;
     736              :         const char *file;
     737              :         int                     line;
     738              :         const char *func;
     739              : } SpinDelayStatus;
     740              : 
     741              : static inline void
     742         1017 : init_spin_delay(SpinDelayStatus *status,
     743              :                                 const char *file, int line, const char *func)
     744              : {
     745         1017 :         status->spins = 0;
     746         1017 :         status->delays = 0;
     747         1017 :         status->cur_delay = 0;
     748         1017 :         status->file = file;
     749         1017 :         status->line = line;
     750         1017 :         status->func = func;
     751         1017 : }
     752              : 
     753              : #define init_local_spin_delay(status) init_spin_delay(status, __FILE__, __LINE__, __func__)
     754              : extern void perform_spin_delay(SpinDelayStatus *status);
     755              : extern void finish_spin_delay(SpinDelayStatus *status);
     756              : 
     757              : #endif   /* S_LOCK_H */
        

Generated by: LCOV version 2.3.2-1