Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
s_lock.h868 linesDownload Raw Back to storage
1/*-------------------------------------------------------------------------2 *3 * s_lock.h4 *	   Hardware-dependent implementation of spinlocks.5 *6 *	NOTE: none of the macros in this file are intended to be called directly.7 *	Call them through the hardware-independent macros in spin.h.8 *9 *	The following hardware-dependent macros must be provided for each10 *	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 a18 *		"reasonable" amount of time --- typically ~ 1 minute.19 *		Should return number of "delays"; see s_lock.c20 *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 these32 *	macros at the bottom of the file.  Check if your platform can use33 *	these or needs to override them.34 *35 *  Usually, S_LOCK() is implemented in terms of even lower-level macros36 *	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 unable41 *		to acquire the lock.42 *43 *	int TAS_SPIN(slock_t *lock)44 *		Like TAS(), but this version is used when waiting for a lock45 *		previously found to be contended.  By default, this is the46 *		same as TAS(), but on some architectures it's better to poll a47 *		contended lock using an unlocked instruction and retry the48 *		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 called51 *	directly.52 *53 *	CAUTION: on some platforms TAS() and/or TAS_SPIN() may sometimes report54 *	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 must56 *	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 compiler59 *	does not re-order accesses to shared memory to precede the actual lock60 *	acquisition, or follow the lock release.  Prior to PostgreSQL 9.5, this61 *	was the caller's responsibility, which meant that callers had to use62 *	volatile-qualified pointers to refer to both the spinlock itself and the63 *	shared data being accessed within the spinlocked critical section.  This64 *	was notationally awkward, easy to forget (and thus error-prone), and65 *	prevented some useful compiler optimizations.  For these reasons, we66 *	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(), and70 *	S_UNLOCK() macros must further include hardware-level memory fence71 *	instructions to prevent similar re-ordering at the hardware level.72 *	TAS() and TAS_SPIN() must guarantee that loads and stores issued after73 *	the macro are not executed until the lock has been obtained.  Conversely,74 *	S_UNLOCK() must guarantee that loads and stores issued before the macro75 *	have been executed before the lock is released.76 *77 *	On most supported platforms, TAS() uses a tas() function written78 *	in assembly language to execute a hardware atomic-test-and-set79 *	instruction.  Equivalent OS-supplied mutex routines could be used too.80 *81 *	If no system-specific TAS() is available (ie, HAVE_SPINLOCKS is not82 *	defined), then we fall back on an emulation that uses SysV semaphores83 *	(see spin.c).  This emulation will be MUCH MUCH slower than a proper TAS()84 *	implementation, because of the cost of a kernel call per lock or unlock.85 *	An old report is that Postgres spends around 40% of its time in semop(2)86 *	when using the SysV semaphore code.87 *88 *89 * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group90 * Portions Copyright (c) 1994, Regents of the University of California91 *92 *	  src/include/storage/s_lock.h93 *94 *-------------------------------------------------------------------------95 */96#ifndef S_LOCK_H97#define S_LOCK_H98 99#ifdef FRONTEND100#error "s_lock.h may not be included from frontend code"101#endif102 103#ifdef HAVE_SPINLOCKS	/* skip spinlocks if requested */104 105#if defined(__GNUC__) || defined(__INTEL_COMPILER)106/*************************************************************************107 * All the gcc inlines108 * Gcc consistently defines the CPU as __cpu__.109 * Other compilers use __cpu or __cpu__ so we test for both in those cases.110 */111 112/*----------113 * Standard gcc asm format (assuming "volatile slock_t *lock"):114 115	__asm__ __volatile__(116		"	instruction	\n"117		"	instruction	\n"118		"	instruction	\n"119:		"=r"(_res), "+m"(*lock)		// return register, in/out lock value120:		"r"(lock)					// lock pointer, in input register121:		"memory", "cc");			// show clobbered registers here122 123 * The output-operands list (after first colon) should always include124 * "+m"(*lock), whether or not the asm code actually refers to this125 * operand directly.  This ensures that gcc believes the value in the126 * lock variable is used and set by the asm code.  Also, the clobbers127 * list (after third colon) should always include "memory"; this prevents128 * gcc from thinking it can cache the values of shared-memory fields129 * across the asm code.  Add "cc" if your asm code changes the condition130 * code register, and also list any temp registers the code uses.131 *----------132 */133 134 135#ifdef __i386__		/* 32-bit i386 */136#define HAS_TEST_AND_SET137 138typedef unsigned char slock_t;139 140#define TAS(lock) tas(lock)141 142static __inline__ int143tas(volatile slock_t *lock)144{145	slock_t		_res = 1;146 147	/*148	 * Use a non-locking test before asserting the bus lock.  Note that the149	 * extra test appears to be a small loss on some x86 platforms and a small150	 * win on others; it's by no means clear that we should keep it.151	 *152	 * When this was last tested, we didn't have separate TAS() and TAS_SPIN()153	 * macros.  Nowadays it probably would be better to do a non-locking test154	 * in TAS_SPIN() but not in TAS(), like on x86_64, but no-one's done the155	 * testing to verify that.  Without some empirical evidence, better to156	 * leave it alone.157	 */158	__asm__ __volatile__(159		"	cmpb	$0,%1	\n"160		"	jne		1f		\n"161		"	lock			\n"162		"	xchgb	%0,%1	\n"163		"1: \n"164:		"+q"(_res), "+m"(*lock)165:		/* no inputs */166:		"memory", "cc");167	return (int) _res;168}169 170#define SPIN_DELAY() spin_delay()171 172static __inline__ void173spin_delay(void)174{175	/*176	 * This sequence is equivalent to the PAUSE instruction ("rep" is177	 * ignored by old IA32 processors if the following instruction is178	 * not a string operation); the IA-32 Architecture Software179	 * Developer's Manual, Vol. 3, Section 7.7.2 describes why using180	 * PAUSE in the inner loop of a spin lock is necessary for good181	 * performance:182	 *183	 *     The PAUSE instruction improves the performance of IA-32184	 *     processors supporting Hyper-Threading Technology when185	 *     executing spin-wait loops and other routines where one186	 *     thread is accessing a shared lock or semaphore in a tight187	 *     polling loop. When executing a spin-wait loop, the188	 *     processor can suffer a severe performance penalty when189	 *     exiting the loop because it detects a possible memory order190	 *     violation and flushes the core processor's pipeline. The191	 *     PAUSE instruction provides a hint to the processor that the192	 *     code sequence is a spin-wait loop. The processor uses this193	 *     hint to avoid the memory order violation and prevent the194	 *     pipeline flush. In addition, the PAUSE instruction195	 *     de-pipelines the spin-wait loop to prevent it from196	 *     consuming execution resources excessively.197	 */198	__asm__ __volatile__(199		" rep; nop			\n");200}201 202#endif	 /* __i386__ */203 204 205#ifdef __x86_64__		/* AMD Opteron, Intel EM64T */206#define HAS_TEST_AND_SET207 208typedef unsigned char slock_t;209 210#define TAS(lock) tas(lock)211 212/*213 * On Intel EM64T, it's a win to use a non-locking test before the xchg proper,214 * but only when spinning.215 *216 * See also Implementing Scalable Atomic Locks for Multi-Core Intel(tm) EM64T217 * and IA32, by Michael Chynoweth and Mary R. Lee. As of this writing, it is218 * available at:219 * http://software.intel.com/en-us/articles/implementing-scalable-atomic-locks-for-multi-core-intel-em64t-and-ia32-architectures220 */221#define TAS_SPIN(lock)    (*(lock) ? 1 : TAS(lock))222 223static __inline__ int224tas(volatile slock_t *lock)225{226	slock_t		_res = 1;227 228	__asm__ __volatile__(229		"	lock			\n"230		"	xchgb	%0,%1	\n"231:		"+q"(_res), "+m"(*lock)232:		/* no inputs */233:		"memory", "cc");234	return (int) _res;235}236 237#define SPIN_DELAY() spin_delay()238 239static __inline__ void240spin_delay(void)241{242	/*243	 * Adding a PAUSE in the spin delay loop is demonstrably a no-op on244	 * Opteron, but it may be of some use on EM64T, so we keep it.245	 */246	__asm__ __volatile__(247		" rep; nop			\n");248}249 250#endif	 /* __x86_64__ */251 252 253/*254 * On ARM and ARM64, we use __sync_lock_test_and_set(int *, int) if available.255 *256 * We use the int-width variant of the builtin because it works on more chips257 * than other widths.258 */259#if defined(__arm__) || defined(__arm) || defined(__aarch64__)260#ifdef HAVE_GCC__SYNC_INT32_TAS261#define HAS_TEST_AND_SET262 263#define TAS(lock) tas(lock)264 265typedef int slock_t;266 267static __inline__ int268tas(volatile slock_t *lock)269{270	return __sync_lock_test_and_set(lock, 1);271}272 273#define S_UNLOCK(lock) __sync_lock_release(lock)274 275/*276 * Using an ISB instruction to delay in spinlock loops appears beneficial on277 * high-core-count ARM64 processors.  It seems mostly a wash for smaller gear,278 * and ISB doesn't exist at all on pre-v7 ARM chips.279 */280#if defined(__aarch64__)281 282#define SPIN_DELAY() spin_delay()283 284static __inline__ void285spin_delay(void)286{287	__asm__ __volatile__(288		" isb;				\n");289}290 291#endif	 /* __aarch64__ */292#endif	 /* HAVE_GCC__SYNC_INT32_TAS */293#endif	 /* __arm__ || __arm || __aarch64__ */294 295 296/* S/390 and S/390x Linux (32- and 64-bit zSeries) */297#if defined(__s390__) || defined(__s390x__)298#define HAS_TEST_AND_SET299 300typedef unsigned int slock_t;301 302#define TAS(lock)	   tas(lock)303 304static __inline__ int305tas(volatile slock_t *lock)306{307	int			_res = 0;308 309	__asm__	__volatile__(310		"	cs 	%0,%3,0(%2)		\n"311:		"+d"(_res), "+m"(*lock)312:		"a"(lock), "d"(1)313:		"memory", "cc");314	return _res;315}316 317#endif	 /* __s390__ || __s390x__ */318 319 320#if defined(__sparc__)		/* Sparc */321/*322 * Solaris has always run sparc processors in TSO (total store) mode, but323 * linux didn't use to and the *BSDs still don't. So, be careful about324 * acquire/release semantics. The CPU will treat superfluous members as325 * NOPs, so it's just code space.326 */327#define HAS_TEST_AND_SET328 329typedef unsigned char slock_t;330 331#define TAS(lock) tas(lock)332 333static __inline__ int334tas(volatile slock_t *lock)335{336	slock_t		_res;337 338	/*339	 *	See comment in src/backend/port/tas/sunstudio_sparc.s for why this340	 *	uses "ldstub", and that file uses "cas".  gcc currently generates341	 *	sparcv7-targeted binaries, so "cas" use isn't possible.342	 */343	__asm__ __volatile__(344		"	ldstub	[%2], %0	\n"345:		"=r"(_res), "+m"(*lock)346:		"r"(lock)347:		"memory");348#if defined(__sparcv7) || defined(__sparc_v7__)349	/*350	 * No stbar or membar available, luckily no actually produced hardware351	 * requires a barrier.352	 */353#elif defined(__sparcv8) || defined(__sparc_v8__)354	/* stbar is available (and required for both PSO, RMO), membar isn't */355	__asm__ __volatile__ ("stbar	 \n":::"memory");356#else357	/*358	 * #LoadStore (RMO) | #LoadLoad (RMO) together are the appropriate acquire359	 * barrier for sparcv8+ upwards.360	 */361	__asm__ __volatile__ ("membar #LoadStore | #LoadLoad \n":::"memory");362#endif363	return (int) _res;364}365 366#if defined(__sparcv7) || defined(__sparc_v7__)367/*368 * No stbar or membar available, luckily no actually produced hardware369 * requires a barrier.  We fall through to the default gcc definition of370 * S_UNLOCK in this case.371 */372#elif defined(__sparcv8) || defined(__sparc_v8__)373/* stbar is available (and required for both PSO, RMO), membar isn't */374#define S_UNLOCK(lock)	\375do \376{ \377	__asm__ __volatile__ ("stbar	 \n":::"memory"); \378	*((volatile slock_t *) (lock)) = 0; \379} while (0)380#else381/*382 * #LoadStore (RMO) | #StoreStore (RMO, PSO) together are the appropriate383 * release barrier for sparcv8+ upwards.384 */385#define S_UNLOCK(lock)	\386do \387{ \388	__asm__ __volatile__ ("membar #LoadStore | #StoreStore \n":::"memory"); \389	*((volatile slock_t *) (lock)) = 0; \390} while (0)391#endif392 393#endif	 /* __sparc__ */394 395 396/* PowerPC */397#if defined(__ppc__) || defined(__powerpc__) || defined(__ppc64__) || defined(__powerpc64__)398#define HAS_TEST_AND_SET399 400typedef unsigned int slock_t;401 402#define TAS(lock) tas(lock)403 404/* On PPC, it's a win to use a non-locking test before the lwarx */405#define TAS_SPIN(lock)	(*(lock) ? 1 : TAS(lock))406 407/*408 * The second operand of addi can hold a constant zero or a register number,409 * hence constraint "=&b" to avoid allocating r0.  "b" stands for "address410 * base register"; most operands having this register-or-zero property are411 * address bases, e.g. the second operand of lwax.412 *413 * NOTE: per the Enhanced PowerPC Architecture manual, v1.0 dated 7-May-2002,414 * an isync is a sufficient synchronization barrier after a lwarx/stwcx loop.415 * But if the spinlock is in ordinary memory, we can use lwsync instead for416 * better performance.417 *418 * Ordinarily, we'd code the branches here using GNU-style local symbols, that419 * is "1f" referencing "1:" and so on.  But some people run gcc on AIX with420 * IBM's assembler as backend, and IBM's assembler doesn't do local symbols.421 * So hand-code the branch offsets; fortunately, all PPC instructions are422 * exactly 4 bytes each, so it's not too hard to count.423 */424static __inline__ int425tas(volatile slock_t *lock)426{427	slock_t _t;428	int _res;429 430	__asm__ __volatile__(431"	lwarx   %0,0,%3,1	\n"432"	cmpwi   %0,0		\n"433"	bne     $+16		\n"		/* branch to li %1,1 */434"	addi    %0,%0,1		\n"435"	stwcx.  %0,0,%3		\n"436"	beq     $+12		\n"		/* branch to lwsync */437"	li      %1,1		\n"438"	b       $+12		\n"		/* branch to end of asm sequence */439"	lwsync				\n"440"	li      %1,0		\n"441 442:	"=&b"(_t), "=r"(_res), "+m"(*lock)443:	"r"(lock)444:	"memory", "cc");445	return _res;446}447 448/*449 * PowerPC S_UNLOCK is almost standard but requires a "sync" instruction.450 * But we can use lwsync instead for better performance.451 */452#define S_UNLOCK(lock)	\453do \454{ \455	__asm__ __volatile__ ("	lwsync \n" ::: "memory"); \456	*((volatile slock_t *) (lock)) = 0; \457} while (0)458 459#endif /* powerpc */460 461 462#if defined(__mips__) && !defined(__sgi)	/* non-SGI MIPS */463#define HAS_TEST_AND_SET464 465typedef unsigned int slock_t;466 467#define TAS(lock) tas(lock)468 469/*470 * Original MIPS-I processors lacked the LL/SC instructions, but if we are471 * so unfortunate as to be running on one of those, we expect that the kernel472 * will handle the illegal-instruction traps and emulate them for us.  On473 * anything newer (and really, MIPS-I is extinct) LL/SC is the only sane474 * choice because any other synchronization method must involve a kernel475 * call.  Unfortunately, many toolchains still default to MIPS-I as the476 * codegen target; if the symbol __mips shows that that's the case, we477 * have to force the assembler to accept LL/SC.478 *479 * R10000 and up processors require a separate SYNC, which has the same480 * issues as LL/SC.481 */482#if __mips < 2483#define MIPS_SET_MIPS2	"       .set mips2          \n"484#else485#define MIPS_SET_MIPS2486#endif487 488static __inline__ int489tas(volatile slock_t *lock)490{491	volatile slock_t *_l = lock;492	int			_res;493	int			_tmp;494 495	__asm__ __volatile__(496		"       .set push           \n"497		MIPS_SET_MIPS2498		"       .set noreorder      \n"499		"       .set nomacro        \n"500		"       ll      %0, %2      \n"501		"       or      %1, %0, 1   \n"502		"       sc      %1, %2      \n"503		"       xori    %1, 1       \n"504		"       or      %0, %0, %1  \n"505		"       sync                \n"506		"       .set pop              "507:		"=&r" (_res), "=&r" (_tmp), "+R" (*_l)508:		/* no inputs */509:		"memory");510	return _res;511}512 513/* MIPS S_UNLOCK is almost standard but requires a "sync" instruction */514#define S_UNLOCK(lock)	\515do \516{ \517	__asm__ __volatile__( \518		"       .set push           \n" \519		MIPS_SET_MIPS2 \520		"       .set noreorder      \n" \521		"       .set nomacro        \n" \522		"       sync                \n" \523		"       .set pop              " \524:		/* no outputs */ \525:		/* no inputs */	\526:		"memory"); \527	*((volatile slock_t *) (lock)) = 0; \528} while (0)529 530#endif /* __mips__ && !__sgi */531 532 533#if defined(__hppa) || defined(__hppa__)	/* HP PA-RISC */534/*535 * HP's PA-RISC536 *537 * Because LDCWX requires a 16-byte-aligned address, we declare slock_t as a538 * 16-byte struct.  The active word in the struct is whichever has the aligned539 * address; the other three words just sit at -1.540 */541#define HAS_TEST_AND_SET542 543typedef struct544{545	int			sema[4];546} slock_t;547 548#define TAS_ACTIVE_WORD(lock)	((volatile int *) (((uintptr_t) (lock) + 15) & ~15))549 550static __inline__ int551tas(volatile slock_t *lock)552{553	volatile int *lockword = TAS_ACTIVE_WORD(lock);554	int			lockval;555 556	/*557	 * The LDCWX instruction atomically clears the target word and558	 * returns the previous value.  Hence, if the instruction returns559	 * 0, someone else has already acquired the lock before we tested560	 * it (i.e., we have failed).561	 *562	 * Notice that this means that we actually clear the word to set563	 * the lock and set the word to clear the lock.  This is the564	 * opposite behavior from the SPARC LDSTUB instruction.  For some565	 * reason everything that H-P does is rather baroque...566	 *567	 * For details about the LDCWX instruction, see the "Precision568	 * Architecture and Instruction Reference Manual" (09740-90014 of June569	 * 1987), p. 5-38.570	 */571	__asm__ __volatile__(572		"	ldcwx	0(0,%2),%0	\n"573:		"=r"(lockval), "+m"(*lockword)574:		"r"(lockword)575:		"memory");576	return (lockval == 0);577}578 579#define S_UNLOCK(lock)	\580	do { \581		__asm__ __volatile__("" : : : "memory"); \582		*TAS_ACTIVE_WORD(lock) = -1; \583	} while (0)584 585#define S_INIT_LOCK(lock) \586	do { \587		volatile slock_t *lock_ = (lock); \588		lock_->sema[0] = -1; \589		lock_->sema[1] = -1; \590		lock_->sema[2] = -1; \591		lock_->sema[3] = -1; \592	} while (0)593 594#define S_LOCK_FREE(lock)	(*TAS_ACTIVE_WORD(lock) != 0)595 596#endif	 /* __hppa || __hppa__ */597 598 599/*600 * If we have no platform-specific knowledge, but we found that the compiler601 * provides __sync_lock_test_and_set(), use that.  Prefer the int-width602 * version over the char-width version if we have both, on the rather dubious603 * grounds that that's known to be more likely to work in the ARM ecosystem.604 * (But we dealt with ARM above.)605 */606#if !defined(HAS_TEST_AND_SET)607 608#if defined(HAVE_GCC__SYNC_INT32_TAS)609#define HAS_TEST_AND_SET610 611#define TAS(lock) tas(lock)612 613typedef int slock_t;614 615static __inline__ int616tas(volatile slock_t *lock)617{618	return __sync_lock_test_and_set(lock, 1);619}620 621#define S_UNLOCK(lock) __sync_lock_release(lock)622 623#elif defined(HAVE_GCC__SYNC_CHAR_TAS)624#define HAS_TEST_AND_SET625 626#define TAS(lock) tas(lock)627 628typedef char slock_t;629 630static __inline__ int631tas(volatile slock_t *lock)632{633	return __sync_lock_test_and_set(lock, 1);634}635 636#define S_UNLOCK(lock) __sync_lock_release(lock)637 638#endif	 /* HAVE_GCC__SYNC_INT32_TAS */639 640#endif	/* !defined(HAS_TEST_AND_SET) */641 642 643/*644 * Default implementation of S_UNLOCK() for gcc/icc.645 *646 * Note that this implementation is unsafe for any platform that can reorder647 * a memory access (either load or store) after a following store.  That648 * happens not to be possible on x86 and most legacy architectures (some are649 * single-processor!), but many modern systems have weaker memory ordering.650 * Those that do must define their own version of S_UNLOCK() rather than651 * relying on this one.652 */653#if !defined(S_UNLOCK)654#define S_UNLOCK(lock)	\655	do { __asm__ __volatile__("" : : : "memory");  *(lock) = 0; } while (0)656#endif657 658#endif	/* defined(__GNUC__) || defined(__INTEL_COMPILER) */659 660 661/*662 * ---------------------------------------------------------------------663 * Platforms that use non-gcc inline assembly:664 * ---------------------------------------------------------------------665 */666 667#if !defined(HAS_TEST_AND_SET)	/* We didn't trigger above, let's try here */668 669#if defined(_AIX)	/* AIX */670/*671 * AIX (POWER)672 */673#define HAS_TEST_AND_SET674 675#include <sys/atomic_op.h>676 677typedef int slock_t;678 679#define TAS(lock)			_check_lock((slock_t *) (lock), 0, 1)680#define S_UNLOCK(lock)		_clear_lock((slock_t *) (lock), 0)681#endif	 /* _AIX */682 683 684/* These are in sunstudio_(sparc|x86).s */685 686#if defined(__SUNPRO_C) && (defined(__i386) || defined(__x86_64__) || defined(__sparc__) || defined(__sparc))687#define HAS_TEST_AND_SET688 689#if defined(__i386) || defined(__x86_64__) || defined(__sparcv9) || defined(__sparcv8plus)690typedef unsigned int slock_t;691#else692typedef unsigned char slock_t;693#endif694 695extern slock_t pg_atomic_cas(volatile slock_t *lock, slock_t with,696									  slock_t cmp);697 698#define TAS(a) (pg_atomic_cas((a), 1, 0) != 0)699#endif700 701 702#ifdef _MSC_VER703typedef LONG slock_t;704 705#define HAS_TEST_AND_SET706#define TAS(lock) (InterlockedCompareExchange(lock, 1, 0))707 708#define SPIN_DELAY() spin_delay()709 710/* If using Visual C++ on Win64, inline assembly is unavailable.711 * Use a _mm_pause intrinsic instead of rep nop.712 */713#if defined(_WIN64)714static __forceinline void715spin_delay(void)716{717	_mm_pause();718}719#else720static __forceinline void721spin_delay(void)722{723	/* See comment for gcc code. Same code, MASM syntax */724	__asm rep nop;725}726#endif727 728#include <intrin.h>729#pragma intrinsic(_ReadWriteBarrier)730 731#define S_UNLOCK(lock)	\732	do { _ReadWriteBarrier(); (*(lock)) = 0; } while (0)733 734#endif735 736 737#endif	/* !defined(HAS_TEST_AND_SET) */738 739 740/* Blow up if we didn't have any way to do spinlocks */741#ifndef HAS_TEST_AND_SET742#error PostgreSQL does not have native spinlock support on this platform.  To continue the compilation, rerun configure using --disable-spinlocks.  However, performance will be poor.  Please report this to pgsql-bugs@lists.postgresql.org.743#endif744 745 746#else	/* !HAVE_SPINLOCKS */747 748 749/*750 * Fake spinlock implementation using semaphores --- slow and prone751 * to fall foul of kernel limits on number of semaphores, so don't use this752 * unless you must!  The subroutines appear in spin.c.753 */754typedef int slock_t;755 756extern bool s_lock_free_sema(volatile slock_t *lock);757extern void s_unlock_sema(volatile slock_t *lock);758extern void s_init_lock_sema(volatile slock_t *lock, bool nested);759extern int	tas_sema(volatile slock_t *lock);760 761#define S_LOCK_FREE(lock)	s_lock_free_sema(lock)762#define S_UNLOCK(lock)	 s_unlock_sema(lock)763#define S_INIT_LOCK(lock)	s_init_lock_sema(lock, false)764#define TAS(lock)	tas_sema(lock)765 766 767#endif	/* HAVE_SPINLOCKS */768 769 770/*771 * Default Definitions - override these above as needed.772 */773 774#if !defined(S_LOCK)775#define S_LOCK(lock) \776	(TAS(lock) ? s_lock((lock), __FILE__, __LINE__, __func__) : 0)777#endif	 /* S_LOCK */778 779#if !defined(S_LOCK_FREE)780#define S_LOCK_FREE(lock)	(*(lock) == 0)781#endif	 /* S_LOCK_FREE */782 783#if !defined(S_UNLOCK)784/*785 * Our default implementation of S_UNLOCK is essentially *(lock) = 0.  This786 * is unsafe if the platform can reorder a memory access (either load or787 * store) after a following store; platforms where this is possible must788 * define their own S_UNLOCK.  But CPU reordering is not the only concern:789 * if we simply defined S_UNLOCK() as an inline macro, the compiler might790 * reorder instructions from inside the critical section to occur after the791 * lock release.  Since the compiler probably can't know what the external792 * function s_unlock is doing, putting the same logic there should be adequate.793 * A sufficiently-smart globally optimizing compiler could break that794 * assumption, though, and the cost of a function call for every spinlock795 * release may hurt performance significantly, so we use this implementation796 * only for platforms where we don't know of a suitable intrinsic.  For the797 * most part, those are relatively obscure platform/compiler combinations to798 * which the PostgreSQL project does not have access.799 */800#define USE_DEFAULT_S_UNLOCK801extern void s_unlock(volatile slock_t *lock);802#define S_UNLOCK(lock)		s_unlock(lock)803#endif	 /* S_UNLOCK */804 805#if !defined(S_INIT_LOCK)806#define S_INIT_LOCK(lock)	S_UNLOCK(lock)807#endif	 /* S_INIT_LOCK */808 809#if !defined(SPIN_DELAY)810#define SPIN_DELAY()	((void) 0)811#endif	 /* SPIN_DELAY */812 813#if !defined(TAS)814extern int	tas(volatile slock_t *lock);		/* in port/.../tas.s, or815												 * s_lock.c */816 817#define TAS(lock)		tas(lock)818#endif	 /* TAS */819 820#if !defined(TAS_SPIN)821#define TAS_SPIN(lock)	TAS(lock)822#endif	 /* TAS_SPIN */823 824extern PGDLLIMPORT slock_t dummy_spinlock;825 826/*827 * Platform-independent out-of-line support routines828 */829extern int s_lock(volatile slock_t *lock, const char *file, int line, const char *func);830 831/* Support for dynamic adjustment of spins_per_delay */832#define DEFAULT_SPINS_PER_DELAY  100833 834extern void set_spins_per_delay(int shared_spins_per_delay);835extern int	update_spins_per_delay(int shared_spins_per_delay);836 837/*838 * Support for spin delay which is useful in various places where839 * spinlock-like procedures take place.840 */841typedef struct842{843	int			spins;844	int			delays;845	int			cur_delay;846	const char *file;847	int			line;848	const char *func;849} SpinDelayStatus;850 851static inline void852init_spin_delay(SpinDelayStatus *status,853				const char *file, int line, const char *func)854{855	status->spins = 0;856	status->delays = 0;857	status->cur_delay = 0;858	status->file = file;859	status->line = line;860	status->func = func;861}862 863#define init_local_spin_delay(status) init_spin_delay(status, __FILE__, __LINE__, __func__)864extern void perform_spin_delay(SpinDelayStatus *status);865extern void finish_spin_delay(SpinDelayStatus *status);866 867#endif	 /* S_LOCK_H */868 
codekingpro/portable-devtools · Team Ai