]> granicus.if.org Git - postgresql/commitdiff
Here is the long awaited optimized version of the S_LOCK patch.
authorBruce Momjian <bruce@momjian.us>
Tue, 16 Jun 1998 07:18:16 +0000 (07:18 +0000)
committerBruce Momjian <bruce@momjian.us>
Tue, 16 Jun 1998 07:18:16 +0000 (07:18 +0000)
This incorporates all the precedeing patches and emailed suggestions
and the results of the performance testing I posted last week. I
would like to get this tested on as many platforms as possible so
I can verify it went in correctly (as opposed to the horrorshow
last time I sent in a patch).

Once this is confirmed, I will make a tarball of files that can be
dropped into a 6.3.2 source tree as a few people have asked for
this in 6.3.2 as well.

David Gould

src/backend/storage/buffer/Makefile
src/backend/storage/buffer/s_lock.c
src/include/storage/s_lock.h

index 8626500a05f38c7813e4bb913b404032dfe556ac..298cdbb8d508afe5d33128a470140af29e28d7a6 100644 (file)
@@ -4,7 +4,7 @@
 #    Makefile for storage/buffer
 #
 # IDENTIFICATION
-#    $Header: /cvsroot/pgsql/src/backend/storage/buffer/Makefile,v 1.10 1998/05/04 16:58:37 scrappy Exp $
+#    $Header: /cvsroot/pgsql/src/backend/storage/buffer/Makefile,v 1.11 1998/06/16 07:18:15 momjian Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -27,7 +27,7 @@ clean:
        rm -f SUBSYS.o $(OBJS) s_lock_test
 
 s_lock_test: s_lock.c
-       $(CC) $(CFLAGS) -DS_LOCK_TEST=1 -g s_lock.c -o s_lock_test
+       $(CC) $(CFLAGS) -DS_LOCK_TEST=1 s_lock.c -o s_lock_test
        ./s_lock_test
 
 ifeq (depend,$(wildcard depend))
index aa44385fed50522e64af0716591dcfe883207ce6..23bc0ec2c4a37fac8da68770bf21284df97d031c 100644 (file)
@@ -7,12 +7,13 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/storage/buffer/Attic/s_lock.c,v 1.7 1998/05/04 23:49:09 scrappy Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/storage/buffer/Attic/s_lock.c,v 1.8 1998/06/16 07:18:15 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include <stdio.h>
+#include <sys/time.h>
 
 #include "config.h"
 #include "c.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.
- * Values are not critical and are weighted to the low end of the range. They
- * were chosen to work even with different select() timer resolutions on
- * different platforms.
- * note: total time to cycle through all 16 entries might be about .1 second.
+ * Values are not critical but 10 milliseconds is a common platform
+ * granularity.
+ * note: total time to cycle through all 16 entries might be about .07 sec.
  */
-int                    s_spincycle[S_NSPINCYCLE] =
-{0, 0, 0, 1000, 5000, 0, 10000, 3000,
- 0, 10000, 0, 15000, 9000, 21000, 6000, 30000
+#define S_NSPINCYCLE    20
+#define S_MAX_BUSY      500 * S_NSPINCYCLE
+
+int    s_spincycle[S_NSPINCYCLE] =
+{     0,     0,     0,     0, 10000,     0,     0,     0, 10000,     0,
+      0, 10000,     0,     0, 10000,     0, 10000,     0, 10000, 10000
 };
 
 
-#if defined(S_LOCK_DEBUG)
 /*
- * s_lock(lock) - take a spinlock
- * add intrumentation code to this and define S_LOCK_DEBUG
- * instead of hacking up the macro in s_lock.h
+ * s_lock_stuck(lock) - complain about a stuck spinlock
+ */
+static void
+s_lock_stuck(volatile slock_t *lock, const char *file, const int line)
+{
+       fprintf(stderr,
+                       "\nFATAL: s_lock(%08x) at %s:%d, stuck spinlock. Aborting.\n",
+                       (unsigned int) lock, file, line);
+       fprintf(stdout,
+                       "\nFATAL: s_lock(%08x) at %s:%d, stuck spinlock. Aborting.\n",
+                       (unsigned int) lock, file, line);
+       abort();
+}
+
+
+
+/*
+ * s_lock(lock) - take a spinlock with backoff
  */
 void
-s_lock(slock_t *lock, char *file, int line)
+s_lock(volatile slock_t *lock, const char *file, const int line)
 {
        int                     spins = 0;
 
@@ -49,114 +66,39 @@ s_lock(slock_t *lock, char *file, int line)
                struct timeval delay;
 
                delay.tv_sec = 0;
-               delay.tv_usec = s_spincycle[spins++ % S_NSPINCYCLE];
+               delay.tv_usec = s_spincycle[spins % S_NSPINCYCLE];
                (void) select(0, NULL, NULL, NULL, &delay);
-               if (spins > S_MAX_BUSY)
+               if (++spins > S_MAX_BUSY)
                {
-                       /* It's been well over a minute...  */
+                       /* It's been over a minute...  */
                        s_lock_stuck(lock, file, line);
                }
        }
 }
-#endif /* S_LOCK_DEBUG */
-
 
-/*
- * s_lock_stuck(lock) - deal with stuck spinlock
- */
-void
-s_lock_stuck(slock_t *lock, char *file, int line)
-{
-       fprintf(stderr,
-                       "\nFATAL: s_lock(%08x) at %s:%d, stuck spinlock. Aborting.\n",
-                       (unsigned int) lock, file, line);
-       fprintf(stdout,
-                       "\nFATAL: s_lock(%08x) at %s:%d, stuck spinlock. Aborting.\n",
-                       (unsigned int) lock, file, line);
-       abort();
-}
 
 
 
 /*
- * Various TAS implementations moved from s_lock.h to avoid redundant
- * definitions of the same routine.
- * RESOLVE: move this to tas.c. Alternatively get rid of tas.[cso] and fold
- * all that into this file.
+ * Various TAS implementations that cannot live in s_lock.h as no inline
+ * definition exists (yet).
+ * In the future, get rid of tas.[cso] and fold it into this file.
  */
 
 
-#if defined(linux)
+#if defined(__GNUC__)
 /*************************************************************************
- * All the Linux flavors
+ * All the gcc flavors that are not inlined
  */
 
 
-#if defined(__alpha)
-int
-tas(slock_t *lock)
-{
-       slock_t         _res;
-
-  __asm__("      ldq   $0, %0              \n\
-                 bne   $0, already_set     \n\
-                 ldq_l $0, %0             \n\
-                 bne   $0, already_set     \n\
-                 or    $31, 1, $0          \n\
-                 stq_c $0, %0             \n\
-                 beq   $0, stqc_fail       \n\
-        success: bis   $31, $31, %1        \n\
-                 mb                               \n\
-                 jmp   $31, end                   \n\
-      stqc_fail: or    $31, 1, $0             \n\
-    already_set: bis   $0, $0, %1             \n\
-            end: nop      ": "=m"(*lock), "=r"(_res): :"0");
-
-       return (_res != 0);
-}
-#endif /* __alpha */
-
-
-
-#if defined(i386)
-int
-tas(slock_t *lock)
-{
-       slock_t         _res = 1;
-
-  __asm__("lock; xchgb %0,%1": "=q"(_res), "=m"(*lock):"0"(0x1));
-       return (_res != 0);
-}
-#endif /* i386 */
-
-
-
-#if defined(sparc)
-
-int
-tas(slock_t *lock)
-{
-       slock_t         _res;
-       slock_t    *tmplock = lock;
-
-  __asm__("ldstub [%1], %0" \
-  :                    "=&r"(_res), "=r"(tmplock) \
-  :                    "1"(tmplock));
-       return (_res != 0);
-}
-
-#endif /* sparc */
-
-
 
 #if defined(PPC)
-
-static int
-tas_dummy()
+/* Note: need a nice gcc constrained asm version so it can be inlined */
+int
+tas(volatile slock_t *lock)
 {
-       __asm__("                               \n\
-tas:                                           \n\
-                       lwarx   5,0,3   \n\
+       __asm__("lwarx  5,0,3   \n\
                        cmpwi   5,0             \n\
                        bne             fail    \n\
                        addi    5,5,1   \n\
@@ -169,21 +111,20 @@ success:                                  \n\
                blr                             \n\
        ");
 }
-
 #endif /* PPC */
 
 
 
-#else /* defined(linux) */
+#else /* defined(__GNUC__) */
 /***************************************************************************
- * All Non-Linux
+ * All non gcc
  */
 
 
 
 #if defined(sun3)
 static void
-tas_dummy()                                            /* really means: extern int tas(slock_t *lock); */
+tas_dummy()                            /* really means: extern int tas(slock_t *lock); */
 {
        asm("LLA0:");
        asm("   .data");
@@ -208,26 +149,18 @@ tas_dummy()                                               /* really means: extern int tas(slock_t *lock); */
 
 #if defined(NEED_SPARC_TAS_ASM)
 /*
- * bsd and bsdi sparc machines
+ * sparc machines not using gcc
  */
-
-/* if we're using -ansi w/ gcc, use __asm__ instead of asm */
-#if defined(__STRICT_ANSI__)
-#define asm(x) __asm__(x)
-#endif /* __STRICT_ANSI__ */
-
 static void
-tas_dummy()                                            /* really means: extern int tas(slock_t *lock); */
+tas_dummy()                            /* really means: extern int tas(slock_t *lock); */
 {
        asm(".seg \"data\"");
        asm(".seg \"text\"");
        asm("_tas:");
-
        /*
         * Sparc atomic test and set (sparc calls it "atomic load-store")
         */
        asm("ldstub [%r8], %r8");
-
        asm("retl");
        asm("nop");
 }
@@ -237,76 +170,25 @@ tas_dummy()                                               /* really means: extern int tas(slock_t *lock); */
 
 
 
-#if defined(NEED_VAX_TAS_ASM)
-/*
- * VAXen -- even multiprocessor ones
- * (thanks to Tom Ivar Helbekkmo)
- */
-typedef unsigned char slock_t;
-
-int
-tas(slock_t *lock)
-{
-       register        ret;
-
-       asm("   movl $1, r0
-               bbssi $0, (%1), 1f
-               clrl r0
-  1:   movl r0, %0 "
-  :            "=r"(ret)                               /* return value, in register */
-  :            "r"(lock)                               /* argument, 'lock pointer', in register */
-  :            "r0");                                  /* inline code uses this register */
-
-       return ret;
-}
-
-#endif /* NEED_VAX_TAS_ASM */
-
-
-
 #if defined(NEED_I386_TAS_ASM)
-/*
- * i386 based things
- */
-
-#if defined(USE_UNIVEL_CC)
-asm int
-tas(slock_t *s_lock)
-{
-       %lab locked;
-/* Upon entry, %eax will contain the pointer to the lock byte */
-       pushl % ebx
-       xchgl % eax, %ebx
-       xor % eax, %eax
-       movb $255, %al
-       lock
-       xchgb % al, (%ebx)
-       popl % ebx
-}
-
-
-#else /* USE_UNIVEL_CC */
-
-int
-tas(slock_t *lock)
-{
-       slock_t         _res = 1;
+/* non gcc i386 based things */
+#endif /* NEED_I386_TAS_ASM */
 
-  __asm__("lock; xchgb %0,%1": "=q"(_res), "=m"(*lock):"0"(0x1));
-       return (_res != 0);
-}
 
-#endif /* USE_UNIVEL_CC */
 
-#endif /* NEED_I386_TAS_ASM */
+#endif /* not __GNUC__ */
 
 
-#endif /* linux */
 
 
+/*****************************************************************************/
 #if defined(S_LOCK_TEST)
 
-slock_t                test_lock;
+/*
+ * test program for verifying a port.
+ */
+
+volatile slock_t       test_lock;
 
 void
 main()
@@ -330,7 +212,7 @@ main()
        printf("S_LOCK_TEST: this will hang for a few minutes and then abort\n");
        printf("             with a 'stuck spinlock' message if S_LOCK()\n");
        printf("             and TAS() are working.\n");
-       S_LOCK(&test_lock);
+       s_lock(&test_lock, __FILE__, __LINE__);
 
        printf("S_LOCK_TEST: failed, lock not locked~\n");
        exit(3);
@@ -338,3 +220,4 @@ main()
 }
 
 #endif /* S_LOCK_TEST */
+
index 369a3d8724fc5c32d1a0c9f5c72f0dc07d210e59..4808d3d49328bdaf64f8cc1756898a1cbdc1951a 100644 (file)
@@ -7,13 +7,14 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/include/storage/s_lock.h,v 1.35 1998/06/15 18:40:04 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/include/storage/s_lock.h,v 1.36 1998/06/16 07:18:16 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
+
 /*
  *      DESCRIPTION
- *             The public functions that must be provided are:
+ *             The public macros that must be provided are:
  *
  *             void S_INIT_LOCK(slock_t *lock)
  *
  *             #define TAS(lock) tas(lock)
  *             int tas(slock_t *lock)          // True if lock already set
  *
+ *             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.
+ *
+ *     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)...).
  *
- *     NOTES
  *             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
  *             regression test suite by about 25%.  I don't have an assembler
  *             manual for POWER in any case.
  *
- *             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.
- *
  */
 #if !defined(S_LOCK_H)
 #define S_LOCK_H
 
 #if defined(HAS_TEST_AND_SET)
 
-#if defined(linux)
-/***************************************************************************
- * All Linux
+
+#if defined(__GNUC__)
+/*************************************************************************
+ * All the gcc inlines
  */
 
 #if defined(__alpha)
-
+#define TAS(lock) tas(lock)
 #define S_UNLOCK(lock) { __asm__("mb"); *(lock) = 0; }
 
+static __inline__ int
+tas(volatile slock_t *lock)
+{
+       register slock_t        _res;
+
+    __asm__("    ldq   $0, %0              \n\
+                 bne   $0, already_set     \n\
+                 ldq_l $0, %0             \n\
+                 bne   $0, already_set     \n\
+                 or    $31, 1, $0          \n\
+                 stq_c $0, %0             \n\
+                 beq   $0, stqc_fail       \n\
+        success: bis   $31, $31, %1        \n\
+                 mb                               \n\
+                 jmp   $31, end                   \n\
+      stqc_fail: or    $31, 1, $0             \n\
+    already_set: bis   $0, $0, %1             \n\
+            end: nop      " : "=m"(*lock), "=r"(_res) : : "0");
+
+       return (int) _res;
+}
 #endif /* __alpha */
 
 
 
+#if defined(i386)
+#define TAS(lock) tas(lock)
+
+static __inline__ int
+tas(volatile slock_t *lock)
+{
+       register slock_t        _res = 1;
+
+    __asm__("lock; xchgb %0,%1" : "=q"(_res), "=m"(*lock) : "0"(_res) );
+       return (int) _res;
+}
+#endif /* i386 */
+
+
+
+#if defined(sparc)
+#define TAS(lock) tas(lock)
+
+static __inline__ int
+tas(volatile slock_t *lock)
+{
+       register slock_t        _res = 1;
+
+    __asm__("ldstub [%1], %0" \
+            : "=r"(_res), "=m"(*lock) \
+            : "1"(lock));
+       return (int) _res;
+}
+#endif /* sparc */
+
+
+
+#if defined(NEED_VAX_TAS_ASM)
+/*
+ * VAXen -- even multiprocessor ones
+ * (thanks to Tom Ivar Helbekkmo)
+ */
+#define TAS(lock) tas(lock)
+
+typedef unsigned char slock_t;
+
+static __inline__ int
+tas(volatile slock_t *lock)
+{
+       register        _res;
+
+       __asm__("       movl $1, r0
+               bbssi $0, (%1), 1f
+               clrl r0
+  1:   movl r0, %0 "
+  :            "=r"(_res)                              /* return value, in register */
+  :            "r"(lock)                               /* argument, 'lock pointer', in register */
+  :            "r0");                                  /* inline code uses this register */
+       return (int) _res;
+}
+#endif /* NEED_VAX_TAS_ASM */
+
+
 
-#else /* linux */
+
+#else /* __GNUC__ */
 /***************************************************************************
- * All non Linux
+ * All non gcc
  */
 
 #if defined (nextstep)
  */
 
 #define S_LOCK(lock)   mutex_lock(lock)
-
 #define S_UNLOCK(lock) mutex_unlock(lock)
-
 #define S_INIT_LOCK(lock)      mutex_init(lock)
-
 /* For Mach, we have to delve inside the entrails of `struct mutex'.  Ick! */
 #define S_LOCK_FREE(alock)     ((alock)->lock == 0)
-
 #endif /* nextstep */
 
 
  * for the R3000 chips out there.
  */
 #define TAS(lock)      (!acquire_lock(lock))
-
 #define S_UNLOCK(lock) release_lock(lock)
-
 #define S_INIT_LOCK(lock)      init_lock(lock)
-
 #define S_LOCK_FREE(lock)      (stat_lock(lock) == UNLOCKED)
-
 #endif /* __sgi */
 
 
  * (see storage/ipc.h).
  */
 #define TAS(lock)      (msem_lock((lock), MSEM_IF_NOWAIT) < 0)
-
 #define S_UNLOCK(lock) msem_unlock((lock), 0)
-
 #define S_INIT_LOCK(lock)      msem_init((lock), MSEM_UNLOCKED)
-
 #define S_LOCK_FREE(lock)      (!(lock)->msem_state)
-
 #endif /* __alpha */
 
 
  * (see storage/ipc.h).
  */
 #define TAS(lock)      cs((int *) (lock), 0, 1)
-
 #endif /* _AIX */
 
 
@@ -175,64 +244,56 @@ static slock_t clear_lock =
 {-1, -1, -1, -1};
 
 #define S_UNLOCK(lock) (*(lock) = clear_lock)  /* struct assignment */
-
 #define S_LOCK_FREE(lock)      ( *(int *) (((long) (lock) + 15) & ~15) != 0)
-
 #endif /* __hpux */
 
 
 
-#endif /* else defined(linux) */
-
+#if defined(NEED_I386_TAS_ASM)
+/* non gcc i386 based things */
 
 
+#if defined(USE_UNIVEL_CC)
+#define TAS(lock)      tas(lock)
 
-/****************************************************************************
- * Default Definitions - override these above as needed.
- */
+asm int
+tas(slock_t *s_lock)
+{
+       %lab locked;
+       /* Upon entry, %eax will contain the pointer to the lock byte */
+       pushl % ebx
+       xchgl % eax, %ebx
+       xor % eax, %eax
+       movb $255, %al
+       lock
+       xchgb % al, (%ebx)
+       popl % ebx
+}
+#endif /* USE_UNIVEL_CC */
 
-#if !defined(S_LOCK)
 
-#include <sys/time.h>
+#endif /* NEED_I386_TAS_ASM */
 
-#define S_NSPINCYCLE   16
-#define S_MAX_BUSY             1000 * S_NSPINCYCLE
 
-extern int     s_spincycle[];
-extern void s_lock_stuck(slock_t *lock, char *file, int line);
+#endif /* else defined(__GNUC__) */
 
-#if defined(S_LOCK_DEBUG)
 
-extern void s_lock(slock_t *lock);
 
-#define S_LOCK(lock) s_lock(lock, __FILE__, __LINE__)
 
-#else /* S_LOCK_DEBUG */
+/****************************************************************************
+ * 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 { \
-       int spins = 0; \
-       while (TAS(lock)) \
-       { \
-               struct timeval  delay; \
-               delay.tv_sec = 0; \
-               delay.tv_usec = s_spincycle[spins++ % S_NSPINCYCLE]; \
-               (void) select(0, NULL, NULL, NULL, &delay); \
-               if (spins > S_MAX_BUSY) \
-               { \
-                       /* It's been well over a minute...  */ \
-                       s_lock_stuck(lock, __FILE__, __LINE__); \
-               } \
-       } \
-} while(0)
-
-#endif /* S_LOCK_DEBUG */
+    if (TAS((volatile slock_t *) lock)) {\
+        s_lock((volatile slock_t *) lock, __FILE__, __LINE__); \
+    } else
 #endif /* S_LOCK */
 
-
-
 #if !defined(S_LOCK_FREE)
-#define S_LOCK_FREE(lock)      ((*lock) == 0)
+#define S_LOCK_FREE(lock)      (*(lock) == 0)
 #endif /* S_LOCK_FREE */
 
 #if !defined(S_UNLOCK)
@@ -244,12 +305,11 @@ do { \
 #endif /* S_INIT_LOCK */
 
 #if !defined(TAS)
-int                    tas(slock_t *lock); /* port/.../tas.s, or s_lock.c */
-
-#define TAS(lock)              tas(lock)
+int    tas(volatile slock_t *lock); /* port/.../tas.s, or s_lock.c */
+#define TAS(lock)              tas((volatile slock_t *) lock)
 #endif /* TAS */
 
 
 #endif /* HAS_TEST_AND_SET */
-
 #endif /* S_LOCK_H */
+