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

Generated by: LCOV version 2.0-1