aboutsummaryrefslogtreecommitdiff
path: root/src/backend/storage/lmgr/s_lock.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2001-09-29 04:02:27 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2001-09-29 04:02:27 +0000
commit499abb0c0f21cb861c5af1d49a06469f3cfcc1eb (patch)
tree0af6262d9b6d1159315e93e90e69047b959ea5f5 /src/backend/storage/lmgr/s_lock.c
parent818fb55ac49b4b20e65d9899fc1784e54e86db58 (diff)
downloadpostgresql-499abb0c0f21cb861c5af1d49a06469f3cfcc1eb.tar.gz
postgresql-499abb0c0f21cb861c5af1d49a06469f3cfcc1eb.zip
Implement new 'lightweight lock manager' that's intermediate between
existing lock manager and spinlocks: it understands exclusive vs shared lock but has few other fancy features. Replace most uses of spinlocks with lightweight locks. All remaining uses of spinlocks have very short lock hold times (a few dozen instructions), so tweak spinlock backoff code to work efficiently given this assumption. All per my proposal on pghackers 26-Sep-01.
Diffstat (limited to 'src/backend/storage/lmgr/s_lock.c')
-rw-r--r--src/backend/storage/lmgr/s_lock.c120
1 files changed, 29 insertions, 91 deletions
diff --git a/src/backend/storage/lmgr/s_lock.c b/src/backend/storage/lmgr/s_lock.c
index 6dc38b5955c..055c809cf8d 100644
--- a/src/backend/storage/lmgr/s_lock.c
+++ b/src/backend/storage/lmgr/s_lock.c
@@ -1,14 +1,15 @@
/*-------------------------------------------------------------------------
*
* s_lock.c
- * Spinlock support routines
+ * Hardware-dependent implementation of spinlocks.
+ *
*
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/s_lock.c,v 1.1 2001/09/27 19:10:02 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/s_lock.c,v 1.2 2001/09/29 04:02:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -17,49 +18,14 @@
#include <sys/time.h>
#include <unistd.h>
-#include "miscadmin.h"
#include "storage/s_lock.h"
-/*----------
- * Each time we busy spin we select the next element of this array as the
- * number of microseconds to wait. This accomplishes pseudo random back-off.
- *
- * Note that on most platforms, specified values will be rounded up to the
- * next multiple of a clock tick, which is often ten milliseconds (10000).
- * So, we are being way overoptimistic to assume that these different values
- * are really different, other than the last. But there are a few platforms
- * with better-than-usual timekeeping, and on these we will get pretty good
- * pseudo-random behavior.
- *
- * Total time to cycle through all 20 entries will be at least 100 msec,
- * more commonly (10 msec resolution) 220 msec, and on some platforms
- * as much as 420 msec (when the remainder of the current tick cycle is
- * ignored in deciding when to time out, as on FreeBSD and older Linuxen).
- * We use the 100msec figure to figure max_spins, so actual timeouts may
- * be as much as four times the nominal value, but will never be less.
- *----------
- */
-#define S_NSPINCYCLE 20
-
-int s_spincycle[S_NSPINCYCLE] =
-{1, 10, 100, 1000,
- 10000, 1000, 1000, 1000,
- 10000, 1000, 1000, 10000,
- 1000, 1000, 10000, 1000,
- 10000, 1000, 10000, 30000
-};
-
-#define AVG_SPINCYCLE 5000 /* average entry in microsec: 100ms / 20 */
-
-#define DEFAULT_TIMEOUT (100*1000000) /* default timeout: 100 sec */
-
-
/*
* s_lock_stuck() - complain about a stuck spinlock
*/
static void
-s_lock_stuck(volatile slock_t *lock, const char *file, const int line)
+s_lock_stuck(volatile slock_t *lock, const char *file, int line)
{
fprintf(stderr,
"\nFATAL: s_lock(%p) at %s:%d, stuck spinlock. Aborting.\n",
@@ -72,69 +38,41 @@ s_lock_stuck(volatile slock_t *lock, const char *file, const int line)
/*
- * s_lock_sleep() - sleep a pseudo-random amount of time, check for timeout
- *
- * The 'timeout' is given in microsec, or may be 0 for "infinity". Note that
- * this will be a lower bound (a fairly loose lower bound, on most platforms).
- *
- * 'microsec' is the number of microsec to delay per loop. Normally
- * 'microsec' is 0, specifying to use the next s_spincycle[] value.
- * Some callers may pass a nonzero interval, specifying to use exactly that
- * delay value rather than a pseudo-random delay.
+ * s_lock(lock) - platform-independent portion of waiting for a spinlock.
*/
void
-s_lock_sleep(unsigned spins, int timeout, int microsec,
- volatile slock_t *lock,
- const char *file, const int line)
-{
- struct timeval delay;
-
- if (microsec > 0)
- {
- delay.tv_sec = microsec / 1000000;
- delay.tv_usec = microsec % 1000000;
- }
- else
- {
- delay.tv_sec = 0;
- delay.tv_usec = s_spincycle[spins % S_NSPINCYCLE];
- microsec = AVG_SPINCYCLE; /* use average to figure timeout */
- }
-
- if (timeout > 0)
- {
- unsigned max_spins = timeout / microsec;
-
- if (spins > max_spins)
- s_lock_stuck(lock, file, line);
- }
-
- (void) select(0, NULL, NULL, NULL, &delay);
-}
-
-
-/*
- * s_lock(lock) - take a spinlock with backoff
- */
-void
-s_lock(volatile slock_t *lock, const char *file, const int line)
+s_lock(volatile slock_t *lock, const char *file, int line)
{
unsigned spins = 0;
+ unsigned delays = 0;
+ struct timeval delay;
/*
- * If you are thinking of changing this code, be careful. This same
- * loop logic is used in other places that call TAS() directly.
+ * We loop tightly for awhile, then delay using select() and try again.
+ * Preferably, "awhile" should be a small multiple of the maximum time
+ * we expect a spinlock to be held. 100 iterations seems about right.
*
- * While waiting for a lock, we check for cancel/die interrupts (which is
- * a no-op if we are inside a critical section). The interrupt check
- * can be omitted in places that know they are inside a critical
- * section. Note that an interrupt must NOT be accepted after
- * acquiring the lock.
+ * We use a 10 millisec select delay because that is the lower limit on
+ * many platforms. The timeout is figured on this delay only, and so the
+ * nominal 1 minute is a lower bound.
*/
+#define SPINS_PER_DELAY 100
+#define DELAY_MSEC 10
+#define TIMEOUT_MSEC (60 * 1000)
+
while (TAS(lock))
{
- s_lock_sleep(spins++, DEFAULT_TIMEOUT, 0, lock, file, line);
- CHECK_FOR_INTERRUPTS();
+ if (++spins > SPINS_PER_DELAY)
+ {
+ if (++delays > (TIMEOUT_MSEC / DELAY_MSEC))
+ s_lock_stuck(lock, file, line);
+
+ delay.tv_sec = 0;
+ delay.tv_usec = DELAY_MSEC * 1000;
+ (void) select(0, NULL, NULL, NULL, &delay);
+
+ spins = 0;
+ }
}
}