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

Generated by: LCOV version 1.16