List:Commits« Previous MessageNext Message »
From:Davi Arnaut Date:July 8 2010 4:16pm
Subject:bzr commit into mysql-trunk-bugfixing branch (davi:3095) Bug#22320 Bug#52261
View as plain text  
# At a local mysql-trunk-bugfixing repository of davi

 3095 Davi Arnaut	2010-07-08
      Bug#22320: my_atomic-t unit test fails
      Bug#52261: 64 bit atomic operations do not work on Solaris i386
                 gcc in debug compilation
      
      One of the various problems was that the source operand to
      CMPXCHG8b was marked as a input/output operand, causing GCC
      to use the EBX register as the destination register for the
      CMPXCHG8b instruction. This could lead to crashes as the EBX
      register is also implicitly used by the instruction, causing
      the value to be potentially garbaged and a protection fault
      once the value is used to access a position in memory.
      
      Another problem was the lack of proper clobbers for the atomic
      operations and, also, a discrepancy between the implementations
      for the Compare and Set operation. The specific problems are
      described and fixed by Kristian Nielsen patches:
      
      Patch: 1
      
      Fix bugs in my_atomic_cas*(val,cmp,new) that *cmp is accessed
      after CAS succeds.
      
      In the gcc builtin implementation, problem was that *cmp was
      read again after atomic CAS to check if old *val == *cmp;
      this fails if CAS is successful and another thread modifies
      *cmp in-between.
      
      In the x86-gcc implementation, problem was that *cmp was set
      also in the case of successful CAS; this means there is a
      window where it can clobber a value written by another thread
      after successful CAS.
      
      Patch 2:
      
      Add a GCC asm "memory" clobber to primitives that imply a
      memory barrier.
      
      This signifies to GCC that any potentially aliased memory
      must be flushed before the operation, and re-read after the
      operation, so that read or modification in other threads of
      such memory values will work as intended.
      
      In effect, it makes these primitives work as memory barriers
      for the compiler as well as the CPU. This is better and more
      correct than adding "volatile" to variables.
     @ include/atomic/gcc_builtins.h
        Do not read from *cmp after the operation as it might be
        already gone if the operation was successful.
     @ include/atomic/nolock.h
        Prefer system provided atomics over the broken x86 asm.
     @ include/atomic/x86-gcc.h
        Do not mark source operands as input/output operands.
        Add proper memory clobbers.
     @ include/my_atomic.h
        Add notes about my_atomic_add and my_atomic_cas behaviors.
     @ unittest/mysys/my_atomic-t.c
        Remove work around, if it fails, there is either a problem
        with the atomic operations code or the specific compiler
        version should be black-listed.

    modified:
      include/atomic/gcc_builtins.h
      include/atomic/nolock.h
      include/atomic/x86-gcc.h
      include/my_atomic.h
      unittest/mysys/my_atomic-t.c
=== modified file 'include/atomic/gcc_builtins.h'
--- a/include/atomic/gcc_builtins.h	2009-11-27 17:11:05 +0000
+++ b/include/atomic/gcc_builtins.h	2010-07-08 16:16:32 +0000
@@ -22,8 +22,9 @@
   v= __sync_lock_test_and_set(a, v);
 #define make_atomic_cas_body(S)                     \
   int ## S sav;                                     \
-  sav= __sync_val_compare_and_swap(a, *cmp, set);   \
-  if (!(ret= (sav == *cmp))) *cmp= sav;
+  int ## S cmp_val= *cmp;                           \
+  sav= __sync_val_compare_and_swap(a, cmp_val, set);\
+  if (!(ret= (sav == cmp_val))) *cmp= sav
 
 #ifdef MY_ATOMIC_MODE_DUMMY
 #define make_atomic_load_body(S)   ret= *a

=== modified file 'include/atomic/nolock.h'
--- a/include/atomic/nolock.h	2009-12-23 08:27:41 +0000
+++ b/include/atomic/nolock.h	2010-07-08 16:16:32 +0000
@@ -29,21 +29,22 @@
   We choose implementation as follows:
   ------------------------------------
   On Windows using Visual C++ the native implementation should be
-  preferrable. When using gcc we prefer the native x86 implementation,
-  we prefer the Solaris implementation before the gcc because of
-  stability preference, we choose gcc implementation if nothing else
-  works on gcc. If neither Visual C++ or gcc we still choose the
-  Solaris implementation on Solaris (mainly for SunStudio compiles.
+  preferrable. When using gcc we prefer the Solaris implementation
+  before the gcc because of stability preference, we choose gcc
+  builtins if available, otherwise we choose the somewhat broken
+  native x86 implementation. If neither Visual C++ or gcc we still
+  choose the Solaris implementation on Solaris (mainly for SunStudio
+  compilers).
 */
 #  if defined(_MSV_VER)
 #    include "generic-msvc.h"
 #  elif __GNUC__
-#    if defined(__i386__) || defined(__x86_64__)
-#      include "x86-gcc.h"
-#    elif defined(HAVE_SOLARIS_ATOMIC)
+#    if defined(HAVE_SOLARIS_ATOMIC)
 #      include "solaris.h"
 #    elif defined(HAVE_GCC_ATOMIC_BUILTINS)
 #      include "gcc_builtins.h"
+#    elif defined(__i386__) || defined(__x86_64__)
+#      include "x86-gcc.h"
 #    endif
 #  elif defined(HAVE_SOLARIS_ATOMIC)
 #    include "solaris.h"

=== modified file 'include/atomic/x86-gcc.h'
--- a/include/atomic/x86-gcc.h	2010-02-19 14:20:29 +0000
+++ b/include/atomic/x86-gcc.h	2010-07-08 16:16:32 +0000
@@ -53,18 +53,29 @@
 #endif
 
 #define make_atomic_add_body32                                  \
-  asm volatile (LOCK_prefix "; xadd %0, %1;" : "+r" (v) , "+m" (*a))
+  asm volatile (LOCK_prefix "; xadd %0, %1;"                    \
+                : "+r" (v), "=m" (*a)                           \
+                : "m" (*a)                                      \
+                : "memory")
 
 #define make_atomic_cas_body32                                  \
+  __typeof__(*cmp) sav;                                         \
   asm volatile (LOCK_prefix "; cmpxchg %3, %0; setz %2;"	\
-               : "+m" (*a), "+a" (*cmp), "=q" (ret): "r" (set))
+                : "=m" (*a), "=a" (sav), "=q" (ret)             \
+                : "r" (set), "m" (*a), "a" (*cmp)               \
+                : "memory");                                    \
+  if (!ret)                                                     \
+    *cmp= sav
 
 #ifdef __x86_64__
 #define make_atomic_add_body64 make_atomic_add_body32
 #define make_atomic_cas_body64 make_atomic_cas_body32
 
-#define make_atomic_fas_body(S)                                        \
-  asm volatile ("xchg %0, %1;" : "+r" (v) , "+m" (*a))
+#define make_atomic_fas_body(S)                                 \
+  asm volatile ("xchg %0, %1;"                                  \
+                : "+r" (v), "=m" (*a)                           \
+                : "m" (*a)                                      \
+                : "memory")
 
 /*
   Actually 32-bit reads/writes are always atomic on x86
@@ -73,9 +84,14 @@
 #define make_atomic_load_body(S)                                \
   ret=0;                                                        \
   asm volatile (LOCK_prefix "; cmpxchg %2, %0"                  \
-                : "+m" (*a), "+a" (ret): "r" (ret))
+                : "=m" (*a), "=a" (ret)                         \
+                : "r" (ret), "m" (*a)                           \
+                : "memory")
 #define make_atomic_store_body(S)                               \
-  asm volatile ("; xchg %0, %1;" : "+m" (*a), "+r" (v))
+  asm volatile ("; xchg %0, %1;"                                \
+                : "=m" (*a), "+r" (v)                           \
+                : "m" (*a)                                      \
+                : "memory")
 
 #else
 /*
@@ -104,12 +120,13 @@
   platforms the much simpler make_atomic_cas_body32 will work
   fine.
 */
-#define make_atomic_cas_body64                                  \
-  int32 ebx=(set & 0xFFFFFFFF), ecx=(set >> 32);                \
-  asm volatile ("push %%ebx; movl %3, %%ebx;"                   \
-                LOCK_prefix "; cmpxchg8b %0; setz %2; pop %%ebx"\
-               : "+m" (*a), "+A" (*cmp), "=c" (ret)             \
-               :"m" (ebx), "c" (ecx))
+#define make_atomic_cas_body64                                    \
+  int32 ebx=(set & 0xFFFFFFFF), ecx=(set >> 32);                  \
+  asm volatile ("push %%ebx; movl %3, %%ebx;"                     \
+                LOCK_prefix "; cmpxchg8b %0; setz %2; pop %%ebx"  \
+                : "=m" (*a), "+A" (*cmp), "=c" (ret)               \
+                : "m" (ebx), "c" (ecx), "m" (*a)                  \
+                : "memory", "esp")
 #endif
 
 /*

=== modified file 'include/my_atomic.h'
--- a/include/my_atomic.h	2010-02-23 15:49:21 +0000
+++ b/include/my_atomic.h	2010-07-08 16:16:32 +0000
@@ -20,6 +20,7 @@
   This header defines five atomic operations:
 
   my_atomic_add#(&var, what)
+    'Fetch and Add'
     add 'what' to *var, and return the old value of *var
 
   my_atomic_fas#(&var, what)
@@ -27,9 +28,10 @@
     store 'what' in *var, and return the old value of *var
 
   my_atomic_cas#(&var, &old, new)
-    'Compare And Swap'
+    An odd variation of 'Compare And Set/Swap'
     if *var is equal to *old, then store 'new' in *var, and return TRUE
     otherwise store *var in *old, and return FALSE
+    Usually, &old should not be accessed if the operation is successful.
 
   my_atomic_load#(&var)
     return *var

=== modified file 'unittest/mysys/my_atomic-t.c'
--- a/unittest/mysys/my_atomic-t.c	2009-11-27 17:11:05 +0000
+++ b/unittest/mysys/my_atomic-t.c	2010-07-08 16:16:32 +0000
@@ -15,13 +15,6 @@
 
 #include "thr_template.c"
 
-/* at least gcc 3.4.5 and 3.4.6 (but not 3.2.3) on RHEL */
-#if __GNUC__ == 3 && __GNUC_MINOR__ == 4
-#define GCC_BUG_WORKAROUND volatile
-#else
-#define GCC_BUG_WORKAROUND
-#endif
-
 volatile uint32 b32;
 volatile int32  c32;
 my_atomic_rwlock_t rwl;
@@ -29,8 +22,8 @@ my_atomic_rwlock_t rwl;
 /* add and sub a random number in a loop. Must get 0 at the end */
 pthread_handler_t test_atomic_add(void *arg)
 {
-  int    m= (*(int *)arg)/2;
-  GCC_BUG_WORKAROUND int32 x;
+  int m= (*(int *)arg)/2;
+  int32 x;
   for (x= ((int)(intptr)(&m)); m ; m--)
   {
     x= (x*m+0x87654321) & INT_MAX32;
@@ -52,8 +45,8 @@ volatile int64 a64;
 /* add and sub a random number in a loop. Must get 0 at the end */
 pthread_handler_t test_atomic_add64(void *arg)
 {
-  int    m= (*(int *)arg)/2;
-  GCC_BUG_WORKAROUND int64 x;
+  int m= (*(int *)arg)/2;
+  int64 x;
   for (x= ((int64)(intptr)(&m)); m ; m--)
   {
     x= (x*m+0xfdecba987654321LL) & INT_MAX64;
@@ -128,8 +121,8 @@ pthread_handler_t test_atomic_fas(void *
 */
 pthread_handler_t test_atomic_cas(void *arg)
 {
-  int    m= (*(int *)arg)/2, ok= 0;
-  GCC_BUG_WORKAROUND int32 x, y;
+  int m= (*(int *)arg)/2, ok= 0;
+  int32 x, y;
   for (x= ((int)(intptr)(&m)); m ; m--)
   {
     my_atomic_rwlock_wrlock(&rwl);


Attachment: [text/bzr-bundle] bzr/davi.arnaut@sun.com-20100708161632-7kztg8833xl58o41.bundle
Thread
bzr commit into mysql-trunk-bugfixing branch (davi:3095) Bug#22320 Bug#52261Davi Arnaut8 Jul
Re: bzr commit into mysql-trunk-bugfixing branch (davi:3095) Bug#22320Bug#52261Konstantin Osipov8 Jul