summaryrefslogtreecommitdiff
path: root/src/include
diff options
context:
space:
mode:
authorTom Lane2000-12-29 21:31:21 +0000
committerTom Lane2000-12-29 21:31:21 +0000
commit7f60b81e1ab7cbb27298e980f25445dfa58d50bb (patch)
treef1ace7fa85f8f4037bf254008b41e65c9a7ce7f8 /src/include
parent7d363c4c3376459bc9389b250bc7b70e9cf7aa75 (diff)
Fix failure in CreateCheckPoint on some Alpha boxes --- it's not OK to
assume that TAS() will always succeed the first time, even if the lock is known to be free. Also, make sure that code will eventually time out and report a stuck spinlock, rather than looping forever. Small cleanups in s_lock.h, too.
Diffstat (limited to 'src/include')
-rw-r--r--src/include/storage/s_lock.h231
1 files changed, 125 insertions, 106 deletions
diff --git a/src/include/storage/s_lock.h b/src/include/storage/s_lock.h
index 863801f8ee3..9f6b8d3a12c 100644
--- a/src/include/storage/s_lock.h
+++ b/src/include/storage/s_lock.h
@@ -1,75 +1,97 @@
/*-------------------------------------------------------------------------
*
* s_lock.h
- * This file contains the implementation (if any) for spinlocks.
+ * This file contains the in-line portion of the implementation
+ * of spinlocks.
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/include/storage/s_lock.h,v 1.75 2000/12/03 14:41:42 thomas Exp $
+ * $Header: /cvsroot/pgsql/src/include/storage/s_lock.h,v 1.76 2000/12/29 21:31:20 tgl Exp $
*
*-------------------------------------------------------------------------
*/
-/*
- * DESCRIPTION
- * The public macros that must be provided are:
+/*----------
+ * DESCRIPTION
+ * The public macros that must be provided are:
+ *
+ * void S_INIT_LOCK(slock_t *lock)
+ * Initialize a spinlock (to the unlocked state).
+ *
+ * void S_LOCK(slock_t *lock)
+ * Acquire a spinlock, waiting if necessary.
+ * Time out and abort() if unable to acquire the lock in a
+ * "reasonable" amount of time --- typically ~ 1 minute.
*
- * void S_INIT_LOCK(slock_t *lock)
+ * void S_UNLOCK(slock_t *lock)
+ * Unlock a previously acquired lock.
*
- * void S_LOCK(slock_t *lock)
+ * bool S_LOCK_FREE(slock_t *lock)
+ * Tests if the lock is free. Returns TRUE if free, FALSE if locked.
+ * This does *not* change the state of the lock.
*
- * void S_UNLOCK(slock_t *lock)
+ * int TAS(slock_t *lock)
+ * Atomic test-and-set instruction. Attempt to acquire the lock,
+ * but do *not* wait. Returns 0 if successful, nonzero if unable
+ * to acquire the lock.
*
- * void S_LOCK_FREE(slock_t *lock)
- * Tests if the lock is free. Returns non-zero if free, 0 if locked.
+ * TAS() is a lower-level part of the API, but is used directly in a
+ * few places that want to do other things while waiting for a lock.
+ * The S_LOCK() macro is equivalent to
*
- * The S_LOCK() macro implements a primitive but still useful random
- * backoff to avoid hordes of busywaiting lockers chewing CPU.
+ * void
+ * S_LOCK(slock_t *lock)
+ * {
+ * unsigned spins = 0;
*
- * Effectively:
- * void
- * S_LOCK(slock_t *lock)
+ * while (TAS(lock))
* {
- * while (TAS(lock))
- * {
- * // back off the cpu for a semi-random short time
- * }
+ * S_LOCK_SLEEP(lock, spins++);
* }
+ * }
*
- * This implementation takes advantage of a tas function written
- * (in assembly language) on machines that have a native test-and-set
- * instruction. Alternative mutex implementations may also be used.
- * This function is hidden under the TAS macro to allow substitutions.
+ * where S_LOCK_SLEEP() checks for timeout and sleeps for a short
+ * interval. Callers that want to perform useful work while waiting
+ * can write out this entire loop and insert the "useful work" inside
+ * the loop.
*
- * #define TAS(lock) tas(lock)
- * int tas(slock_t *lock) // True if lock already set
+ * CAUTION to TAS() callers: on some platforms TAS() may sometimes
+ * report failure to acquire a lock even when the lock is not locked.
+ * For example, on Alpha TAS() will "fail" if interrupted. Therefore
+ * TAS() must *always* be invoked in a retry loop as depicted, even when
+ * you are certain the lock is free.
*
- * There are default implementations for all these macros at the bottom
- * of this file. Check if your platform can use these or needs to
- * override them.
+ * On most supported platforms, TAS() uses a tas() function written
+ * in assembly language to execute a hardware atomic-test-and-set
+ * instruction. Equivalent OS-supplied mutex routines could be used too.
*
- * NOTES
- * If none of this can be done, POSTGRES will default to using
- * System V semaphores (and take a large performance hit -- around 40%
- * of its time on a DS5000/240 is spent in semop(3)...).
- *
- * AIX has a test-and-set but the recommended interface is the cs(3)
- * system call. This provides an 8-instruction (plus system call
- * overhead) uninterruptible compare-and-set operation. True
- * spinlocks might be faster but using cs(3) still speeds up the
- * regression test suite by about 25%. I don't have an assembler
- * manual for POWER in any case.
+ * If no system-specific TAS() is available (ie, HAS_TEST_AND_SET is not
+ * defined), then we fall back on an emulation that uses SysV semaphores.
+ * This emulation will be MUCH MUCH MUCH slower than a proper TAS()
+ * implementation, because of the cost of a kernel call per lock or unlock.
+ * An old report is that Postgres spends around 40% of its time in semop(2)
+ * when using the SysV semaphore code.
*
+ * Note to implementors: there are default implementations for all these
+ * macros at the bottom of the file. Check if your platform can use
+ * these or needs to override them.
+ *----------
*/
#ifndef S_LOCK_H
#define S_LOCK_H
#include "storage/ipc.h"
-extern void s_lock_sleep(unsigned spin);
+/* Platform-independent out-of-line support routines */
+extern void s_lock(volatile slock_t *lock,
+ const char *file, const int line);
+extern void s_lock_sleep(unsigned spins, int microsec,
+ volatile slock_t *lock,
+ const char *file, const int line);
+
#if defined(HAS_TEST_AND_SET)
@@ -216,7 +238,6 @@ tas(volatile slock_t *lock)
#endif /* NEED_VAX_TAS_ASM */
-
#if defined(NEED_NS32K_TAS_ASM)
#define TAS(lock) tas(lock)
@@ -234,28 +255,13 @@ tas(volatile slock_t *lock)
-#else /* __GNUC__ */
-/***************************************************************************
- * All non gcc
- */
+#else /* !__GNUC__ */
-#if defined(__QNX__)
-/*
- * QNX 4
- *
- * Note that slock_t under QNX is sem_t instead of char
+/***************************************************************************
+ * All non-gcc inlines
*/
-#define TAS(lock) (sem_trywait((lock)) < 0)
-#define S_UNLOCK(lock) sem_post((lock))
-#define S_INIT_LOCK(lock) sem_init((lock), 1, 1)
-#define S_LOCK_FREE(lock) (lock)->value
-#endif /* __QNX__ */
-
-#if defined(NEED_I386_TAS_ASM)
-/* non gcc i386 based things */
-
-#if defined(USE_UNIVEL_CC)
+#if defined(NEED_I386_TAS_ASM) && defined(USE_UNIVEL_CC)
#define TAS(lock) tas(lock)
asm int
@@ -271,16 +277,15 @@ tas(volatile slock_t *s_lock)
popl %ebx
}
-#endif /* USE_UNIVEL_CC */
-
-#endif /* NEED_I386_TAS_ASM */
+#endif /* defined(NEED_I386_TAS_ASM) && defined(USE_UNIVEL_CC) */
#endif /* defined(__GNUC__) */
/*************************************************************************
- * These are the platforms that have common code for gcc and non-gcc
+ * These are the platforms that do not use inline assembler (and hence
+ * have common code for gcc and non-gcc compilers, if both are available).
*/
@@ -342,7 +347,7 @@ __asm__(" ldq $0, %0 \n\
* (see include/port/hpux.h).
*
* a "set" slock_t has a single word cleared. a "clear" slock_t has
- * all words set to non-zero. tas() in tas.s
+ * all words set to non-zero. tas() is in tas.s
*/
#define S_UNLOCK(lock) \
@@ -356,6 +361,19 @@ do { \
#endif /* __hpux */
+#if defined(__QNX__)
+/*
+ * QNX 4
+ *
+ * Note that slock_t under QNX is sem_t instead of char
+ */
+#define TAS(lock) (sem_trywait((lock)) < 0)
+#define S_UNLOCK(lock) sem_post((lock))
+#define S_INIT_LOCK(lock) sem_init((lock), 1, 1)
+#define S_LOCK_FREE(lock) ((lock)->value)
+#endif /* __QNX__ */
+
+
#if defined(__sgi)
/*
* SGI IRIX 5
@@ -416,21 +434,57 @@ do { \
+#else /* !HAS_TEST_AND_SET */
+
+/*
+ * Fake spinlock implementation using SysV semaphores --- slow and prone
+ * to fall foul of kernel limits on number of semaphores, so don't use this
+ * unless you must!
+ */
+
+typedef struct
+{
+ /* reference to semaphore used to implement this spinlock */
+ IpcSemaphoreId semId;
+ int sem;
+} slock_t;
+
+extern bool s_lock_free_sema(volatile slock_t *lock);
+extern void s_unlock_sema(volatile slock_t *lock);
+extern void s_init_lock_sema(volatile slock_t *lock);
+extern int tas_sema(volatile slock_t *lock);
+
+#define S_LOCK_FREE(lock) s_lock_free_sema(lock)
+#define S_UNLOCK(lock) s_unlock_sema(lock)
+#define S_INIT_LOCK(lock) s_init_lock_sema(lock)
+#define TAS(lock) tas_sema(lock)
+
+#endif /* HAS_TEST_AND_SET */
+
+
/****************************************************************************
* Default Definitions - override these above as needed.
*/
#if !defined(S_LOCK)
-extern void s_lock(volatile slock_t *lock, const char *file, const int line);
-
#define S_LOCK(lock) \
do { \
- if (TAS((volatile slock_t *) (lock))) \
- s_lock((volatile slock_t *) (lock), __FILE__, __LINE__); \
+ if (TAS(lock)) \
+ s_lock((lock), __FILE__, __LINE__); \
} while (0)
#endif /* S_LOCK */
+#if !defined(S_LOCK_SLEEP)
+#define S_LOCK_SLEEP(lock,spins) \
+ s_lock_sleep((spins), 0, (lock), __FILE__, __LINE__)
+#endif /* S_LOCK_SLEEP */
+
+#if !defined(S_LOCK_SLEEP_INTERVAL)
+#define S_LOCK_SLEEP_INTERVAL(lock,spins,microsec) \
+ s_lock_sleep((spins), (microsec), (lock), __FILE__, __LINE__)
+#endif /* S_LOCK_SLEEP_INTERVAL */
+
#if !defined(S_LOCK_FREE)
#define S_LOCK_FREE(lock) (*(lock) == 0)
#endif /* S_LOCK_FREE */
@@ -444,46 +498,11 @@ extern void s_lock(volatile slock_t *lock, const char *file, const int line);
#endif /* S_INIT_LOCK */
#if !defined(TAS)
-extern int tas(volatile slock_t *lock); /* port/.../tas.s, or
+extern int tas(volatile slock_t *lock); /* in port/.../tas.s, or
* s_lock.c */
-#define TAS(lock) tas((volatile slock_t *) (lock))
+#define TAS(lock) tas(lock)
#endif /* TAS */
-#else /* !HAS_TEST_AND_SET */
-
-/*
- * Fake spinlock implementation using SysV semaphores --- slow and prone
- * to fall foul of kernel limits on number of semaphores, so don't use this
- * unless you must!
- */
-
-typedef struct
-{
- /* reference to semaphore used to implement this spinlock */
- IpcSemaphoreId semId;
- int sem;
-} slock_t;
-
-extern bool s_lock_free_sema(volatile slock_t *lock);
-extern void s_unlock_sema(volatile slock_t *lock);
-extern void s_init_lock_sema(volatile slock_t *lock);
-extern int tas_sema(volatile slock_t *lock);
-
-extern void s_lock(volatile slock_t *lock, const char *file, const int line);
-
-#define S_LOCK(lock) \
- do { \
- if (TAS((volatile slock_t *) (lock))) \
- s_lock((volatile slock_t *) (lock), __FILE__, __LINE__); \
- } while (0)
-
-#define S_LOCK_FREE(lock) s_lock_free_sema(lock)
-#define S_UNLOCK(lock) s_unlock_sema(lock)
-#define S_INIT_LOCK(lock) s_init_lock_sema(lock)
-#define TAS(lock) tas_sema(lock)
-
-#endif /* HAS_TEST_AND_SET */
-
#endif /* S_LOCK_H */