[Bug 444076] implementing compare-and-swap for 64-bit Linux. r=brendan
authorIgor Bukanov <igor@mir2.org>
Wed, 16 Jul 2008 17:58:37 +0200
changeset 15974 215bf1ed8e2e69b903ef121509de229943abfe10
parent 15973 b87fdbfecc161974996d14882f571ddf11bdcd87
child 15975 ca442067af90a7133858f0d9957f24eadbd0d677
push idunknown
push userunknown
push dateunknown
reviewersbrendan
bugs444076
milestone1.9.1a1pre
[Bug 444076] implementing compare-and-swap for 64-bit Linux. r=brendan
js/src/jslock.cpp
js/src/jslock.h
--- a/js/src/jslock.cpp
+++ b/js/src/jslock.cpp
@@ -56,83 +56,61 @@
 #include "jslock.h"
 #include "jsscope.h"
 #include "jsstr.h"
 
 #define ReadWord(W) (W)
 
 #ifndef NSPR_LOCK
 
-#include <memory.h>
-
-static PRLock **global_locks;
-static uint32 global_lock_count = 1;
-static uint32 global_locks_log2 = 0;
-static uint32 global_locks_mask = 0;
-
-#define GLOBAL_LOCK_INDEX(id)   (((uint32)(id) >> 2) & global_locks_mask)
+/* Implement NativeCompareAndSwap. */ 
 
-static void
-js_LockGlobal(void *id)
-{
-    uint32 i = GLOBAL_LOCK_INDEX(id);
-    PR_Lock(global_locks[i]);
-}
-
-static void
-js_UnlockGlobal(void *id)
-{
-    uint32 i = GLOBAL_LOCK_INDEX(id);
-    PR_Unlock(global_locks[i]);
-}
-
-/* Exclude Alpha NT. */
 #if defined(_WIN32) && defined(_M_IX86)
 #pragma warning( disable : 4035 )
 JS_BEGIN_EXTERN_C
 extern long __cdecl
 _InterlockedCompareExchange(long *volatile dest, long exchange, long comp);
 JS_END_EXTERN_C
 #pragma intrinsic(_InterlockedCompareExchange)
 
 static JS_INLINE int
-js_CompareAndSwapHelper(jsword *w, jsword ov, jsword nv)
+NativeCompareAndSwapHelper(jsword *w, jsword ov, jsword nv)
 {
     _InterlockedCompareExchange(w, nv, ov);
     __asm {
         sete al
     }
 }
 
 static JS_INLINE int
-js_CompareAndSwap(jsword *w, jsword ov, jsword nv)
+NativeCompareAndSwap(jsword *w, jsword ov, jsword nv)
 {
-    return (js_CompareAndSwapHelper(w, ov, nv) & 1);
+    return (NativeCompareAndSwapHelper(w, ov, nv) & 1);
 }
 
 #elif defined(XP_MACOSX) || defined(DARWIN)
 
 #include <libkern/OSAtomic.h>
 
 static JS_INLINE int
-js_CompareAndSwap(jsword *w, jsword ov, jsword nv)
+NativeCompareAndSwap(jsword *w, jsword ov, jsword nv)
 {
     /* Details on these functions available in the manpage for atomic */
 #if JS_BYTES_PER_WORD == 8 && JS_BYTES_PER_LONG != 8
     return OSAtomicCompareAndSwap64Barrier(ov, nv, (int64_t*) w);
 #else
     return OSAtomicCompareAndSwap32Barrier(ov, nv, (int32_t*) w);
 #endif
 }
 
 #elif defined(__GNUC__) && defined(__i386__)
 
 /* Note: This fails on 386 cpus, cmpxchgl is a >= 486 instruction */
 static JS_INLINE int
-js_CompareAndSwap(jsword *w, jsword ov, jsword nv)
+NativeCompareAndSwap(jsword *w, jsword ov, jsword nv)
 {
     unsigned int res;
 
     __asm__ __volatile__ (
                           "lock\n"
                           "cmpxchgl %2, (%1)\n"
                           "sete %%al\n"
                           "andl $1, %%eax\n"
@@ -140,17 +118,17 @@ js_CompareAndSwap(jsword *w, jsword ov, 
                           : "r" (w), "r" (nv), "a" (ov)
                           : "cc", "memory");
     return (int)res;
 }
 
 #elif defined(SOLARIS) && defined(sparc) && defined(ULTRA_SPARC)
 
 static JS_INLINE int
-js_CompareAndSwap(jsword *w, jsword ov, jsword nv)
+NativeCompareAndSwap(jsword *w, jsword ov, jsword nv)
 {
 #if defined(__GNUC__)
     unsigned int res;
     JS_ASSERT(ov != nv);
     asm volatile ("\
 stbar\n\
 cas [%1],%2,%3\n\
 cmp %2,%3\n\
@@ -168,45 +146,75 @@ 1:"
 #endif
 }
 
 #elif defined(AIX)
 
 #include <sys/atomic_op.h>
 
 static JS_INLINE int
-js_CompareAndSwap(jsword *w, jsword ov, jsword nv)
+NativeCompareAndSwap(jsword *w, jsword ov, jsword nv)
 {
     return !_check_lock((atomic_p)w, ov, nv);
 }
 
 #elif defined(USE_ARM_KUSER)
 
 /* See https://bugzilla.mozilla.org/show_bug.cgi?id=429387 for a
  * description of this ABI; this is a function provided at a fixed
  * location by the kernel in the memory space of each process.
  */
 typedef int (__kernel_cmpxchg_t)(int oldval, int newval, volatile int *ptr);
 #define __kernel_cmpxchg (*(__kernel_cmpxchg_t *)0xffff0fc0)
 
 JS_STATIC_ASSERT(sizeof(jsword) == sizeof(int));
 
 static JS_INLINE int
-js_CompareAndSwap(jsword *w, jsword ov, jsword nv)
+NativeCompareAndSwap(jsword *w, jsword ov, jsword nv)
 {
     volatile int *vp = (volatile int*)w;
     return !__kernel_cmpxchg(ov, nv, vp);
 }
 
+#elif defined(__GNUC__) &&                                                    \
+    (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1))
+
+static JS_INLINE int
+NativeCompareAndSwap(jsword *w, jsword ov, jsword nv)
+{
+    return __sync_bool_compare_and_swap(w, ov, nv);
+}
+
 #else
 
-#error "Define NSPR_LOCK if your platform lacks a compare-and-swap instruction."
+#error "JS_HAS_NATIVE_COMPARE_AND_SWAP should be 0 if your platform lacks a compare-and-swap instruction."
 
 #endif /* arch-tests */
 
+static PRLock **global_locks;
+static uint32 global_lock_count = 1;
+static uint32 global_locks_log2 = 0;
+static uint32 global_locks_mask = 0;
+
+#define GLOBAL_LOCK_INDEX(id)   (((uint32)(jsuword)(id)>>2) & global_locks_mask)
+
+static void
+js_LockGlobal(void *id)
+{
+    uint32 i = GLOBAL_LOCK_INDEX(id);
+    PR_Lock(global_locks[i]);
+}
+
+static void
+js_UnlockGlobal(void *id)
+{
+    uint32 i = GLOBAL_LOCK_INDEX(id);
+    PR_Unlock(global_locks[i]);
+}
+
 #endif /* !NSPR_LOCK */
 
 void
 js_InitLock(JSThinLock *tl)
 {
 #ifdef NSPR_LOCK
     tl->owner = 0;
     tl->fat = (JSFatLock*)JS_NEW_LOCK();
@@ -580,35 +588,35 @@ js_GetSlotThreadSafe(JSContext *cx, JSOb
         (title->ownercx && ClaimTitle(title, cx))) {
         return STOBJ_GET_SLOT(obj, slot);
     }
 
 #ifndef NSPR_LOCK
     tl = &title->lock;
     me = CX_THINLOCK_ID(cx);
     JS_ASSERT(CURRENT_THREAD_IS_ME(me));
-    if (js_CompareAndSwap(&tl->owner, 0, me)) {
+    if (NativeCompareAndSwap(&tl->owner, 0, me)) {
         /*
          * Got the lock with one compare-and-swap.  Even so, someone else may
          * have mutated obj so it now has its own scope and lock, which would
          * require either a restart from the top of this routine, or a thin
          * lock release followed by fat lock acquisition.
          */
         if (scope == OBJ_SCOPE(obj)) {
             v = STOBJ_GET_SLOT(obj, slot);
-            if (!js_CompareAndSwap(&tl->owner, me, 0)) {
+            if (!NativeCompareAndSwap(&tl->owner, me, 0)) {
                 /* Assert that scope locks never revert to flyweight. */
                 JS_ASSERT(title->ownercx != cx);
                 LOGIT(scope, '1');
                 title->u.count = 1;
                 js_UnlockObj(cx, obj);
             }
             return v;
         }
-        if (!js_CompareAndSwap(&tl->owner, me, 0))
+        if (!NativeCompareAndSwap(&tl->owner, me, 0))
             js_Dequeue(tl);
     }
     else if (Thin_RemoveWait(ReadWord(tl->owner)) == me) {
         return STOBJ_GET_SLOT(obj, slot);
     }
 #endif
 
     js_LockObj(cx, obj);
@@ -676,29 +684,29 @@ js_SetSlotThreadSafe(JSContext *cx, JSOb
         LOCKED_OBJ_WRITE_BARRIER(cx, obj, slot, v);
         return;
     }
 
 #ifndef NSPR_LOCK
     tl = &title->lock;
     me = CX_THINLOCK_ID(cx);
     JS_ASSERT(CURRENT_THREAD_IS_ME(me));
-    if (js_CompareAndSwap(&tl->owner, 0, me)) {
+    if (NativeCompareAndSwap(&tl->owner, 0, me)) {
         if (scope == OBJ_SCOPE(obj)) {
             LOCKED_OBJ_WRITE_BARRIER(cx, obj, slot, v);
-            if (!js_CompareAndSwap(&tl->owner, me, 0)) {
+            if (!NativeCompareAndSwap(&tl->owner, me, 0)) {
                 /* Assert that scope locks never revert to flyweight. */
                 JS_ASSERT(title->ownercx != cx);
                 LOGIT(scope, '1');
                 title->u.count = 1;
                 js_UnlockObj(cx, obj);
             }
             return;
         }
-        if (!js_CompareAndSwap(&tl->owner, me, 0))
+        if (!NativeCompareAndSwap(&tl->owner, me, 0))
             js_Dequeue(tl);
     }
     else if (Thin_RemoveWait(ReadWord(tl->owner)) == me) {
         LOCKED_OBJ_WRITE_BARRIER(cx, obj, slot, v);
         return;
     }
 #endif
 
@@ -965,67 +973,67 @@ static void
 js_Enqueue(JSThinLock *tl, jsword me)
 {
     jsword o, n;
 
     js_LockGlobal(tl);
     for (;;) {
         o = ReadWord(tl->owner);
         n = Thin_SetWait(o);
-        if (o != 0 && js_CompareAndSwap(&tl->owner, o, n)) {
+        if (o != 0 && NativeCompareAndSwap(&tl->owner, o, n)) {
             if (js_SuspendThread(tl))
                 me = Thin_RemoveWait(me);
             else
                 me = Thin_SetWait(me);
         }
-        else if (js_CompareAndSwap(&tl->owner, 0, me)) {
+        else if (NativeCompareAndSwap(&tl->owner, 0, me)) {
             js_UnlockGlobal(tl);
             return;
         }
     }
 }
 
 static void
 js_Dequeue(JSThinLock *tl)
 {
     jsword o;
 
     js_LockGlobal(tl);
     o = ReadWord(tl->owner);
     JS_ASSERT(Thin_GetWait(o) != 0);
     JS_ASSERT(tl->fat != NULL);
-    if (!js_CompareAndSwap(&tl->owner, o, 0)) /* release it */
+    if (!NativeCompareAndSwap(&tl->owner, o, 0)) /* release it */
         JS_ASSERT(0);
     js_ResumeThread(tl);
 }
 
 JS_INLINE void
 js_Lock(JSThinLock *tl, jsword me)
 {
     JS_ASSERT(CURRENT_THREAD_IS_ME(me));
-    if (js_CompareAndSwap(&tl->owner, 0, me))
+    if (NativeCompareAndSwap(&tl->owner, 0, me))
         return;
     if (Thin_RemoveWait(ReadWord(tl->owner)) != me)
         js_Enqueue(tl, me);
 #ifdef DEBUG
     else
         JS_ASSERT(0);
 #endif
 }
 
 JS_INLINE void
 js_Unlock(JSThinLock *tl, jsword me)
 {
     JS_ASSERT(CURRENT_THREAD_IS_ME(me));
 
     /*
-     * Since we can race with the CompareAndSwap in js_Enqueue, we need
+     * Since we can race with the NativeCompareAndSwap in js_Enqueue, we need
      * to use a C_A_S here as well -- Arjan van de Ven 30/1/08
      */
-    if (js_CompareAndSwap(&tl->owner, me, 0))
+    if (NativeCompareAndSwap(&tl->owner, me, 0))
         return;
 
     JS_ASSERT(Thin_GetWait(tl->owner));
     if (Thin_RemoveWait(ReadWord(tl->owner)) == me)
         js_Dequeue(tl);
 #ifdef DEBUG
     else
         JS_ASSERT(0);   /* unbalanced unlock */
--- a/js/src/jslock.h
+++ b/js/src/jslock.h
@@ -34,29 +34,43 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 #ifndef jslock_h__
 #define jslock_h__
 
-#ifdef JS_THREADSAFE
-
 #include "jstypes.h"
-#include "pratom.h"
-#include "prlock.h"
-#include "prcvar.h"
-#include "prthread.h"
-
 #include "jsprvtd.h"    /* for JSScope, etc. */
 #include "jspubtd.h"    /* for JSRuntime, etc. */
 
+#ifdef JS_THREADSAFE
+# include "pratom.h"
+# include "prlock.h"
+# include "prcvar.h"
+# include "prthread.h"
+#endif
+
 JS_BEGIN_EXTERN_C
 
+#ifdef JS_THREADSAFE
+
+#if (defined(_WIN32) && defined(_M_IX86)) ||                                  \
+    (defined(__GNUC__) && defined(__i386__)) ||                               \
+    (defined(SOLARIS) && defined(sparc) && defined(ULTRA_SPARC)) ||           \
+    defined(AIX) ||                                                           \
+    defined(USE_ARM_KUSER) ||                                                 \
+    (defined(__GNUC__) &&                                                     \
+     (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)))
+# define JS_HAS_NATIVE_COMPARE_AND_SWAP 1
+#else
+# define JS_HAS_NATIVE_COMPARE_AND_SWAP 0
+#endif
+
 #define Thin_GetWait(W) ((jsword)(W) & 0x1)
 #define Thin_SetWait(W) ((jsword)(W) | 0x1)
 #define Thin_RemoveWait(W) ((jsword)(W) & ~0x1)
 
 typedef struct JSFatLock JSFatLock;
 
 struct JSFatLock {
     int         susp;
@@ -89,17 +103,17 @@ struct JSTitle {
     union {                             /* union lockful and lock-free state: */
         jsrefcount  count;              /* lock entry count for reentrancy */
         JSTitle     *link;              /* next link in rt->titleSharingTodo */
     } u;
 #ifdef JS_DEBUG_TITLE_LOCKS
     const char      *file[4];           /* file where lock was (re-)taken */
     unsigned int    line[4];            /* line where lock was (re-)taken */
 #endif
-};    
+};
 
 /*
  * Title structures must be immediately preceded by JSObjectMap structures for
  * maps that use titles for threadsafety.  This is enforced by assertion in
  * jsscope.h; see bug 408416 for future remedies to this somewhat fragile
  * architecture.
  */
 
@@ -217,42 +231,36 @@ extern void js_SetScopeInfo(JSScope *sco
     JS_END_MACRO
 
 #define JS_LOCK_VOID(cx, e)                                                   \
     JS_BEGIN_MACRO                                                            \
         JSRuntime *_rt = (cx)->runtime;                                       \
         JS_LOCK_RUNTIME_VOID(_rt, e);                                         \
     JS_END_MACRO
 
-#if defined(JS_USE_ONLY_NSPR_LOCKS) ||                                        \
-    !( (defined(_WIN32) && defined(_M_IX86)) ||                               \
-       (defined(__GNUC__) && defined(__i386__)) ||                            \
-       (defined(SOLARIS) && defined(sparc) && defined(ULTRA_SPARC)) ||        \
-       defined(AIX) || defined(USE_ARM_KUSER))
+#if defined(JS_USE_ONLY_NSPR_LOCKS) || !JS_HAS_NATIVE_COMPARE_AND_SWAP
 
 #define NSPR_LOCK 1
 
 #undef JS_LOCK0
 #undef JS_UNLOCK0
 #define JS_LOCK0(P,M)   (JS_ACQUIRE_LOCK(((JSLock*)(P)->fat)), (P)->owner = (M))
 #define JS_UNLOCK0(P,M) ((P)->owner = 0, JS_RELEASE_LOCK(((JSLock*)(P)->fat)))
 
-#else  /* arch-tests */
+#else
 
 #undef NSPR_LOCK
 
 extern void js_Lock(JSThinLock *tl, jsword me);
 extern void js_Unlock(JSThinLock *tl, jsword me);
 
-#endif /* arch-tests */
+#endif
 
 #else  /* !JS_THREADSAFE */
 
-JS_BEGIN_EXTERN_C
-
 #define JS_ATOMIC_INCREMENT(p)      (++*(p))
 #define JS_ATOMIC_DECREMENT(p)      (--*(p))
 #define JS_ATOMIC_ADD(p,v)          (*(p) += (v))
 
 #define JS_CurrentThreadId() 0
 #define JS_NEW_LOCK()               NULL
 #define JS_DESTROY_LOCK(l)          ((void)0)
 #define JS_ACQUIRE_LOCK(l)          ((void)0)
@@ -295,17 +303,17 @@ JS_BEGIN_EXTERN_C
 #define JS_AWAIT_GC_DONE(rt)        JS_WAIT_CONDVAR((rt)->gcDone, JS_NO_TIMEOUT)
 #define JS_NOTIFY_GC_DONE(rt)       JS_NOTIFY_ALL_CONDVAR((rt)->gcDone)
 #define JS_AWAIT_REQUEST_DONE(rt)   JS_WAIT_CONDVAR((rt)->requestDone,        \
                                                     JS_NO_TIMEOUT)
 #define JS_NOTIFY_REQUEST_DONE(rt)  JS_NOTIFY_CONDVAR((rt)->requestDone)
 
 #define JS_LOCK(P,CX)               JS_LOCK0(P, CX_THINLOCK_ID(CX))
 #define JS_UNLOCK(P,CX)             JS_UNLOCK0(P, CX_THINLOCK_ID(CX))
- 
+
 #ifndef SET_OBJ_INFO
 #define SET_OBJ_INFO(obj,f,l)       ((void)0)
 #endif
 #ifndef SET_TITLE_INFO
 #define SET_TITLE_INFO(title,f,l)   ((void)0)
 #endif
 
 JS_END_EXTERN_C